* [PATCH 01/20] perf: Start the massive restructuring
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 02/20] perf: Add persistent event facilities Borislav Petkov
` (19 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 288281 bytes --]
From: Borislav Petkov <borislav.petkov@amd.com>
mv kernel/perf_event.c -> kernel/events/core.c. From there, all other
sensible splitting can happen.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
kernel/Makefile | 5 +-
kernel/events/Makefile | 5 +
kernel/events/core.c | 5895 ++++++++++++++++++++++++++++++++++++++++++++++++
kernel/perf_event.c | 5895 ------------------------------------------------
4 files changed, 5904 insertions(+), 5896 deletions(-)
create mode 100644 kernel/events/Makefile
create mode 100644 kernel/events/core.c
delete mode 100644 kernel/perf_event.c
diff --git a/kernel/Makefile b/kernel/Makefile
index 0b72d1a..5830059 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -100,7 +100,10 @@ obj-$(CONFIG_TRACING) += trace/
obj-$(CONFIG_X86_DS) += trace/
obj-$(CONFIG_RING_BUFFER) += trace/
obj-$(CONFIG_SMP) += sched_cpupri.o
-obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+
+# obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+obj-$(CONFIG_PERF_EVENTS) += events/
+
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o
obj-$(CONFIG_PADATA) += padata.o
diff --git a/kernel/events/Makefile b/kernel/events/Makefile
new file mode 100644
index 0000000..5445cbf
--- /dev/null
+++ b/kernel/events/Makefile
@@ -0,0 +1,5 @@
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_perf_event.o = -pg
+endif
+
+obj-y += core.o
diff --git a/kernel/events/core.c b/kernel/events/core.c
new file mode 100644
index 0000000..b98bed3
--- /dev/null
+++ b/kernel/events/core.c
@@ -0,0 +1,5895 @@
+/*
+ * Performance events core code:
+ *
+ * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
+ *
+ * For licensing details see kernel-base/COPYING
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/hash.h>
+#include <linux/sysfs.h>
+#include <linux/dcache.h>
+#include <linux/percpu.h>
+#include <linux/ptrace.h>
+#include <linux/vmstat.h>
+#include <linux/vmalloc.h>
+#include <linux/hardirq.h>
+#include <linux/rculist.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/anon_inodes.h>
+#include <linux/kernel_stat.h>
+#include <linux/perf_event.h>
+#include <linux/ftrace_event.h>
+#include <linux/hw_breakpoint.h>
+
+#include <asm/irq_regs.h>
+
+/*
+ * Each CPU has a list of per CPU events:
+ */
+static DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context);
+
+int perf_max_events __read_mostly = 1;
+static int perf_reserved_percpu __read_mostly;
+static int perf_overcommit __read_mostly = 1;
+
+static atomic_t nr_events __read_mostly;
+static atomic_t nr_mmap_events __read_mostly;
+static atomic_t nr_comm_events __read_mostly;
+static atomic_t nr_task_events __read_mostly;
+
+/*
+ * perf event paranoia level:
+ * -1 - not paranoid at all
+ * 0 - disallow raw tracepoint access for unpriv
+ * 1 - disallow cpu events for unpriv
+ * 2 - disallow kernel profiling for unpriv
+ */
+int sysctl_perf_event_paranoid __read_mostly = 1;
+
+int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */
+
+/*
+ * max perf event sample rate
+ */
+int sysctl_perf_event_sample_rate __read_mostly = 100000;
+
+static atomic64_t perf_event_id;
+
+/*
+ * Lock for (sysadmin-configurable) event reservations:
+ */
+static DEFINE_SPINLOCK(perf_resource_lock);
+
+/*
+ * Architecture provided APIs - weak aliases:
+ */
+extern __weak const struct pmu *hw_perf_event_init(struct perf_event *event)
+{
+ return NULL;
+}
+
+void __weak hw_perf_disable(void) { barrier(); }
+void __weak hw_perf_enable(void) { barrier(); }
+
+void __weak perf_event_print_debug(void) { }
+
+static DEFINE_PER_CPU(int, perf_disable_count);
+
+void perf_disable(void)
+{
+ if (!__get_cpu_var(perf_disable_count)++)
+ hw_perf_disable();
+}
+
+void perf_enable(void)
+{
+ if (!--__get_cpu_var(perf_disable_count))
+ hw_perf_enable();
+}
+
+static void get_ctx(struct perf_event_context *ctx)
+{
+ WARN_ON(!atomic_inc_not_zero(&ctx->refcount));
+}
+
+static void free_ctx(struct rcu_head *head)
+{
+ struct perf_event_context *ctx;
+
+ ctx = container_of(head, struct perf_event_context, rcu_head);
+ kfree(ctx);
+}
+
+static void put_ctx(struct perf_event_context *ctx)
+{
+ if (atomic_dec_and_test(&ctx->refcount)) {
+ if (ctx->parent_ctx)
+ put_ctx(ctx->parent_ctx);
+ if (ctx->task)
+ put_task_struct(ctx->task);
+ call_rcu(&ctx->rcu_head, free_ctx);
+ }
+}
+
+static void unclone_ctx(struct perf_event_context *ctx)
+{
+ if (ctx->parent_ctx) {
+ put_ctx(ctx->parent_ctx);
+ ctx->parent_ctx = NULL;
+ }
+}
+
+/*
+ * If we inherit events we want to return the parent event id
+ * to userspace.
+ */
+static u64 primary_event_id(struct perf_event *event)
+{
+ u64 id = event->id;
+
+ if (event->parent)
+ id = event->parent->id;
+
+ return id;
+}
+
+/*
+ * Get the perf_event_context for a task and lock it.
+ * This has to cope with with the fact that until it is locked,
+ * the context could get moved to another task.
+ */
+static struct perf_event_context *
+perf_lock_task_context(struct task_struct *task, unsigned long *flags)
+{
+ struct perf_event_context *ctx;
+
+ rcu_read_lock();
+ retry:
+ ctx = rcu_dereference(task->perf_event_ctxp);
+ if (ctx) {
+ /*
+ * If this context is a clone of another, it might
+ * get swapped for another underneath us by
+ * perf_event_task_sched_out, though the
+ * rcu_read_lock() protects us from any context
+ * getting freed. Lock the context and check if it
+ * got swapped before we could get the lock, and retry
+ * if so. If we locked the right context, then it
+ * can't get swapped on us any more.
+ */
+ raw_spin_lock_irqsave(&ctx->lock, *flags);
+ if (ctx != rcu_dereference(task->perf_event_ctxp)) {
+ raw_spin_unlock_irqrestore(&ctx->lock, *flags);
+ goto retry;
+ }
+
+ if (!atomic_inc_not_zero(&ctx->refcount)) {
+ raw_spin_unlock_irqrestore(&ctx->lock, *flags);
+ ctx = NULL;
+ }
+ }
+ rcu_read_unlock();
+ return ctx;
+}
+
+/*
+ * Get the context for a task and increment its pin_count so it
+ * can't get swapped to another task. This also increments its
+ * reference count so that the context can't get freed.
+ */
+static struct perf_event_context *perf_pin_task_context(struct task_struct *task)
+{
+ struct perf_event_context *ctx;
+ unsigned long flags;
+
+ ctx = perf_lock_task_context(task, &flags);
+ if (ctx) {
+ ++ctx->pin_count;
+ raw_spin_unlock_irqrestore(&ctx->lock, flags);
+ }
+ return ctx;
+}
+
+static void perf_unpin_context(struct perf_event_context *ctx)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&ctx->lock, flags);
+ --ctx->pin_count;
+ raw_spin_unlock_irqrestore(&ctx->lock, flags);
+ put_ctx(ctx);
+}
+
+static inline u64 perf_clock(void)
+{
+ return local_clock();
+}
+
+/*
+ * Update the record of the current time in a context.
+ */
+static void update_context_time(struct perf_event_context *ctx)
+{
+ u64 now = perf_clock();
+
+ ctx->time += now - ctx->timestamp;
+ ctx->timestamp = now;
+}
+
+/*
+ * Update the total_time_enabled and total_time_running fields for a event.
+ */
+static void update_event_times(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+ u64 run_end;
+
+ if (event->state < PERF_EVENT_STATE_INACTIVE ||
+ event->group_leader->state < PERF_EVENT_STATE_INACTIVE)
+ return;
+
+ if (ctx->is_active)
+ run_end = ctx->time;
+ else
+ run_end = event->tstamp_stopped;
+
+ event->total_time_enabled = run_end - event->tstamp_enabled;
+
+ if (event->state == PERF_EVENT_STATE_INACTIVE)
+ run_end = event->tstamp_stopped;
+ else
+ run_end = ctx->time;
+
+ event->total_time_running = run_end - event->tstamp_running;
+}
+
+/*
+ * Update total_time_enabled and total_time_running for all events in a group.
+ */
+static void update_group_times(struct perf_event *leader)
+{
+ struct perf_event *event;
+
+ update_event_times(leader);
+ list_for_each_entry(event, &leader->sibling_list, group_entry)
+ update_event_times(event);
+}
+
+static struct list_head *
+ctx_group_list(struct perf_event *event, struct perf_event_context *ctx)
+{
+ if (event->attr.pinned)
+ return &ctx->pinned_groups;
+ else
+ return &ctx->flexible_groups;
+}
+
+/*
+ * Add a event from the lists for its context.
+ * Must be called with ctx->mutex and ctx->lock held.
+ */
+static void
+list_add_event(struct perf_event *event, struct perf_event_context *ctx)
+{
+ WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
+ event->attach_state |= PERF_ATTACH_CONTEXT;
+
+ /*
+ * If we're a stand alone event or group leader, we go to the context
+ * list, group events are kept attached to the group so that
+ * perf_group_detach can, at all times, locate all siblings.
+ */
+ if (event->group_leader == event) {
+ struct list_head *list;
+
+ if (is_software_event(event))
+ event->group_flags |= PERF_GROUP_SOFTWARE;
+
+ list = ctx_group_list(event, ctx);
+ list_add_tail(&event->group_entry, list);
+ }
+
+ list_add_rcu(&event->event_entry, &ctx->event_list);
+ ctx->nr_events++;
+ if (event->attr.inherit_stat)
+ ctx->nr_stat++;
+}
+
+static void perf_group_attach(struct perf_event *event)
+{
+ struct perf_event *group_leader = event->group_leader;
+
+ WARN_ON_ONCE(event->attach_state & PERF_ATTACH_GROUP);
+ event->attach_state |= PERF_ATTACH_GROUP;
+
+ if (group_leader == event)
+ return;
+
+ if (group_leader->group_flags & PERF_GROUP_SOFTWARE &&
+ !is_software_event(event))
+ group_leader->group_flags &= ~PERF_GROUP_SOFTWARE;
+
+ list_add_tail(&event->group_entry, &group_leader->sibling_list);
+ group_leader->nr_siblings++;
+}
+
+/*
+ * Remove a event from the lists for its context.
+ * Must be called with ctx->mutex and ctx->lock held.
+ */
+static void
+list_del_event(struct perf_event *event, struct perf_event_context *ctx)
+{
+ /*
+ * We can have double detach due to exit/hot-unplug + close.
+ */
+ if (!(event->attach_state & PERF_ATTACH_CONTEXT))
+ return;
+
+ event->attach_state &= ~PERF_ATTACH_CONTEXT;
+
+ ctx->nr_events--;
+ if (event->attr.inherit_stat)
+ ctx->nr_stat--;
+
+ list_del_rcu(&event->event_entry);
+
+ if (event->group_leader == event)
+ list_del_init(&event->group_entry);
+
+ update_group_times(event);
+
+ /*
+ * If event was in error state, then keep it
+ * that way, otherwise bogus counts will be
+ * returned on read(). The only way to get out
+ * of error state is by explicit re-enabling
+ * of the event
+ */
+ if (event->state > PERF_EVENT_STATE_OFF)
+ event->state = PERF_EVENT_STATE_OFF;
+}
+
+static void perf_group_detach(struct perf_event *event)
+{
+ struct perf_event *sibling, *tmp;
+ struct list_head *list = NULL;
+
+ /*
+ * We can have double detach due to exit/hot-unplug + close.
+ */
+ if (!(event->attach_state & PERF_ATTACH_GROUP))
+ return;
+
+ event->attach_state &= ~PERF_ATTACH_GROUP;
+
+ /*
+ * If this is a sibling, remove it from its group.
+ */
+ if (event->group_leader != event) {
+ list_del_init(&event->group_entry);
+ event->group_leader->nr_siblings--;
+ return;
+ }
+
+ if (!list_empty(&event->group_entry))
+ list = &event->group_entry;
+
+ /*
+ * If this was a group event with sibling events then
+ * upgrade the siblings to singleton events by adding them
+ * to whatever list we are on.
+ */
+ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, group_entry) {
+ if (list)
+ list_move_tail(&sibling->group_entry, list);
+ sibling->group_leader = sibling;
+
+ /* Inherit group flags from the previous leader */
+ sibling->group_flags = event->group_flags;
+ }
+}
+
+static inline int
+event_filter_match(struct perf_event *event)
+{
+ return event->cpu == -1 || event->cpu == smp_processor_id();
+}
+
+static void
+event_sched_out(struct perf_event *event,
+ struct perf_cpu_context *cpuctx,
+ struct perf_event_context *ctx)
+{
+ u64 delta;
+ /*
+ * An event which could not be activated because of
+ * filter mismatch still needs to have its timings
+ * maintained, otherwise bogus information is return
+ * via read() for time_enabled, time_running:
+ */
+ if (event->state == PERF_EVENT_STATE_INACTIVE
+ && !event_filter_match(event)) {
+ delta = ctx->time - event->tstamp_stopped;
+ event->tstamp_running += delta;
+ event->tstamp_stopped = ctx->time;
+ }
+
+ if (event->state != PERF_EVENT_STATE_ACTIVE)
+ return;
+
+ event->state = PERF_EVENT_STATE_INACTIVE;
+ if (event->pending_disable) {
+ event->pending_disable = 0;
+ event->state = PERF_EVENT_STATE_OFF;
+ }
+ event->tstamp_stopped = ctx->time;
+ event->pmu->disable(event);
+ event->oncpu = -1;
+
+ if (!is_software_event(event))
+ cpuctx->active_oncpu--;
+ ctx->nr_active--;
+ if (event->attr.exclusive || !cpuctx->active_oncpu)
+ cpuctx->exclusive = 0;
+}
+
+static void
+group_sched_out(struct perf_event *group_event,
+ struct perf_cpu_context *cpuctx,
+ struct perf_event_context *ctx)
+{
+ struct perf_event *event;
+ int state = group_event->state;
+
+ event_sched_out(group_event, cpuctx, ctx);
+
+ /*
+ * Schedule out siblings (if any):
+ */
+ list_for_each_entry(event, &group_event->sibling_list, group_entry)
+ event_sched_out(event, cpuctx, ctx);
+
+ if (state == PERF_EVENT_STATE_ACTIVE && group_event->attr.exclusive)
+ cpuctx->exclusive = 0;
+}
+
+/*
+ * Cross CPU call to remove a performance event
+ *
+ * We disable the event on the hardware level first. After that we
+ * remove it from the context list.
+ */
+static void __perf_event_remove_from_context(void *info)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event *event = info;
+ struct perf_event_context *ctx = event->ctx;
+
+ /*
+ * If this is a task context, we need to check whether it is
+ * the current task context of this cpu. If not it has been
+ * scheduled out before the smp call arrived.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx)
+ return;
+
+ raw_spin_lock(&ctx->lock);
+ /*
+ * Protect the list operation against NMI by disabling the
+ * events on a global level.
+ */
+ perf_disable();
+
+ event_sched_out(event, cpuctx, ctx);
+
+ list_del_event(event, ctx);
+
+ if (!ctx->task) {
+ /*
+ * Allow more per task events with respect to the
+ * reservation:
+ */
+ cpuctx->max_pertask =
+ min(perf_max_events - ctx->nr_events,
+ perf_max_events - perf_reserved_percpu);
+ }
+
+ perf_enable();
+ raw_spin_unlock(&ctx->lock);
+}
+
+
+/*
+ * Remove the event from a task's (or a CPU's) list of events.
+ *
+ * Must be called with ctx->mutex held.
+ *
+ * CPU events are removed with a smp call. For task events we only
+ * call when the task is on a CPU.
+ *
+ * If event->ctx is a cloned context, callers must make sure that
+ * every task struct that event->ctx->task could possibly point to
+ * remains valid. This is OK when called from perf_release since
+ * that only calls us on the top-level context, which can't be a clone.
+ * When called from perf_event_exit_task, it's OK because the
+ * context has been detached from its task.
+ */
+static void perf_event_remove_from_context(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+ struct task_struct *task = ctx->task;
+
+ if (!task) {
+ /*
+ * Per cpu events are removed via an smp call and
+ * the removal is always successful.
+ */
+ smp_call_function_single(event->cpu,
+ __perf_event_remove_from_context,
+ event, 1);
+ return;
+ }
+
+retry:
+ task_oncpu_function_call(task, __perf_event_remove_from_context,
+ event);
+
+ raw_spin_lock_irq(&ctx->lock);
+ /*
+ * If the context is active we need to retry the smp call.
+ */
+ if (ctx->nr_active && !list_empty(&event->group_entry)) {
+ raw_spin_unlock_irq(&ctx->lock);
+ goto retry;
+ }
+
+ /*
+ * The lock prevents that this context is scheduled in so we
+ * can remove the event safely, if the call above did not
+ * succeed.
+ */
+ if (!list_empty(&event->group_entry))
+ list_del_event(event, ctx);
+ raw_spin_unlock_irq(&ctx->lock);
+}
+
+/*
+ * Cross CPU call to disable a performance event
+ */
+static void __perf_event_disable(void *info)
+{
+ struct perf_event *event = info;
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = event->ctx;
+
+ /*
+ * If this is a per-task event, need to check whether this
+ * event's task is the current task on this cpu.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx)
+ return;
+
+ raw_spin_lock(&ctx->lock);
+
+ /*
+ * If the event is on, turn it off.
+ * If it is in error state, leave it in error state.
+ */
+ if (event->state >= PERF_EVENT_STATE_INACTIVE) {
+ update_context_time(ctx);
+ update_group_times(event);
+ if (event == event->group_leader)
+ group_sched_out(event, cpuctx, ctx);
+ else
+ event_sched_out(event, cpuctx, ctx);
+ event->state = PERF_EVENT_STATE_OFF;
+ }
+
+ raw_spin_unlock(&ctx->lock);
+}
+
+/*
+ * Disable a event.
+ *
+ * If event->ctx is a cloned context, callers must make sure that
+ * every task struct that event->ctx->task could possibly point to
+ * remains valid. This condition is satisifed when called through
+ * perf_event_for_each_child or perf_event_for_each because they
+ * hold the top-level event's child_mutex, so any descendant that
+ * goes to exit will block in sync_child_event.
+ * When called from perf_pending_event it's OK because event->ctx
+ * is the current context on this CPU and preemption is disabled,
+ * hence we can't get into perf_event_task_sched_out for this context.
+ */
+void perf_event_disable(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+ struct task_struct *task = ctx->task;
+
+ if (!task) {
+ /*
+ * Disable the event on the cpu that it's on
+ */
+ smp_call_function_single(event->cpu, __perf_event_disable,
+ event, 1);
+ return;
+ }
+
+ retry:
+ task_oncpu_function_call(task, __perf_event_disable, event);
+
+ raw_spin_lock_irq(&ctx->lock);
+ /*
+ * If the event is still active, we need to retry the cross-call.
+ */
+ if (event->state == PERF_EVENT_STATE_ACTIVE) {
+ raw_spin_unlock_irq(&ctx->lock);
+ goto retry;
+ }
+
+ /*
+ * Since we have the lock this context can't be scheduled
+ * in, so we can change the state safely.
+ */
+ if (event->state == PERF_EVENT_STATE_INACTIVE) {
+ update_group_times(event);
+ event->state = PERF_EVENT_STATE_OFF;
+ }
+
+ raw_spin_unlock_irq(&ctx->lock);
+}
+
+static int
+event_sched_in(struct perf_event *event,
+ struct perf_cpu_context *cpuctx,
+ struct perf_event_context *ctx)
+{
+ if (event->state <= PERF_EVENT_STATE_OFF)
+ return 0;
+
+ event->state = PERF_EVENT_STATE_ACTIVE;
+ event->oncpu = smp_processor_id();
+ /*
+ * The new state must be visible before we turn it on in the hardware:
+ */
+ smp_wmb();
+
+ if (event->pmu->enable(event)) {
+ event->state = PERF_EVENT_STATE_INACTIVE;
+ event->oncpu = -1;
+ return -EAGAIN;
+ }
+
+ event->tstamp_running += ctx->time - event->tstamp_stopped;
+
+ if (!is_software_event(event))
+ cpuctx->active_oncpu++;
+ ctx->nr_active++;
+
+ if (event->attr.exclusive)
+ cpuctx->exclusive = 1;
+
+ return 0;
+}
+
+static int
+group_sched_in(struct perf_event *group_event,
+ struct perf_cpu_context *cpuctx,
+ struct perf_event_context *ctx)
+{
+ struct perf_event *event, *partial_group = NULL;
+ const struct pmu *pmu = group_event->pmu;
+ bool txn = false;
+
+ if (group_event->state == PERF_EVENT_STATE_OFF)
+ return 0;
+
+ /* Check if group transaction availabe */
+ if (pmu->start_txn)
+ txn = true;
+
+ if (txn)
+ pmu->start_txn(pmu);
+
+ if (event_sched_in(group_event, cpuctx, ctx)) {
+ if (txn)
+ pmu->cancel_txn(pmu);
+ return -EAGAIN;
+ }
+
+ /*
+ * Schedule in siblings as one group (if any):
+ */
+ list_for_each_entry(event, &group_event->sibling_list, group_entry) {
+ if (event_sched_in(event, cpuctx, ctx)) {
+ partial_group = event;
+ goto group_error;
+ }
+ }
+
+ if (!txn || !pmu->commit_txn(pmu))
+ return 0;
+
+group_error:
+ /*
+ * Groups can be scheduled in as one unit only, so undo any
+ * partial group before returning:
+ */
+ list_for_each_entry(event, &group_event->sibling_list, group_entry) {
+ if (event == partial_group)
+ break;
+ event_sched_out(event, cpuctx, ctx);
+ }
+ event_sched_out(group_event, cpuctx, ctx);
+
+ if (txn)
+ pmu->cancel_txn(pmu);
+
+ return -EAGAIN;
+}
+
+/*
+ * Work out whether we can put this event group on the CPU now.
+ */
+static int group_can_go_on(struct perf_event *event,
+ struct perf_cpu_context *cpuctx,
+ int can_add_hw)
+{
+ /*
+ * Groups consisting entirely of software events can always go on.
+ */
+ if (event->group_flags & PERF_GROUP_SOFTWARE)
+ return 1;
+ /*
+ * If an exclusive group is already on, no other hardware
+ * events can go on.
+ */
+ if (cpuctx->exclusive)
+ return 0;
+ /*
+ * If this group is exclusive and there are already
+ * events on the CPU, it can't go on.
+ */
+ if (event->attr.exclusive && cpuctx->active_oncpu)
+ return 0;
+ /*
+ * Otherwise, try to add it if all previous groups were able
+ * to go on.
+ */
+ return can_add_hw;
+}
+
+static void add_event_to_ctx(struct perf_event *event,
+ struct perf_event_context *ctx)
+{
+ list_add_event(event, ctx);
+ perf_group_attach(event);
+ event->tstamp_enabled = ctx->time;
+ event->tstamp_running = ctx->time;
+ event->tstamp_stopped = ctx->time;
+}
+
+/*
+ * Cross CPU call to install and enable a performance event
+ *
+ * Must be called with ctx->mutex held
+ */
+static void __perf_install_in_context(void *info)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event *event = info;
+ struct perf_event_context *ctx = event->ctx;
+ struct perf_event *leader = event->group_leader;
+ int err;
+
+ /*
+ * If this is a task context, we need to check whether it is
+ * the current task context of this cpu. If not it has been
+ * scheduled out before the smp call arrived.
+ * Or possibly this is the right context but it isn't
+ * on this cpu because it had no events.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx) {
+ if (cpuctx->task_ctx || ctx->task != current)
+ return;
+ cpuctx->task_ctx = ctx;
+ }
+
+ raw_spin_lock(&ctx->lock);
+ ctx->is_active = 1;
+ update_context_time(ctx);
+
+ /*
+ * Protect the list operation against NMI by disabling the
+ * events on a global level. NOP for non NMI based events.
+ */
+ perf_disable();
+
+ add_event_to_ctx(event, ctx);
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ goto unlock;
+
+ /*
+ * Don't put the event on if it is disabled or if
+ * it is in a group and the group isn't on.
+ */
+ if (event->state != PERF_EVENT_STATE_INACTIVE ||
+ (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE))
+ goto unlock;
+
+ /*
+ * An exclusive event can't go on if there are already active
+ * hardware events, and no hardware event can go on if there
+ * is already an exclusive event on.
+ */
+ if (!group_can_go_on(event, cpuctx, 1))
+ err = -EEXIST;
+ else
+ err = event_sched_in(event, cpuctx, ctx);
+
+ if (err) {
+ /*
+ * This event couldn't go on. If it is in a group
+ * then we have to pull the whole group off.
+ * If the event group is pinned then put it in error state.
+ */
+ if (leader != event)
+ group_sched_out(leader, cpuctx, ctx);
+ if (leader->attr.pinned) {
+ update_group_times(leader);
+ leader->state = PERF_EVENT_STATE_ERROR;
+ }
+ }
+
+ if (!err && !ctx->task && cpuctx->max_pertask)
+ cpuctx->max_pertask--;
+
+ unlock:
+ perf_enable();
+
+ raw_spin_unlock(&ctx->lock);
+}
+
+/*
+ * Attach a performance event to a context
+ *
+ * First we add the event to the list with the hardware enable bit
+ * in event->hw_config cleared.
+ *
+ * If the event is attached to a task which is on a CPU we use a smp
+ * call to enable it in the task context. The task might have been
+ * scheduled away, but we check this in the smp call again.
+ *
+ * Must be called with ctx->mutex held.
+ */
+static void
+perf_install_in_context(struct perf_event_context *ctx,
+ struct perf_event *event,
+ int cpu)
+{
+ struct task_struct *task = ctx->task;
+
+ if (!task) {
+ /*
+ * Per cpu events are installed via an smp call and
+ * the install is always successful.
+ */
+ smp_call_function_single(cpu, __perf_install_in_context,
+ event, 1);
+ return;
+ }
+
+retry:
+ task_oncpu_function_call(task, __perf_install_in_context,
+ event);
+
+ raw_spin_lock_irq(&ctx->lock);
+ /*
+ * we need to retry the smp call.
+ */
+ if (ctx->is_active && list_empty(&event->group_entry)) {
+ raw_spin_unlock_irq(&ctx->lock);
+ goto retry;
+ }
+
+ /*
+ * The lock prevents that this context is scheduled in so we
+ * can add the event safely, if it the call above did not
+ * succeed.
+ */
+ if (list_empty(&event->group_entry))
+ add_event_to_ctx(event, ctx);
+ raw_spin_unlock_irq(&ctx->lock);
+}
+
+/*
+ * Put a event into inactive state and update time fields.
+ * Enabling the leader of a group effectively enables all
+ * the group members that aren't explicitly disabled, so we
+ * have to update their ->tstamp_enabled also.
+ * Note: this works for group members as well as group leaders
+ * since the non-leader members' sibling_lists will be empty.
+ */
+static void __perf_event_mark_enabled(struct perf_event *event,
+ struct perf_event_context *ctx)
+{
+ struct perf_event *sub;
+
+ event->state = PERF_EVENT_STATE_INACTIVE;
+ event->tstamp_enabled = ctx->time - event->total_time_enabled;
+ list_for_each_entry(sub, &event->sibling_list, group_entry)
+ if (sub->state >= PERF_EVENT_STATE_INACTIVE)
+ sub->tstamp_enabled =
+ ctx->time - sub->total_time_enabled;
+}
+
+/*
+ * Cross CPU call to enable a performance event
+ */
+static void __perf_event_enable(void *info)
+{
+ struct perf_event *event = info;
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = event->ctx;
+ struct perf_event *leader = event->group_leader;
+ int err;
+
+ /*
+ * If this is a per-task event, need to check whether this
+ * event's task is the current task on this cpu.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx) {
+ if (cpuctx->task_ctx || ctx->task != current)
+ return;
+ cpuctx->task_ctx = ctx;
+ }
+
+ raw_spin_lock(&ctx->lock);
+ ctx->is_active = 1;
+ update_context_time(ctx);
+
+ if (event->state >= PERF_EVENT_STATE_INACTIVE)
+ goto unlock;
+ __perf_event_mark_enabled(event, ctx);
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ goto unlock;
+
+ /*
+ * If the event is in a group and isn't the group leader,
+ * then don't put it on unless the group is on.
+ */
+ if (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE)
+ goto unlock;
+
+ if (!group_can_go_on(event, cpuctx, 1)) {
+ err = -EEXIST;
+ } else {
+ perf_disable();
+ if (event == leader)
+ err = group_sched_in(event, cpuctx, ctx);
+ else
+ err = event_sched_in(event, cpuctx, ctx);
+ perf_enable();
+ }
+
+ if (err) {
+ /*
+ * If this event can't go on and it's part of a
+ * group, then the whole group has to come off.
+ */
+ if (leader != event)
+ group_sched_out(leader, cpuctx, ctx);
+ if (leader->attr.pinned) {
+ update_group_times(leader);
+ leader->state = PERF_EVENT_STATE_ERROR;
+ }
+ }
+
+ unlock:
+ raw_spin_unlock(&ctx->lock);
+}
+
+/*
+ * Enable a event.
+ *
+ * If event->ctx is a cloned context, callers must make sure that
+ * every task struct that event->ctx->task could possibly point to
+ * remains valid. This condition is satisfied when called through
+ * perf_event_for_each_child or perf_event_for_each as described
+ * for perf_event_disable.
+ */
+void perf_event_enable(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+ struct task_struct *task = ctx->task;
+
+ if (!task) {
+ /*
+ * Enable the event on the cpu that it's on
+ */
+ smp_call_function_single(event->cpu, __perf_event_enable,
+ event, 1);
+ return;
+ }
+
+ raw_spin_lock_irq(&ctx->lock);
+ if (event->state >= PERF_EVENT_STATE_INACTIVE)
+ goto out;
+
+ /*
+ * If the event is in error state, clear that first.
+ * That way, if we see the event in error state below, we
+ * know that it has gone back into error state, as distinct
+ * from the task having been scheduled away before the
+ * cross-call arrived.
+ */
+ if (event->state == PERF_EVENT_STATE_ERROR)
+ event->state = PERF_EVENT_STATE_OFF;
+
+ retry:
+ raw_spin_unlock_irq(&ctx->lock);
+ task_oncpu_function_call(task, __perf_event_enable, event);
+
+ raw_spin_lock_irq(&ctx->lock);
+
+ /*
+ * If the context is active and the event is still off,
+ * we need to retry the cross-call.
+ */
+ if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF)
+ goto retry;
+
+ /*
+ * Since we have the lock this context can't be scheduled
+ * in, so we can change the state safely.
+ */
+ if (event->state == PERF_EVENT_STATE_OFF)
+ __perf_event_mark_enabled(event, ctx);
+
+ out:
+ raw_spin_unlock_irq(&ctx->lock);
+}
+
+static int perf_event_refresh(struct perf_event *event, int refresh)
+{
+ /*
+ * not supported on inherited events
+ */
+ if (event->attr.inherit)
+ return -EINVAL;
+
+ atomic_add(refresh, &event->event_limit);
+ perf_event_enable(event);
+
+ return 0;
+}
+
+enum event_type_t {
+ EVENT_FLEXIBLE = 0x1,
+ EVENT_PINNED = 0x2,
+ EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED,
+};
+
+static void ctx_sched_out(struct perf_event_context *ctx,
+ struct perf_cpu_context *cpuctx,
+ enum event_type_t event_type)
+{
+ struct perf_event *event;
+
+ raw_spin_lock(&ctx->lock);
+ ctx->is_active = 0;
+ if (likely(!ctx->nr_events))
+ goto out;
+ update_context_time(ctx);
+
+ perf_disable();
+ if (!ctx->nr_active)
+ goto out_enable;
+
+ if (event_type & EVENT_PINNED)
+ list_for_each_entry(event, &ctx->pinned_groups, group_entry)
+ group_sched_out(event, cpuctx, ctx);
+
+ if (event_type & EVENT_FLEXIBLE)
+ list_for_each_entry(event, &ctx->flexible_groups, group_entry)
+ group_sched_out(event, cpuctx, ctx);
+
+ out_enable:
+ perf_enable();
+ out:
+ raw_spin_unlock(&ctx->lock);
+}
+
+/*
+ * Test whether two contexts are equivalent, i.e. whether they
+ * have both been cloned from the same version of the same context
+ * and they both have the same number of enabled events.
+ * If the number of enabled events is the same, then the set
+ * of enabled events should be the same, because these are both
+ * inherited contexts, therefore we can't access individual events
+ * in them directly with an fd; we can only enable/disable all
+ * events via prctl, or enable/disable all events in a family
+ * via ioctl, which will have the same effect on both contexts.
+ */
+static int context_equiv(struct perf_event_context *ctx1,
+ struct perf_event_context *ctx2)
+{
+ return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx
+ && ctx1->parent_gen == ctx2->parent_gen
+ && !ctx1->pin_count && !ctx2->pin_count;
+}
+
+static void __perf_event_sync_stat(struct perf_event *event,
+ struct perf_event *next_event)
+{
+ u64 value;
+
+ if (!event->attr.inherit_stat)
+ return;
+
+ /*
+ * Update the event value, we cannot use perf_event_read()
+ * because we're in the middle of a context switch and have IRQs
+ * disabled, which upsets smp_call_function_single(), however
+ * we know the event must be on the current CPU, therefore we
+ * don't need to use it.
+ */
+ switch (event->state) {
+ case PERF_EVENT_STATE_ACTIVE:
+ event->pmu->read(event);
+ /* fall-through */
+
+ case PERF_EVENT_STATE_INACTIVE:
+ update_event_times(event);
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * In order to keep per-task stats reliable we need to flip the event
+ * values when we flip the contexts.
+ */
+ value = local64_read(&next_event->count);
+ value = local64_xchg(&event->count, value);
+ local64_set(&next_event->count, value);
+
+ swap(event->total_time_enabled, next_event->total_time_enabled);
+ swap(event->total_time_running, next_event->total_time_running);
+
+ /*
+ * Since we swizzled the values, update the user visible data too.
+ */
+ perf_event_update_userpage(event);
+ perf_event_update_userpage(next_event);
+}
+
+#define list_next_entry(pos, member) \
+ list_entry(pos->member.next, typeof(*pos), member)
+
+static void perf_event_sync_stat(struct perf_event_context *ctx,
+ struct perf_event_context *next_ctx)
+{
+ struct perf_event *event, *next_event;
+
+ if (!ctx->nr_stat)
+ return;
+
+ update_context_time(ctx);
+
+ event = list_first_entry(&ctx->event_list,
+ struct perf_event, event_entry);
+
+ next_event = list_first_entry(&next_ctx->event_list,
+ struct perf_event, event_entry);
+
+ while (&event->event_entry != &ctx->event_list &&
+ &next_event->event_entry != &next_ctx->event_list) {
+
+ __perf_event_sync_stat(event, next_event);
+
+ event = list_next_entry(event, event_entry);
+ next_event = list_next_entry(next_event, event_entry);
+ }
+}
+
+/*
+ * Called from scheduler to remove the events of the current task,
+ * with interrupts disabled.
+ *
+ * We stop each event and update the event value in event->count.
+ *
+ * This does not protect us against NMI, but disable()
+ * sets the disabled bit in the control field of event _before_
+ * 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)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = task->perf_event_ctxp;
+ struct perf_event_context *next_ctx;
+ struct perf_event_context *parent;
+ int do_switch = 1;
+
+ perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
+
+ if (likely(!ctx || !cpuctx->task_ctx))
+ return;
+
+ rcu_read_lock();
+ parent = rcu_dereference(ctx->parent_ctx);
+ next_ctx = next->perf_event_ctxp;
+ if (parent && next_ctx &&
+ rcu_dereference(next_ctx->parent_ctx) == parent) {
+ /*
+ * Looks like the two contexts are clones, so we might be
+ * able to optimize the context switch. We lock both
+ * contexts and check that they are clones under the
+ * lock (including re-checking that neither has been
+ * uncloned in the meantime). It doesn't matter which
+ * order we take the locks because no other cpu could
+ * be trying to lock both of these tasks.
+ */
+ raw_spin_lock(&ctx->lock);
+ raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING);
+ if (context_equiv(ctx, next_ctx)) {
+ /*
+ * XXX do we need a memory barrier of sorts
+ * wrt to rcu_dereference() of perf_event_ctxp
+ */
+ task->perf_event_ctxp = next_ctx;
+ next->perf_event_ctxp = ctx;
+ ctx->task = next;
+ next_ctx->task = task;
+ do_switch = 0;
+
+ perf_event_sync_stat(ctx, next_ctx);
+ }
+ raw_spin_unlock(&next_ctx->lock);
+ raw_spin_unlock(&ctx->lock);
+ }
+ rcu_read_unlock();
+
+ if (do_switch) {
+ ctx_sched_out(ctx, cpuctx, EVENT_ALL);
+ cpuctx->task_ctx = NULL;
+ }
+}
+
+static void task_ctx_sched_out(struct perf_event_context *ctx,
+ enum event_type_t event_type)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+
+ if (!cpuctx->task_ctx)
+ return;
+
+ if (WARN_ON_ONCE(ctx != cpuctx->task_ctx))
+ return;
+
+ ctx_sched_out(ctx, cpuctx, event_type);
+ cpuctx->task_ctx = NULL;
+}
+
+/*
+ * Called with IRQs disabled
+ */
+static void __perf_event_task_sched_out(struct perf_event_context *ctx)
+{
+ task_ctx_sched_out(ctx, EVENT_ALL);
+}
+
+/*
+ * Called with IRQs disabled
+ */
+static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx,
+ enum event_type_t event_type)
+{
+ ctx_sched_out(&cpuctx->ctx, cpuctx, event_type);
+}
+
+static void
+ctx_pinned_sched_in(struct perf_event_context *ctx,
+ struct perf_cpu_context *cpuctx)
+{
+ struct perf_event *event;
+
+ list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
+ if (event->state <= PERF_EVENT_STATE_OFF)
+ continue;
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ continue;
+
+ if (group_can_go_on(event, cpuctx, 1))
+ group_sched_in(event, cpuctx, ctx);
+
+ /*
+ * If this pinned group hasn't been scheduled,
+ * put it in error state.
+ */
+ if (event->state == PERF_EVENT_STATE_INACTIVE) {
+ update_group_times(event);
+ event->state = PERF_EVENT_STATE_ERROR;
+ }
+ }
+}
+
+static void
+ctx_flexible_sched_in(struct perf_event_context *ctx,
+ struct perf_cpu_context *cpuctx)
+{
+ struct perf_event *event;
+ int can_add_hw = 1;
+
+ list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
+ /* Ignore events in OFF or ERROR state */
+ if (event->state <= PERF_EVENT_STATE_OFF)
+ continue;
+ /*
+ * Listen to the 'cpu' scheduling filter constraint
+ * of events:
+ */
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ continue;
+
+ if (group_can_go_on(event, cpuctx, can_add_hw))
+ if (group_sched_in(event, cpuctx, ctx))
+ can_add_hw = 0;
+ }
+}
+
+static void
+ctx_sched_in(struct perf_event_context *ctx,
+ struct perf_cpu_context *cpuctx,
+ enum event_type_t event_type)
+{
+ raw_spin_lock(&ctx->lock);
+ ctx->is_active = 1;
+ if (likely(!ctx->nr_events))
+ goto out;
+
+ ctx->timestamp = perf_clock();
+
+ perf_disable();
+
+ /*
+ * First go through the list and put on any pinned groups
+ * in order to give them the best chance of going on.
+ */
+ if (event_type & EVENT_PINNED)
+ ctx_pinned_sched_in(ctx, cpuctx);
+
+ /* Then walk through the lower prio flexible groups */
+ if (event_type & EVENT_FLEXIBLE)
+ ctx_flexible_sched_in(ctx, cpuctx);
+
+ perf_enable();
+ out:
+ raw_spin_unlock(&ctx->lock);
+}
+
+static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
+ enum event_type_t event_type)
+{
+ struct perf_event_context *ctx = &cpuctx->ctx;
+
+ ctx_sched_in(ctx, cpuctx, event_type);
+}
+
+static void task_ctx_sched_in(struct task_struct *task,
+ enum event_type_t event_type)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = task->perf_event_ctxp;
+
+ if (likely(!ctx))
+ return;
+ if (cpuctx->task_ctx == ctx)
+ return;
+ ctx_sched_in(ctx, cpuctx, event_type);
+ cpuctx->task_ctx = ctx;
+}
+/*
+ * Called from scheduler to add the events of the current task
+ * with interrupts disabled.
+ *
+ * We restore the event value and then enable it.
+ *
+ * This does not protect us against NMI, but enable()
+ * sets the enabled bit in the control field of event _before_
+ * 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 *task)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = task->perf_event_ctxp;
+
+ if (likely(!ctx))
+ return;
+
+ if (cpuctx->task_ctx == ctx)
+ return;
+
+ perf_disable();
+
+ /*
+ * We want to keep the following priority order:
+ * cpu pinned (that don't need to move), task pinned,
+ * cpu flexible, task flexible.
+ */
+ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
+
+ ctx_sched_in(ctx, cpuctx, EVENT_PINNED);
+ cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE);
+ ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE);
+
+ cpuctx->task_ctx = ctx;
+
+ perf_enable();
+}
+
+#define MAX_INTERRUPTS (~0ULL)
+
+static void perf_log_throttle(struct perf_event *event, int enable);
+
+static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count)
+{
+ u64 frequency = event->attr.sample_freq;
+ u64 sec = NSEC_PER_SEC;
+ u64 divisor, dividend;
+
+ int count_fls, nsec_fls, frequency_fls, sec_fls;
+
+ count_fls = fls64(count);
+ nsec_fls = fls64(nsec);
+ frequency_fls = fls64(frequency);
+ sec_fls = 30;
+
+ /*
+ * We got @count in @nsec, with a target of sample_freq HZ
+ * the target period becomes:
+ *
+ * @count * 10^9
+ * period = -------------------
+ * @nsec * sample_freq
+ *
+ */
+
+ /*
+ * Reduce accuracy by one bit such that @a and @b converge
+ * to a similar magnitude.
+ */
+#define REDUCE_FLS(a, b) \
+do { \
+ if (a##_fls > b##_fls) { \
+ a >>= 1; \
+ a##_fls--; \
+ } else { \
+ b >>= 1; \
+ b##_fls--; \
+ } \
+} while (0)
+
+ /*
+ * Reduce accuracy until either term fits in a u64, then proceed with
+ * the other, so that finally we can do a u64/u64 division.
+ */
+ while (count_fls + sec_fls > 64 && nsec_fls + frequency_fls > 64) {
+ REDUCE_FLS(nsec, frequency);
+ REDUCE_FLS(sec, count);
+ }
+
+ if (count_fls + sec_fls > 64) {
+ divisor = nsec * frequency;
+
+ while (count_fls + sec_fls > 64) {
+ REDUCE_FLS(count, sec);
+ divisor >>= 1;
+ }
+
+ dividend = count * sec;
+ } else {
+ dividend = count * sec;
+
+ while (nsec_fls + frequency_fls > 64) {
+ REDUCE_FLS(nsec, frequency);
+ dividend >>= 1;
+ }
+
+ divisor = nsec * frequency;
+ }
+
+ if (!divisor)
+ return dividend;
+
+ return div64_u64(dividend, divisor);
+}
+
+static void perf_event_stop(struct perf_event *event)
+{
+ if (!event->pmu->stop)
+ return event->pmu->disable(event);
+
+ return event->pmu->stop(event);
+}
+
+static int perf_event_start(struct perf_event *event)
+{
+ if (!event->pmu->start)
+ return event->pmu->enable(event);
+
+ return event->pmu->start(event);
+}
+
+static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ s64 period, sample_period;
+ s64 delta;
+
+ period = perf_calculate_period(event, nsec, count);
+
+ delta = (s64)(period - hwc->sample_period);
+ delta = (delta + 7) / 8; /* low pass filter */
+
+ sample_period = hwc->sample_period + delta;
+
+ if (!sample_period)
+ sample_period = 1;
+
+ hwc->sample_period = sample_period;
+
+ if (local64_read(&hwc->period_left) > 8*sample_period) {
+ perf_disable();
+ perf_event_stop(event);
+ local64_set(&hwc->period_left, 0);
+ perf_event_start(event);
+ perf_enable();
+ }
+}
+
+static void perf_ctx_adjust_freq(struct perf_event_context *ctx)
+{
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ u64 interrupts, now;
+ s64 delta;
+
+ raw_spin_lock(&ctx->lock);
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (event->state != PERF_EVENT_STATE_ACTIVE)
+ continue;
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ continue;
+
+ hwc = &event->hw;
+
+ interrupts = hwc->interrupts;
+ hwc->interrupts = 0;
+
+ /*
+ * unthrottle events on the tick
+ */
+ if (interrupts == MAX_INTERRUPTS) {
+ perf_log_throttle(event, 1);
+ perf_disable();
+ event->pmu->unthrottle(event);
+ perf_enable();
+ }
+
+ if (!event->attr.freq || !event->attr.sample_freq)
+ continue;
+
+ perf_disable();
+ event->pmu->read(event);
+ now = local64_read(&event->count);
+ delta = now - hwc->freq_count_stamp;
+ hwc->freq_count_stamp = now;
+
+ if (delta > 0)
+ perf_adjust_period(event, TICK_NSEC, delta);
+ perf_enable();
+ }
+ raw_spin_unlock(&ctx->lock);
+}
+
+/*
+ * Round-robin a context's events:
+ */
+static void rotate_ctx(struct perf_event_context *ctx)
+{
+ raw_spin_lock(&ctx->lock);
+
+ /* Rotate the first entry last of non-pinned groups */
+ list_rotate_left(&ctx->flexible_groups);
+
+ raw_spin_unlock(&ctx->lock);
+}
+
+void perf_event_task_tick(struct task_struct *curr)
+{
+ struct perf_cpu_context *cpuctx;
+ struct perf_event_context *ctx;
+ int rotate = 0;
+
+ if (!atomic_read(&nr_events))
+ return;
+
+ cpuctx = &__get_cpu_var(perf_cpu_context);
+ if (cpuctx->ctx.nr_events &&
+ cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
+ rotate = 1;
+
+ ctx = curr->perf_event_ctxp;
+ if (ctx && ctx->nr_events && ctx->nr_events != ctx->nr_active)
+ rotate = 1;
+
+ perf_ctx_adjust_freq(&cpuctx->ctx);
+ if (ctx)
+ perf_ctx_adjust_freq(ctx);
+
+ if (!rotate)
+ return;
+
+ perf_disable();
+ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
+ if (ctx)
+ task_ctx_sched_out(ctx, EVENT_FLEXIBLE);
+
+ rotate_ctx(&cpuctx->ctx);
+ if (ctx)
+ rotate_ctx(ctx);
+
+ cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE);
+ if (ctx)
+ task_ctx_sched_in(curr, EVENT_FLEXIBLE);
+ perf_enable();
+}
+
+static int event_enable_on_exec(struct perf_event *event,
+ struct perf_event_context *ctx)
+{
+ if (!event->attr.enable_on_exec)
+ return 0;
+
+ event->attr.enable_on_exec = 0;
+ if (event->state >= PERF_EVENT_STATE_INACTIVE)
+ return 0;
+
+ __perf_event_mark_enabled(event, ctx);
+
+ return 1;
+}
+
+/*
+ * Enable all of a task's events that have been marked enable-on-exec.
+ * This expects task == current.
+ */
+static void perf_event_enable_on_exec(struct task_struct *task)
+{
+ struct perf_event_context *ctx;
+ struct perf_event *event;
+ unsigned long flags;
+ int enabled = 0;
+ int ret;
+
+ local_irq_save(flags);
+ ctx = task->perf_event_ctxp;
+ if (!ctx || !ctx->nr_events)
+ goto out;
+
+ __perf_event_task_sched_out(ctx);
+
+ raw_spin_lock(&ctx->lock);
+
+ list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
+ ret = event_enable_on_exec(event, ctx);
+ if (ret)
+ enabled = 1;
+ }
+
+ list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
+ ret = event_enable_on_exec(event, ctx);
+ if (ret)
+ enabled = 1;
+ }
+
+ /*
+ * Unclone this context if we enabled any event.
+ */
+ if (enabled)
+ unclone_ctx(ctx);
+
+ raw_spin_unlock(&ctx->lock);
+
+ perf_event_task_sched_in(task);
+ out:
+ local_irq_restore(flags);
+}
+
+/*
+ * Cross CPU call to read the hardware event
+ */
+static void __perf_event_read(void *info)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event *event = info;
+ struct perf_event_context *ctx = event->ctx;
+
+ /*
+ * If this is a task context, we need to check whether it is
+ * the current task context of this cpu. If not it has been
+ * scheduled out before the smp call arrived. In that case
+ * event->count would have been updated to a recent sample
+ * when the event was scheduled out.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx)
+ return;
+
+ raw_spin_lock(&ctx->lock);
+ update_context_time(ctx);
+ update_event_times(event);
+ raw_spin_unlock(&ctx->lock);
+
+ event->pmu->read(event);
+}
+
+static inline u64 perf_event_count(struct perf_event *event)
+{
+ return local64_read(&event->count) + atomic64_read(&event->child_count);
+}
+
+static u64 perf_event_read(struct perf_event *event)
+{
+ /*
+ * If event is enabled and currently active on a CPU, update the
+ * value in the event structure:
+ */
+ if (event->state == PERF_EVENT_STATE_ACTIVE) {
+ smp_call_function_single(event->oncpu,
+ __perf_event_read, event, 1);
+ } else if (event->state == PERF_EVENT_STATE_INACTIVE) {
+ struct perf_event_context *ctx = event->ctx;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&ctx->lock, flags);
+ update_context_time(ctx);
+ update_event_times(event);
+ raw_spin_unlock_irqrestore(&ctx->lock, flags);
+ }
+
+ return perf_event_count(event);
+}
+
+/*
+ * Initialize the perf_event context in a task_struct:
+ */
+static void
+__perf_event_init_context(struct perf_event_context *ctx,
+ struct task_struct *task)
+{
+ raw_spin_lock_init(&ctx->lock);
+ mutex_init(&ctx->mutex);
+ INIT_LIST_HEAD(&ctx->pinned_groups);
+ INIT_LIST_HEAD(&ctx->flexible_groups);
+ INIT_LIST_HEAD(&ctx->event_list);
+ atomic_set(&ctx->refcount, 1);
+ ctx->task = task;
+}
+
+static struct perf_event_context *find_get_context(pid_t pid, int cpu)
+{
+ struct perf_event_context *ctx;
+ struct perf_cpu_context *cpuctx;
+ struct task_struct *task;
+ unsigned long flags;
+ int err;
+
+ if (pid == -1 && cpu != -1) {
+ /* Must be root to operate on a CPU event: */
+ if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
+ return ERR_PTR(-EACCES);
+
+ if (cpu < 0 || cpu >= nr_cpumask_bits)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * We could be clever and allow to attach a event to an
+ * offline CPU and activate it when the CPU comes up, but
+ * that's for later.
+ */
+ if (!cpu_online(cpu))
+ return ERR_PTR(-ENODEV);
+
+ cpuctx = &per_cpu(perf_cpu_context, cpu);
+ ctx = &cpuctx->ctx;
+ get_ctx(ctx);
+
+ return ctx;
+ }
+
+ rcu_read_lock();
+ if (!pid)
+ task = current;
+ else
+ task = find_task_by_vpid(pid);
+ if (task)
+ get_task_struct(task);
+ rcu_read_unlock();
+
+ if (!task)
+ return ERR_PTR(-ESRCH);
+
+ /*
+ * Can't attach events to a dying task.
+ */
+ err = -ESRCH;
+ if (task->flags & PF_EXITING)
+ goto errout;
+
+ /* Reuse ptrace permission checks for now. */
+ err = -EACCES;
+ if (!ptrace_may_access(task, PTRACE_MODE_READ))
+ goto errout;
+
+ retry:
+ ctx = perf_lock_task_context(task, &flags);
+ if (ctx) {
+ unclone_ctx(ctx);
+ raw_spin_unlock_irqrestore(&ctx->lock, flags);
+ }
+
+ if (!ctx) {
+ ctx = kzalloc(sizeof(struct perf_event_context), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!ctx)
+ goto errout;
+ __perf_event_init_context(ctx, task);
+ get_ctx(ctx);
+ if (cmpxchg(&task->perf_event_ctxp, NULL, ctx)) {
+ /*
+ * We raced with some other task; use
+ * the context they set.
+ */
+ kfree(ctx);
+ goto retry;
+ }
+ get_task_struct(task);
+ }
+
+ put_task_struct(task);
+ return ctx;
+
+ errout:
+ put_task_struct(task);
+ return ERR_PTR(err);
+}
+
+static void perf_event_free_filter(struct perf_event *event);
+
+static void free_event_rcu(struct rcu_head *head)
+{
+ struct perf_event *event;
+
+ event = container_of(head, struct perf_event, rcu_head);
+ if (event->ns)
+ put_pid_ns(event->ns);
+ perf_event_free_filter(event);
+ kfree(event);
+}
+
+static void perf_pending_sync(struct perf_event *event);
+static void perf_buffer_put(struct perf_buffer *buffer);
+
+static void free_event(struct perf_event *event)
+{
+ perf_pending_sync(event);
+
+ if (!event->parent) {
+ atomic_dec(&nr_events);
+ if (event->attr.mmap || event->attr.mmap_data)
+ atomic_dec(&nr_mmap_events);
+ if (event->attr.comm)
+ atomic_dec(&nr_comm_events);
+ if (event->attr.task)
+ atomic_dec(&nr_task_events);
+ }
+
+ if (event->buffer) {
+ perf_buffer_put(event->buffer);
+ event->buffer = NULL;
+ }
+
+ if (event->destroy)
+ event->destroy(event);
+
+ put_ctx(event->ctx);
+ call_rcu(&event->rcu_head, free_event_rcu);
+}
+
+int perf_event_release_kernel(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+
+ /*
+ * Remove from the PMU, can't get re-enabled since we got
+ * here because the last ref went.
+ */
+ perf_event_disable(event);
+
+ WARN_ON_ONCE(ctx->parent_ctx);
+ /*
+ * There are two ways this annotation is useful:
+ *
+ * 1) there is a lock recursion from perf_event_exit_task
+ * see the comment there.
+ *
+ * 2) there is a lock-inversion with mmap_sem through
+ * perf_event_read_group(), which takes faults while
+ * holding ctx->mutex, however this is called after
+ * the last filedesc died, so there is no possibility
+ * to trigger the AB-BA case.
+ */
+ mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING);
+ raw_spin_lock_irq(&ctx->lock);
+ perf_group_detach(event);
+ list_del_event(event, ctx);
+ raw_spin_unlock_irq(&ctx->lock);
+ mutex_unlock(&ctx->mutex);
+
+ mutex_lock(&event->owner->perf_event_mutex);
+ list_del_init(&event->owner_entry);
+ mutex_unlock(&event->owner->perf_event_mutex);
+ put_task_struct(event->owner);
+
+ free_event(event);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(perf_event_release_kernel);
+
+/*
+ * Called when the last reference to the file is gone.
+ */
+static int perf_release(struct inode *inode, struct file *file)
+{
+ struct perf_event *event = file->private_data;
+
+ file->private_data = NULL;
+
+ return perf_event_release_kernel(event);
+}
+
+static int perf_event_read_size(struct perf_event *event)
+{
+ int entry = sizeof(u64); /* value */
+ int size = 0;
+ int nr = 1;
+
+ if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ size += sizeof(u64);
+
+ if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ size += sizeof(u64);
+
+ if (event->attr.read_format & PERF_FORMAT_ID)
+ entry += sizeof(u64);
+
+ if (event->attr.read_format & PERF_FORMAT_GROUP) {
+ nr += event->group_leader->nr_siblings;
+ size += sizeof(u64);
+ }
+
+ size += entry * nr;
+
+ return size;
+}
+
+u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
+{
+ struct perf_event *child;
+ u64 total = 0;
+
+ *enabled = 0;
+ *running = 0;
+
+ mutex_lock(&event->child_mutex);
+ total += perf_event_read(event);
+ *enabled += event->total_time_enabled +
+ atomic64_read(&event->child_total_time_enabled);
+ *running += event->total_time_running +
+ atomic64_read(&event->child_total_time_running);
+
+ list_for_each_entry(child, &event->child_list, child_list) {
+ total += perf_event_read(child);
+ *enabled += child->total_time_enabled;
+ *running += child->total_time_running;
+ }
+ mutex_unlock(&event->child_mutex);
+
+ return total;
+}
+EXPORT_SYMBOL_GPL(perf_event_read_value);
+
+static int perf_event_read_group(struct perf_event *event,
+ u64 read_format, char __user *buf)
+{
+ struct perf_event *leader = event->group_leader, *sub;
+ int n = 0, size = 0, ret = -EFAULT;
+ struct perf_event_context *ctx = leader->ctx;
+ u64 values[5];
+ u64 count, enabled, running;
+
+ mutex_lock(&ctx->mutex);
+ count = perf_event_read_value(leader, &enabled, &running);
+
+ values[n++] = 1 + leader->nr_siblings;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ values[n++] = enabled;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ values[n++] = running;
+ values[n++] = count;
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(leader);
+
+ size = n * sizeof(u64);
+
+ if (copy_to_user(buf, values, size))
+ goto unlock;
+
+ ret = size;
+
+ list_for_each_entry(sub, &leader->sibling_list, group_entry) {
+ n = 0;
+
+ values[n++] = perf_event_read_value(sub, &enabled, &running);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(sub);
+
+ size = n * sizeof(u64);
+
+ if (copy_to_user(buf + ret, values, size)) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ ret += size;
+ }
+unlock:
+ mutex_unlock(&ctx->mutex);
+
+ return ret;
+}
+
+static int perf_event_read_one(struct perf_event *event,
+ u64 read_format, char __user *buf)
+{
+ u64 enabled, running;
+ u64 values[4];
+ int n = 0;
+
+ values[n++] = perf_event_read_value(event, &enabled, &running);
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ values[n++] = enabled;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ values[n++] = running;
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(event);
+
+ if (copy_to_user(buf, values, n * sizeof(u64)))
+ return -EFAULT;
+
+ return n * sizeof(u64);
+}
+
+/*
+ * Read the performance event - simple non blocking version for now
+ */
+static ssize_t
+perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
+{
+ u64 read_format = event->attr.read_format;
+ int ret;
+
+ /*
+ * Return end-of-file for a read on a event that is in
+ * error state (i.e. because it was pinned but it couldn't be
+ * scheduled on to the CPU at some point).
+ */
+ if (event->state == PERF_EVENT_STATE_ERROR)
+ return 0;
+
+ if (count < perf_event_read_size(event))
+ return -ENOSPC;
+
+ WARN_ON_ONCE(event->ctx->parent_ctx);
+ if (read_format & PERF_FORMAT_GROUP)
+ ret = perf_event_read_group(event, read_format, buf);
+ else
+ ret = perf_event_read_one(event, read_format, buf);
+
+ return ret;
+}
+
+static ssize_t
+perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct perf_event *event = file->private_data;
+
+ return perf_read_hw(event, buf, count);
+}
+
+static unsigned int perf_poll(struct file *file, poll_table *wait)
+{
+ struct perf_event *event = file->private_data;
+ struct perf_buffer *buffer;
+ unsigned int events = POLL_HUP;
+
+ rcu_read_lock();
+ buffer = rcu_dereference(event->buffer);
+ if (buffer)
+ events = atomic_xchg(&buffer->poll, 0);
+ rcu_read_unlock();
+
+ poll_wait(file, &event->waitq, wait);
+
+ return events;
+}
+
+static void perf_event_reset(struct perf_event *event)
+{
+ (void)perf_event_read(event);
+ local64_set(&event->count, 0);
+ perf_event_update_userpage(event);
+}
+
+/*
+ * Holding the top-level event's child_mutex means that any
+ * descendant process that has inherited this event will block
+ * in sync_child_event if it goes to exit, thus satisfying the
+ * task existence requirements of perf_event_enable/disable.
+ */
+static void perf_event_for_each_child(struct perf_event *event,
+ void (*func)(struct perf_event *))
+{
+ struct perf_event *child;
+
+ WARN_ON_ONCE(event->ctx->parent_ctx);
+ mutex_lock(&event->child_mutex);
+ func(event);
+ list_for_each_entry(child, &event->child_list, child_list)
+ func(child);
+ mutex_unlock(&event->child_mutex);
+}
+
+static void perf_event_for_each(struct perf_event *event,
+ void (*func)(struct perf_event *))
+{
+ struct perf_event_context *ctx = event->ctx;
+ struct perf_event *sibling;
+
+ WARN_ON_ONCE(ctx->parent_ctx);
+ mutex_lock(&ctx->mutex);
+ event = event->group_leader;
+
+ perf_event_for_each_child(event, func);
+ func(event);
+ list_for_each_entry(sibling, &event->sibling_list, group_entry)
+ perf_event_for_each_child(event, func);
+ mutex_unlock(&ctx->mutex);
+}
+
+static int perf_event_period(struct perf_event *event, u64 __user *arg)
+{
+ struct perf_event_context *ctx = event->ctx;
+ int ret = 0;
+ u64 value;
+
+ if (!event->attr.sample_period)
+ return -EINVAL;
+
+ if (copy_from_user(&value, arg, sizeof(value)))
+ return -EFAULT;
+
+ if (!value)
+ return -EINVAL;
+
+ raw_spin_lock_irq(&ctx->lock);
+ if (event->attr.freq) {
+ if (value > sysctl_perf_event_sample_rate) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ event->attr.sample_freq = value;
+ } else {
+ event->attr.sample_period = value;
+ event->hw.sample_period = value;
+ }
+unlock:
+ raw_spin_unlock_irq(&ctx->lock);
+
+ return ret;
+}
+
+static const struct file_operations perf_fops;
+
+static struct perf_event *perf_fget_light(int fd, int *fput_needed)
+{
+ struct file *file;
+
+ file = fget_light(fd, fput_needed);
+ if (!file)
+ return ERR_PTR(-EBADF);
+
+ if (file->f_op != &perf_fops) {
+ fput_light(file, *fput_needed);
+ *fput_needed = 0;
+ return ERR_PTR(-EBADF);
+ }
+
+ return file->private_data;
+}
+
+static int perf_event_set_output(struct perf_event *event,
+ struct perf_event *output_event);
+static int perf_event_set_filter(struct perf_event *event, void __user *arg);
+
+static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct perf_event *event = file->private_data;
+ void (*func)(struct perf_event *);
+ u32 flags = arg;
+
+ switch (cmd) {
+ case PERF_EVENT_IOC_ENABLE:
+ func = perf_event_enable;
+ break;
+ case PERF_EVENT_IOC_DISABLE:
+ func = perf_event_disable;
+ break;
+ case PERF_EVENT_IOC_RESET:
+ func = perf_event_reset;
+ break;
+
+ case PERF_EVENT_IOC_REFRESH:
+ return perf_event_refresh(event, arg);
+
+ case PERF_EVENT_IOC_PERIOD:
+ return perf_event_period(event, (u64 __user *)arg);
+
+ case PERF_EVENT_IOC_SET_OUTPUT:
+ {
+ struct perf_event *output_event = NULL;
+ int fput_needed = 0;
+ int ret;
+
+ if (arg != -1) {
+ output_event = perf_fget_light(arg, &fput_needed);
+ if (IS_ERR(output_event))
+ return PTR_ERR(output_event);
+ }
+
+ ret = perf_event_set_output(event, output_event);
+ if (output_event)
+ fput_light(output_event->filp, fput_needed);
+
+ return ret;
+ }
+
+ case PERF_EVENT_IOC_SET_FILTER:
+ return perf_event_set_filter(event, (void __user *)arg);
+
+ default:
+ return -ENOTTY;
+ }
+
+ if (flags & PERF_IOC_FLAG_GROUP)
+ perf_event_for_each(event, func);
+ else
+ perf_event_for_each_child(event, func);
+
+ return 0;
+}
+
+int perf_event_task_enable(void)
+{
+ struct perf_event *event;
+
+ mutex_lock(¤t->perf_event_mutex);
+ list_for_each_entry(event, ¤t->perf_event_list, owner_entry)
+ perf_event_for_each_child(event, perf_event_enable);
+ mutex_unlock(¤t->perf_event_mutex);
+
+ return 0;
+}
+
+int perf_event_task_disable(void)
+{
+ struct perf_event *event;
+
+ mutex_lock(¤t->perf_event_mutex);
+ list_for_each_entry(event, ¤t->perf_event_list, owner_entry)
+ perf_event_for_each_child(event, perf_event_disable);
+ mutex_unlock(¤t->perf_event_mutex);
+
+ return 0;
+}
+
+#ifndef PERF_EVENT_INDEX_OFFSET
+# define PERF_EVENT_INDEX_OFFSET 0
+#endif
+
+static int perf_event_index(struct perf_event *event)
+{
+ if (event->state != PERF_EVENT_STATE_ACTIVE)
+ return 0;
+
+ return event->hw.idx + 1 - PERF_EVENT_INDEX_OFFSET;
+}
+
+/*
+ * Callers need to ensure there can be no nesting of this function, otherwise
+ * the seqlock logic goes bad. We can not serialize this because the arch
+ * code calls this from NMI context.
+ */
+void perf_event_update_userpage(struct perf_event *event)
+{
+ struct perf_event_mmap_page *userpg;
+ struct perf_buffer *buffer;
+
+ rcu_read_lock();
+ buffer = rcu_dereference(event->buffer);
+ if (!buffer)
+ goto unlock;
+
+ userpg = buffer->user_page;
+
+ /*
+ * Disable preemption so as to not let the corresponding user-space
+ * spin too long if we get preempted.
+ */
+ preempt_disable();
+ ++userpg->lock;
+ barrier();
+ userpg->index = perf_event_index(event);
+ userpg->offset = perf_event_count(event);
+ if (event->state == PERF_EVENT_STATE_ACTIVE)
+ userpg->offset -= local64_read(&event->hw.prev_count);
+
+ userpg->time_enabled = event->total_time_enabled +
+ atomic64_read(&event->child_total_time_enabled);
+
+ userpg->time_running = event->total_time_running +
+ atomic64_read(&event->child_total_time_running);
+
+ barrier();
+ ++userpg->lock;
+ preempt_enable();
+unlock:
+ rcu_read_unlock();
+}
+
+static unsigned long perf_data_size(struct perf_buffer *buffer);
+
+static void
+perf_buffer_init(struct perf_buffer *buffer, long watermark, int flags)
+{
+ long max_size = perf_data_size(buffer);
+
+ if (watermark)
+ buffer->watermark = min(max_size, watermark);
+
+ if (!buffer->watermark)
+ buffer->watermark = max_size / 2;
+
+ if (flags & PERF_BUFFER_WRITABLE)
+ buffer->writable = 1;
+
+ atomic_set(&buffer->refcount, 1);
+}
+
+#ifndef CONFIG_PERF_USE_VMALLOC
+
+/*
+ * Back perf_mmap() with regular GFP_KERNEL-0 pages.
+ */
+
+static struct page *
+perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff)
+{
+ if (pgoff > buffer->nr_pages)
+ return NULL;
+
+ if (pgoff == 0)
+ return virt_to_page(buffer->user_page);
+
+ return virt_to_page(buffer->data_pages[pgoff - 1]);
+}
+
+static void *perf_mmap_alloc_page(int cpu)
+{
+ struct page *page;
+ int node;
+
+ node = (cpu == -1) ? cpu : cpu_to_node(cpu);
+ page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+ if (!page)
+ return NULL;
+
+ return page_address(page);
+}
+
+static struct perf_buffer *
+perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
+{
+ struct perf_buffer *buffer;
+ unsigned long size;
+ int i;
+
+ size = sizeof(struct perf_buffer);
+ size += nr_pages * sizeof(void *);
+
+ buffer = kzalloc(size, GFP_KERNEL);
+ if (!buffer)
+ goto fail;
+
+ buffer->user_page = perf_mmap_alloc_page(cpu);
+ if (!buffer->user_page)
+ goto fail_user_page;
+
+ for (i = 0; i < nr_pages; i++) {
+ buffer->data_pages[i] = perf_mmap_alloc_page(cpu);
+ if (!buffer->data_pages[i])
+ goto fail_data_pages;
+ }
+
+ buffer->nr_pages = nr_pages;
+
+ perf_buffer_init(buffer, watermark, flags);
+
+ return buffer;
+
+fail_data_pages:
+ for (i--; i >= 0; i--)
+ free_page((unsigned long)buffer->data_pages[i]);
+
+ free_page((unsigned long)buffer->user_page);
+
+fail_user_page:
+ kfree(buffer);
+
+fail:
+ return NULL;
+}
+
+static void perf_mmap_free_page(unsigned long addr)
+{
+ struct page *page = virt_to_page((void *)addr);
+
+ page->mapping = NULL;
+ __free_page(page);
+}
+
+static void perf_buffer_free(struct perf_buffer *buffer)
+{
+ int i;
+
+ perf_mmap_free_page((unsigned long)buffer->user_page);
+ for (i = 0; i < buffer->nr_pages; i++)
+ perf_mmap_free_page((unsigned long)buffer->data_pages[i]);
+ kfree(buffer);
+}
+
+static inline int page_order(struct perf_buffer *buffer)
+{
+ return 0;
+}
+
+#else
+
+/*
+ * Back perf_mmap() with vmalloc memory.
+ *
+ * Required for architectures that have d-cache aliasing issues.
+ */
+
+static inline int page_order(struct perf_buffer *buffer)
+{
+ return buffer->page_order;
+}
+
+static struct page *
+perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff)
+{
+ if (pgoff > (1UL << page_order(buffer)))
+ return NULL;
+
+ return vmalloc_to_page((void *)buffer->user_page + pgoff * PAGE_SIZE);
+}
+
+static void perf_mmap_unmark_page(void *addr)
+{
+ struct page *page = vmalloc_to_page(addr);
+
+ page->mapping = NULL;
+}
+
+static void perf_buffer_free_work(struct work_struct *work)
+{
+ struct perf_buffer *buffer;
+ void *base;
+ int i, nr;
+
+ buffer = container_of(work, struct perf_buffer, work);
+ nr = 1 << page_order(buffer);
+
+ base = buffer->user_page;
+ for (i = 0; i < nr + 1; i++)
+ perf_mmap_unmark_page(base + (i * PAGE_SIZE));
+
+ vfree(base);
+ kfree(buffer);
+}
+
+static void perf_buffer_free(struct perf_buffer *buffer)
+{
+ schedule_work(&buffer->work);
+}
+
+static struct perf_buffer *
+perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
+{
+ struct perf_buffer *buffer;
+ unsigned long size;
+ void *all_buf;
+
+ size = sizeof(struct perf_buffer);
+ size += sizeof(void *);
+
+ buffer = kzalloc(size, GFP_KERNEL);
+ if (!buffer)
+ goto fail;
+
+ INIT_WORK(&buffer->work, perf_buffer_free_work);
+
+ all_buf = vmalloc_user((nr_pages + 1) * PAGE_SIZE);
+ if (!all_buf)
+ goto fail_all_buf;
+
+ buffer->user_page = all_buf;
+ buffer->data_pages[0] = all_buf + PAGE_SIZE;
+ buffer->page_order = ilog2(nr_pages);
+ buffer->nr_pages = 1;
+
+ perf_buffer_init(buffer, watermark, flags);
+
+ return buffer;
+
+fail_all_buf:
+ kfree(buffer);
+
+fail:
+ return NULL;
+}
+
+#endif
+
+static unsigned long perf_data_size(struct perf_buffer *buffer)
+{
+ return buffer->nr_pages << (PAGE_SHIFT + page_order(buffer));
+}
+
+static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct perf_event *event = vma->vm_file->private_data;
+ struct perf_buffer *buffer;
+ int ret = VM_FAULT_SIGBUS;
+
+ if (vmf->flags & FAULT_FLAG_MKWRITE) {
+ if (vmf->pgoff == 0)
+ ret = 0;
+ return ret;
+ }
+
+ rcu_read_lock();
+ buffer = rcu_dereference(event->buffer);
+ if (!buffer)
+ goto unlock;
+
+ if (vmf->pgoff && (vmf->flags & FAULT_FLAG_WRITE))
+ goto unlock;
+
+ vmf->page = perf_mmap_to_page(buffer, vmf->pgoff);
+ if (!vmf->page)
+ goto unlock;
+
+ get_page(vmf->page);
+ vmf->page->mapping = vma->vm_file->f_mapping;
+ vmf->page->index = vmf->pgoff;
+
+ ret = 0;
+unlock:
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static void perf_buffer_free_rcu(struct rcu_head *rcu_head)
+{
+ struct perf_buffer *buffer;
+
+ buffer = container_of(rcu_head, struct perf_buffer, rcu_head);
+ perf_buffer_free(buffer);
+}
+
+static struct perf_buffer *perf_buffer_get(struct perf_event *event)
+{
+ struct perf_buffer *buffer;
+
+ rcu_read_lock();
+ buffer = rcu_dereference(event->buffer);
+ if (buffer) {
+ if (!atomic_inc_not_zero(&buffer->refcount))
+ buffer = NULL;
+ }
+ rcu_read_unlock();
+
+ return buffer;
+}
+
+static void perf_buffer_put(struct perf_buffer *buffer)
+{
+ if (!atomic_dec_and_test(&buffer->refcount))
+ return;
+
+ call_rcu(&buffer->rcu_head, perf_buffer_free_rcu);
+}
+
+static void perf_mmap_open(struct vm_area_struct *vma)
+{
+ struct perf_event *event = vma->vm_file->private_data;
+
+ atomic_inc(&event->mmap_count);
+}
+
+static void perf_mmap_close(struct vm_area_struct *vma)
+{
+ struct perf_event *event = vma->vm_file->private_data;
+
+ if (atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) {
+ unsigned long size = perf_data_size(event->buffer);
+ struct user_struct *user = event->mmap_user;
+ struct perf_buffer *buffer = event->buffer;
+
+ atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm);
+ vma->vm_mm->locked_vm -= event->mmap_locked;
+ rcu_assign_pointer(event->buffer, NULL);
+ mutex_unlock(&event->mmap_mutex);
+
+ perf_buffer_put(buffer);
+ free_uid(user);
+ }
+}
+
+static const struct vm_operations_struct perf_mmap_vmops = {
+ .open = perf_mmap_open,
+ .close = perf_mmap_close,
+ .fault = perf_mmap_fault,
+ .page_mkwrite = perf_mmap_fault,
+};
+
+static int perf_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct perf_event *event = file->private_data;
+ unsigned long user_locked, user_lock_limit;
+ struct user_struct *user = current_user();
+ unsigned long locked, lock_limit;
+ struct perf_buffer *buffer;
+ unsigned long vma_size;
+ unsigned long nr_pages;
+ long user_extra, extra;
+ int ret = 0, flags = 0;
+
+ /*
+ * Don't allow mmap() of inherited per-task counters. This would
+ * create a performance issue due to all children writing to the
+ * same buffer.
+ */
+ if (event->cpu == -1 && event->attr.inherit)
+ return -EINVAL;
+
+ if (!(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ vma_size = vma->vm_end - vma->vm_start;
+ nr_pages = (vma_size / PAGE_SIZE) - 1;
+
+ /*
+ * If we have buffer pages ensure they're a power-of-two number, so we
+ * can do bitmasks instead of modulo.
+ */
+ if (nr_pages != 0 && !is_power_of_2(nr_pages))
+ return -EINVAL;
+
+ if (vma_size != PAGE_SIZE * (1 + nr_pages))
+ return -EINVAL;
+
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ WARN_ON_ONCE(event->ctx->parent_ctx);
+ mutex_lock(&event->mmap_mutex);
+ if (event->buffer) {
+ if (event->buffer->nr_pages == nr_pages)
+ atomic_inc(&event->buffer->refcount);
+ else
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ user_extra = nr_pages + 1;
+ user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10);
+
+ /*
+ * Increase the limit linearly with more CPUs:
+ */
+ user_lock_limit *= num_online_cpus();
+
+ user_locked = atomic_long_read(&user->locked_vm) + user_extra;
+
+ extra = 0;
+ if (user_locked > user_lock_limit)
+ extra = user_locked - user_lock_limit;
+
+ lock_limit = rlimit(RLIMIT_MEMLOCK);
+ lock_limit >>= PAGE_SHIFT;
+ locked = vma->vm_mm->locked_vm + extra;
+
+ if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() &&
+ !capable(CAP_IPC_LOCK)) {
+ ret = -EPERM;
+ goto unlock;
+ }
+
+ WARN_ON(event->buffer);
+
+ if (vma->vm_flags & VM_WRITE)
+ flags |= PERF_BUFFER_WRITABLE;
+
+ buffer = perf_buffer_alloc(nr_pages, event->attr.wakeup_watermark,
+ event->cpu, flags);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ rcu_assign_pointer(event->buffer, buffer);
+
+ atomic_long_add(user_extra, &user->locked_vm);
+ event->mmap_locked = extra;
+ event->mmap_user = get_current_user();
+ vma->vm_mm->locked_vm += event->mmap_locked;
+
+unlock:
+ if (!ret)
+ atomic_inc(&event->mmap_count);
+ mutex_unlock(&event->mmap_mutex);
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_ops = &perf_mmap_vmops;
+
+ return ret;
+}
+
+static int perf_fasync(int fd, struct file *filp, int on)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct perf_event *event = filp->private_data;
+ int retval;
+
+ mutex_lock(&inode->i_mutex);
+ retval = fasync_helper(fd, filp, on, &event->fasync);
+ mutex_unlock(&inode->i_mutex);
+
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+static const struct file_operations perf_fops = {
+ .llseek = no_llseek,
+ .release = perf_release,
+ .read = perf_read,
+ .poll = perf_poll,
+ .unlocked_ioctl = perf_ioctl,
+ .compat_ioctl = perf_ioctl,
+ .mmap = perf_mmap,
+ .fasync = perf_fasync,
+};
+
+/*
+ * Perf event wakeup
+ *
+ * If there's data, ensure we set the poll() state and publish everything
+ * to user-space before waking everybody up.
+ */
+
+void perf_event_wakeup(struct perf_event *event)
+{
+ wake_up_all(&event->waitq);
+
+ if (event->pending_kill) {
+ kill_fasync(&event->fasync, SIGIO, event->pending_kill);
+ event->pending_kill = 0;
+ }
+}
+
+/*
+ * Pending wakeups
+ *
+ * Handle the case where we need to wakeup up from NMI (or rq->lock) context.
+ *
+ * The NMI bit means we cannot possibly take locks. Therefore, maintain a
+ * single linked list and use cmpxchg() to add entries lockless.
+ */
+
+static void perf_pending_event(struct perf_pending_entry *entry)
+{
+ struct perf_event *event = container_of(entry,
+ struct perf_event, pending);
+
+ if (event->pending_disable) {
+ event->pending_disable = 0;
+ __perf_event_disable(event);
+ }
+
+ if (event->pending_wakeup) {
+ event->pending_wakeup = 0;
+ perf_event_wakeup(event);
+ }
+}
+
+#define PENDING_TAIL ((struct perf_pending_entry *)-1UL)
+
+static DEFINE_PER_CPU(struct perf_pending_entry *, perf_pending_head) = {
+ PENDING_TAIL,
+};
+
+static void perf_pending_queue(struct perf_pending_entry *entry,
+ void (*func)(struct perf_pending_entry *))
+{
+ struct perf_pending_entry **head;
+
+ if (cmpxchg(&entry->next, NULL, PENDING_TAIL) != NULL)
+ return;
+
+ entry->func = func;
+
+ head = &get_cpu_var(perf_pending_head);
+
+ do {
+ entry->next = *head;
+ } while (cmpxchg(head, entry->next, entry) != entry->next);
+
+ set_perf_event_pending();
+
+ put_cpu_var(perf_pending_head);
+}
+
+static int __perf_pending_run(void)
+{
+ struct perf_pending_entry *list;
+ int nr = 0;
+
+ list = xchg(&__get_cpu_var(perf_pending_head), PENDING_TAIL);
+ while (list != PENDING_TAIL) {
+ void (*func)(struct perf_pending_entry *);
+ struct perf_pending_entry *entry = list;
+
+ list = list->next;
+
+ func = entry->func;
+ entry->next = NULL;
+ /*
+ * Ensure we observe the unqueue before we issue the wakeup,
+ * so that we won't be waiting forever.
+ * -- see perf_not_pending().
+ */
+ smp_wmb();
+
+ func(entry);
+ nr++;
+ }
+
+ return nr;
+}
+
+static inline int perf_not_pending(struct perf_event *event)
+{
+ /*
+ * If we flush on whatever cpu we run, there is a chance we don't
+ * need to wait.
+ */
+ get_cpu();
+ __perf_pending_run();
+ put_cpu();
+
+ /*
+ * Ensure we see the proper queue state before going to sleep
+ * so that we do not miss the wakeup. -- see perf_pending_handle()
+ */
+ smp_rmb();
+ return event->pending.next == NULL;
+}
+
+static void perf_pending_sync(struct perf_event *event)
+{
+ wait_event(event->waitq, perf_not_pending(event));
+}
+
+void perf_event_do_pending(void)
+{
+ __perf_pending_run();
+}
+
+/*
+ * Callchain support -- arch specific
+ */
+
+__weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
+{
+ return NULL;
+}
+
+
+/*
+ * We assume there is only KVM supporting the callbacks.
+ * Later on, we might change it to a list if there is
+ * another virtualization implementation supporting the callbacks.
+ */
+struct perf_guest_info_callbacks *perf_guest_cbs;
+
+int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
+{
+ perf_guest_cbs = cbs;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks);
+
+int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
+{
+ perf_guest_cbs = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
+
+/*
+ * Output
+ */
+static bool perf_output_space(struct perf_buffer *buffer, unsigned long tail,
+ unsigned long offset, unsigned long head)
+{
+ unsigned long mask;
+
+ if (!buffer->writable)
+ return true;
+
+ mask = perf_data_size(buffer) - 1;
+
+ offset = (offset - tail) & mask;
+ head = (head - tail) & mask;
+
+ if ((int)(head - offset) < 0)
+ return false;
+
+ return true;
+}
+
+static void perf_output_wakeup(struct perf_output_handle *handle)
+{
+ atomic_set(&handle->buffer->poll, POLL_IN);
+
+ if (handle->nmi) {
+ handle->event->pending_wakeup = 1;
+ perf_pending_queue(&handle->event->pending,
+ perf_pending_event);
+ } else
+ perf_event_wakeup(handle->event);
+}
+
+/*
+ * We need to ensure a later event_id doesn't publish a head when a former
+ * event isn't done writing. However since we need to deal with NMIs we
+ * cannot fully serialize things.
+ *
+ * We only publish the head (and generate a wakeup) when the outer-most
+ * event completes.
+ */
+static void perf_output_get_handle(struct perf_output_handle *handle)
+{
+ struct perf_buffer *buffer = handle->buffer;
+
+ preempt_disable();
+ local_inc(&buffer->nest);
+ handle->wakeup = local_read(&buffer->wakeup);
+}
+
+static void perf_output_put_handle(struct perf_output_handle *handle)
+{
+ struct perf_buffer *buffer = handle->buffer;
+ unsigned long head;
+
+again:
+ head = local_read(&buffer->head);
+
+ /*
+ * IRQ/NMI can happen here, which means we can miss a head update.
+ */
+
+ if (!local_dec_and_test(&buffer->nest))
+ goto out;
+
+ /*
+ * Publish the known good head. Rely on the full barrier implied
+ * by atomic_dec_and_test() order the buffer->head read and this
+ * write.
+ */
+ buffer->user_page->data_head = head;
+
+ /*
+ * Now check if we missed an update, rely on the (compiler)
+ * barrier in atomic_dec_and_test() to re-read buffer->head.
+ */
+ if (unlikely(head != local_read(&buffer->head))) {
+ local_inc(&buffer->nest);
+ goto again;
+ }
+
+ if (handle->wakeup != local_read(&buffer->wakeup))
+ perf_output_wakeup(handle);
+
+ out:
+ preempt_enable();
+}
+
+__always_inline void perf_output_copy(struct perf_output_handle *handle,
+ const void *buf, unsigned int len)
+{
+ do {
+ unsigned long size = min_t(unsigned long, handle->size, len);
+
+ memcpy(handle->addr, buf, size);
+
+ len -= size;
+ handle->addr += size;
+ buf += size;
+ handle->size -= size;
+ if (!handle->size) {
+ struct perf_buffer *buffer = handle->buffer;
+
+ handle->page++;
+ handle->page &= buffer->nr_pages - 1;
+ handle->addr = buffer->data_pages[handle->page];
+ handle->size = PAGE_SIZE << page_order(buffer);
+ }
+ } while (len);
+}
+
+int perf_output_begin(struct perf_output_handle *handle,
+ struct perf_event *event, unsigned int size,
+ int nmi, int sample)
+{
+ struct perf_buffer *buffer;
+ unsigned long tail, offset, head;
+ int have_lost;
+ struct {
+ struct perf_event_header header;
+ u64 id;
+ u64 lost;
+ } lost_event;
+
+ rcu_read_lock();
+ /*
+ * For inherited events we send all the output towards the parent.
+ */
+ if (event->parent)
+ event = event->parent;
+
+ buffer = rcu_dereference(event->buffer);
+ if (!buffer)
+ goto out;
+
+ handle->buffer = buffer;
+ handle->event = event;
+ handle->nmi = nmi;
+ handle->sample = sample;
+
+ if (!buffer->nr_pages)
+ goto out;
+
+ have_lost = local_read(&buffer->lost);
+ if (have_lost)
+ size += sizeof(lost_event);
+
+ perf_output_get_handle(handle);
+
+ do {
+ /*
+ * Userspace could choose to issue a mb() before updating the
+ * tail pointer. So that all reads will be completed before the
+ * write is issued.
+ */
+ tail = ACCESS_ONCE(buffer->user_page->data_tail);
+ smp_rmb();
+ offset = head = local_read(&buffer->head);
+ head += size;
+ if (unlikely(!perf_output_space(buffer, tail, offset, head)))
+ goto fail;
+ } while (local_cmpxchg(&buffer->head, offset, head) != offset);
+
+ if (head - local_read(&buffer->wakeup) > buffer->watermark)
+ local_add(buffer->watermark, &buffer->wakeup);
+
+ handle->page = offset >> (PAGE_SHIFT + page_order(buffer));
+ handle->page &= buffer->nr_pages - 1;
+ handle->size = offset & ((PAGE_SIZE << page_order(buffer)) - 1);
+ handle->addr = buffer->data_pages[handle->page];
+ handle->addr += handle->size;
+ handle->size = (PAGE_SIZE << page_order(buffer)) - handle->size;
+
+ if (have_lost) {
+ lost_event.header.type = PERF_RECORD_LOST;
+ lost_event.header.misc = 0;
+ lost_event.header.size = sizeof(lost_event);
+ lost_event.id = event->id;
+ lost_event.lost = local_xchg(&buffer->lost, 0);
+
+ perf_output_put(handle, lost_event);
+ }
+
+ return 0;
+
+fail:
+ local_inc(&buffer->lost);
+ perf_output_put_handle(handle);
+out:
+ rcu_read_unlock();
+
+ return -ENOSPC;
+}
+
+void perf_output_end(struct perf_output_handle *handle)
+{
+ struct perf_event *event = handle->event;
+ struct perf_buffer *buffer = handle->buffer;
+
+ int wakeup_events = event->attr.wakeup_events;
+
+ if (handle->sample && wakeup_events) {
+ int events = local_inc_return(&buffer->events);
+ if (events >= wakeup_events) {
+ local_sub(wakeup_events, &buffer->events);
+ local_inc(&buffer->wakeup);
+ }
+ }
+
+ perf_output_put_handle(handle);
+ rcu_read_unlock();
+}
+
+static u32 perf_event_pid(struct perf_event *event, struct task_struct *p)
+{
+ /*
+ * only top level events have the pid namespace they were created in
+ */
+ if (event->parent)
+ event = event->parent;
+
+ return task_tgid_nr_ns(p, event->ns);
+}
+
+static u32 perf_event_tid(struct perf_event *event, struct task_struct *p)
+{
+ /*
+ * only top level events have the pid namespace they were created in
+ */
+ if (event->parent)
+ event = event->parent;
+
+ return task_pid_nr_ns(p, event->ns);
+}
+
+static void perf_output_read_one(struct perf_output_handle *handle,
+ struct perf_event *event)
+{
+ u64 read_format = event->attr.read_format;
+ u64 values[4];
+ int n = 0;
+
+ values[n++] = perf_event_count(event);
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+ values[n++] = event->total_time_enabled +
+ atomic64_read(&event->child_total_time_enabled);
+ }
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+ values[n++] = event->total_time_running +
+ atomic64_read(&event->child_total_time_running);
+ }
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(event);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+}
+
+/*
+ * XXX PERF_FORMAT_GROUP vs inherited events seems difficult.
+ */
+static void perf_output_read_group(struct perf_output_handle *handle,
+ struct perf_event *event)
+{
+ struct perf_event *leader = event->group_leader, *sub;
+ u64 read_format = event->attr.read_format;
+ u64 values[5];
+ int n = 0;
+
+ values[n++] = 1 + leader->nr_siblings;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ values[n++] = leader->total_time_enabled;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ values[n++] = leader->total_time_running;
+
+ if (leader != event)
+ leader->pmu->read(leader);
+
+ values[n++] = perf_event_count(leader);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(leader);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+
+ list_for_each_entry(sub, &leader->sibling_list, group_entry) {
+ n = 0;
+
+ if (sub != event)
+ sub->pmu->read(sub);
+
+ values[n++] = perf_event_count(sub);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_event_id(sub);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+ }
+}
+
+static void perf_output_read(struct perf_output_handle *handle,
+ struct perf_event *event)
+{
+ if (event->attr.read_format & PERF_FORMAT_GROUP)
+ perf_output_read_group(handle, event);
+ else
+ perf_output_read_one(handle, event);
+}
+
+void perf_output_sample(struct perf_output_handle *handle,
+ struct perf_event_header *header,
+ struct perf_sample_data *data,
+ struct perf_event *event)
+{
+ u64 sample_type = data->type;
+
+ perf_output_put(handle, *header);
+
+ if (sample_type & PERF_SAMPLE_IP)
+ perf_output_put(handle, data->ip);
+
+ if (sample_type & PERF_SAMPLE_TID)
+ perf_output_put(handle, data->tid_entry);
+
+ if (sample_type & PERF_SAMPLE_TIME)
+ perf_output_put(handle, data->time);
+
+ if (sample_type & PERF_SAMPLE_ADDR)
+ perf_output_put(handle, data->addr);
+
+ if (sample_type & PERF_SAMPLE_ID)
+ perf_output_put(handle, data->id);
+
+ if (sample_type & PERF_SAMPLE_STREAM_ID)
+ perf_output_put(handle, data->stream_id);
+
+ if (sample_type & PERF_SAMPLE_CPU)
+ perf_output_put(handle, data->cpu_entry);
+
+ if (sample_type & PERF_SAMPLE_PERIOD)
+ perf_output_put(handle, data->period);
+
+ if (sample_type & PERF_SAMPLE_READ)
+ perf_output_read(handle, event);
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+ if (data->callchain) {
+ int size = 1;
+
+ if (data->callchain)
+ size += data->callchain->nr;
+
+ size *= sizeof(u64);
+
+ perf_output_copy(handle, data->callchain, size);
+ } else {
+ u64 nr = 0;
+ perf_output_put(handle, nr);
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_RAW) {
+ if (data->raw) {
+ perf_output_put(handle, data->raw->size);
+ perf_output_copy(handle, data->raw->data,
+ data->raw->size);
+ } else {
+ struct {
+ u32 size;
+ u32 data;
+ } raw = {
+ .size = sizeof(u32),
+ .data = 0,
+ };
+ perf_output_put(handle, raw);
+ }
+ }
+}
+
+void perf_prepare_sample(struct perf_event_header *header,
+ struct perf_sample_data *data,
+ struct perf_event *event,
+ struct pt_regs *regs)
+{
+ u64 sample_type = event->attr.sample_type;
+
+ data->type = sample_type;
+
+ header->type = PERF_RECORD_SAMPLE;
+ header->size = sizeof(*header);
+
+ header->misc = 0;
+ header->misc |= perf_misc_flags(regs);
+
+ if (sample_type & PERF_SAMPLE_IP) {
+ data->ip = perf_instruction_pointer(regs);
+
+ header->size += sizeof(data->ip);
+ }
+
+ if (sample_type & PERF_SAMPLE_TID) {
+ /* namespace issues */
+ data->tid_entry.pid = perf_event_pid(event, current);
+ data->tid_entry.tid = perf_event_tid(event, current);
+
+ header->size += sizeof(data->tid_entry);
+ }
+
+ if (sample_type & PERF_SAMPLE_TIME) {
+ data->time = perf_clock();
+
+ header->size += sizeof(data->time);
+ }
+
+ if (sample_type & PERF_SAMPLE_ADDR)
+ header->size += sizeof(data->addr);
+
+ if (sample_type & PERF_SAMPLE_ID) {
+ data->id = primary_event_id(event);
+
+ header->size += sizeof(data->id);
+ }
+
+ if (sample_type & PERF_SAMPLE_STREAM_ID) {
+ data->stream_id = event->id;
+
+ header->size += sizeof(data->stream_id);
+ }
+
+ if (sample_type & PERF_SAMPLE_CPU) {
+ data->cpu_entry.cpu = raw_smp_processor_id();
+ data->cpu_entry.reserved = 0;
+
+ header->size += sizeof(data->cpu_entry);
+ }
+
+ if (sample_type & PERF_SAMPLE_PERIOD)
+ header->size += sizeof(data->period);
+
+ if (sample_type & PERF_SAMPLE_READ)
+ header->size += perf_event_read_size(event);
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+ int size = 1;
+
+ data->callchain = perf_callchain(regs);
+
+ if (data->callchain)
+ size += data->callchain->nr;
+
+ header->size += size * sizeof(u64);
+ }
+
+ if (sample_type & PERF_SAMPLE_RAW) {
+ int size = sizeof(u32);
+
+ if (data->raw)
+ size += data->raw->size;
+ else
+ size += sizeof(u32);
+
+ WARN_ON_ONCE(size & (sizeof(u64)-1));
+ header->size += size;
+ }
+}
+
+static void perf_event_output(struct perf_event *event, int nmi,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct perf_output_handle handle;
+ struct perf_event_header header;
+
+ perf_prepare_sample(&header, data, event, regs);
+
+ if (perf_output_begin(&handle, event, header.size, nmi, 1))
+ return;
+
+ perf_output_sample(&handle, &header, data, event);
+
+ perf_output_end(&handle);
+}
+
+/*
+ * read event_id
+ */
+
+struct perf_read_event {
+ struct perf_event_header header;
+
+ u32 pid;
+ u32 tid;
+};
+
+static void
+perf_event_read_event(struct perf_event *event,
+ struct task_struct *task)
+{
+ struct perf_output_handle handle;
+ struct perf_read_event read_event = {
+ .header = {
+ .type = PERF_RECORD_READ,
+ .misc = 0,
+ .size = sizeof(read_event) + perf_event_read_size(event),
+ },
+ .pid = perf_event_pid(event, task),
+ .tid = perf_event_tid(event, task),
+ };
+ int ret;
+
+ ret = perf_output_begin(&handle, event, read_event.header.size, 0, 0);
+ if (ret)
+ return;
+
+ perf_output_put(&handle, read_event);
+ perf_output_read(&handle, event);
+
+ perf_output_end(&handle);
+}
+
+/*
+ * task tracking -- fork/exit
+ *
+ * enabled by: attr.comm | attr.mmap | attr.mmap_data | attr.task
+ */
+
+struct perf_task_event {
+ struct task_struct *task;
+ struct perf_event_context *task_ctx;
+
+ struct {
+ struct perf_event_header header;
+
+ u32 pid;
+ u32 ppid;
+ u32 tid;
+ u32 ptid;
+ u64 time;
+ } event_id;
+};
+
+static void perf_event_task_output(struct perf_event *event,
+ struct perf_task_event *task_event)
+{
+ struct perf_output_handle handle;
+ struct task_struct *task = task_event->task;
+ int size, ret;
+
+ size = task_event->event_id.header.size;
+ ret = perf_output_begin(&handle, event, size, 0, 0);
+
+ if (ret)
+ return;
+
+ task_event->event_id.pid = perf_event_pid(event, task);
+ task_event->event_id.ppid = perf_event_pid(event, current);
+
+ task_event->event_id.tid = perf_event_tid(event, task);
+ task_event->event_id.ptid = perf_event_tid(event, current);
+
+ perf_output_put(&handle, task_event->event_id);
+
+ perf_output_end(&handle);
+}
+
+static int perf_event_task_match(struct perf_event *event)
+{
+ if (event->state < PERF_EVENT_STATE_INACTIVE)
+ return 0;
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ return 0;
+
+ if (event->attr.comm || event->attr.mmap ||
+ event->attr.mmap_data || event->attr.task)
+ return 1;
+
+ return 0;
+}
+
+static void perf_event_task_ctx(struct perf_event_context *ctx,
+ struct perf_task_event *task_event)
+{
+ struct perf_event *event;
+
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (perf_event_task_match(event))
+ perf_event_task_output(event, task_event);
+ }
+}
+
+static void perf_event_task_event(struct perf_task_event *task_event)
+{
+ struct perf_cpu_context *cpuctx;
+ struct perf_event_context *ctx = task_event->task_ctx;
+
+ rcu_read_lock();
+ cpuctx = &get_cpu_var(perf_cpu_context);
+ perf_event_task_ctx(&cpuctx->ctx, task_event);
+ if (!ctx)
+ ctx = rcu_dereference(current->perf_event_ctxp);
+ if (ctx)
+ perf_event_task_ctx(ctx, task_event);
+ put_cpu_var(perf_cpu_context);
+ rcu_read_unlock();
+}
+
+static void perf_event_task(struct task_struct *task,
+ struct perf_event_context *task_ctx,
+ int new)
+{
+ struct perf_task_event task_event;
+
+ if (!atomic_read(&nr_comm_events) &&
+ !atomic_read(&nr_mmap_events) &&
+ !atomic_read(&nr_task_events))
+ return;
+
+ task_event = (struct perf_task_event){
+ .task = task,
+ .task_ctx = task_ctx,
+ .event_id = {
+ .header = {
+ .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT,
+ .misc = 0,
+ .size = sizeof(task_event.event_id),
+ },
+ /* .pid */
+ /* .ppid */
+ /* .tid */
+ /* .ptid */
+ .time = perf_clock(),
+ },
+ };
+
+ perf_event_task_event(&task_event);
+}
+
+void perf_event_fork(struct task_struct *task)
+{
+ perf_event_task(task, NULL, 1);
+}
+
+/*
+ * comm tracking
+ */
+
+struct perf_comm_event {
+ struct task_struct *task;
+ char *comm;
+ int comm_size;
+
+ struct {
+ struct perf_event_header header;
+
+ u32 pid;
+ u32 tid;
+ } event_id;
+};
+
+static void perf_event_comm_output(struct perf_event *event,
+ struct perf_comm_event *comm_event)
+{
+ struct perf_output_handle handle;
+ int size = comm_event->event_id.header.size;
+ int ret = perf_output_begin(&handle, event, size, 0, 0);
+
+ if (ret)
+ return;
+
+ comm_event->event_id.pid = perf_event_pid(event, comm_event->task);
+ comm_event->event_id.tid = perf_event_tid(event, comm_event->task);
+
+ perf_output_put(&handle, comm_event->event_id);
+ perf_output_copy(&handle, comm_event->comm,
+ comm_event->comm_size);
+ perf_output_end(&handle);
+}
+
+static int perf_event_comm_match(struct perf_event *event)
+{
+ if (event->state < PERF_EVENT_STATE_INACTIVE)
+ return 0;
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ return 0;
+
+ if (event->attr.comm)
+ return 1;
+
+ return 0;
+}
+
+static void perf_event_comm_ctx(struct perf_event_context *ctx,
+ struct perf_comm_event *comm_event)
+{
+ struct perf_event *event;
+
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (perf_event_comm_match(event))
+ perf_event_comm_output(event, comm_event);
+ }
+}
+
+static void perf_event_comm_event(struct perf_comm_event *comm_event)
+{
+ struct perf_cpu_context *cpuctx;
+ struct perf_event_context *ctx;
+ unsigned int size;
+ char comm[TASK_COMM_LEN];
+
+ memset(comm, 0, sizeof(comm));
+ strlcpy(comm, comm_event->task->comm, sizeof(comm));
+ size = ALIGN(strlen(comm)+1, sizeof(u64));
+
+ comm_event->comm = comm;
+ comm_event->comm_size = size;
+
+ comm_event->event_id.header.size = sizeof(comm_event->event_id) + size;
+
+ rcu_read_lock();
+ cpuctx = &get_cpu_var(perf_cpu_context);
+ perf_event_comm_ctx(&cpuctx->ctx, comm_event);
+ ctx = rcu_dereference(current->perf_event_ctxp);
+ if (ctx)
+ perf_event_comm_ctx(ctx, comm_event);
+ put_cpu_var(perf_cpu_context);
+ rcu_read_unlock();
+}
+
+void perf_event_comm(struct task_struct *task)
+{
+ struct perf_comm_event comm_event;
+
+ if (task->perf_event_ctxp)
+ perf_event_enable_on_exec(task);
+
+ if (!atomic_read(&nr_comm_events))
+ return;
+
+ comm_event = (struct perf_comm_event){
+ .task = task,
+ /* .comm */
+ /* .comm_size */
+ .event_id = {
+ .header = {
+ .type = PERF_RECORD_COMM,
+ .misc = 0,
+ /* .size */
+ },
+ /* .pid */
+ /* .tid */
+ },
+ };
+
+ perf_event_comm_event(&comm_event);
+}
+
+/*
+ * mmap tracking
+ */
+
+struct perf_mmap_event {
+ struct vm_area_struct *vma;
+
+ const char *file_name;
+ int file_size;
+
+ struct {
+ struct perf_event_header header;
+
+ u32 pid;
+ u32 tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ } event_id;
+};
+
+static void perf_event_mmap_output(struct perf_event *event,
+ struct perf_mmap_event *mmap_event)
+{
+ struct perf_output_handle handle;
+ int size = mmap_event->event_id.header.size;
+ int ret = perf_output_begin(&handle, event, size, 0, 0);
+
+ if (ret)
+ return;
+
+ mmap_event->event_id.pid = perf_event_pid(event, current);
+ mmap_event->event_id.tid = perf_event_tid(event, current);
+
+ perf_output_put(&handle, mmap_event->event_id);
+ perf_output_copy(&handle, mmap_event->file_name,
+ mmap_event->file_size);
+ perf_output_end(&handle);
+}
+
+static int perf_event_mmap_match(struct perf_event *event,
+ struct perf_mmap_event *mmap_event,
+ int executable)
+{
+ if (event->state < PERF_EVENT_STATE_INACTIVE)
+ return 0;
+
+ if (event->cpu != -1 && event->cpu != smp_processor_id())
+ return 0;
+
+ if ((!executable && event->attr.mmap_data) ||
+ (executable && event->attr.mmap))
+ return 1;
+
+ return 0;
+}
+
+static void perf_event_mmap_ctx(struct perf_event_context *ctx,
+ struct perf_mmap_event *mmap_event,
+ int executable)
+{
+ struct perf_event *event;
+
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (perf_event_mmap_match(event, mmap_event, executable))
+ perf_event_mmap_output(event, mmap_event);
+ }
+}
+
+static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
+{
+ struct perf_cpu_context *cpuctx;
+ struct perf_event_context *ctx;
+ struct vm_area_struct *vma = mmap_event->vma;
+ struct file *file = vma->vm_file;
+ unsigned int size;
+ char tmp[16];
+ char *buf = NULL;
+ const char *name;
+
+ memset(tmp, 0, sizeof(tmp));
+
+ if (file) {
+ /*
+ * d_path works from the end of the buffer backwards, so we
+ * need to add enough zero bytes after the string to handle
+ * the 64bit alignment we do later.
+ */
+ buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL);
+ if (!buf) {
+ name = strncpy(tmp, "//enomem", sizeof(tmp));
+ goto got_name;
+ }
+ name = d_path(&file->f_path, buf, PATH_MAX);
+ if (IS_ERR(name)) {
+ name = strncpy(tmp, "//toolong", sizeof(tmp));
+ goto got_name;
+ }
+ } else {
+ if (arch_vma_name(mmap_event->vma)) {
+ name = strncpy(tmp, arch_vma_name(mmap_event->vma),
+ sizeof(tmp));
+ goto got_name;
+ }
+
+ if (!vma->vm_mm) {
+ name = strncpy(tmp, "[vdso]", sizeof(tmp));
+ goto got_name;
+ } else if (vma->vm_start <= vma->vm_mm->start_brk &&
+ vma->vm_end >= vma->vm_mm->brk) {
+ name = strncpy(tmp, "[heap]", sizeof(tmp));
+ goto got_name;
+ } else if (vma->vm_start <= vma->vm_mm->start_stack &&
+ vma->vm_end >= vma->vm_mm->start_stack) {
+ name = strncpy(tmp, "[stack]", sizeof(tmp));
+ goto got_name;
+ }
+
+ name = strncpy(tmp, "//anon", sizeof(tmp));
+ goto got_name;
+ }
+
+got_name:
+ size = ALIGN(strlen(name)+1, sizeof(u64));
+
+ mmap_event->file_name = name;
+ mmap_event->file_size = size;
+
+ mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size;
+
+ rcu_read_lock();
+ cpuctx = &get_cpu_var(perf_cpu_context);
+ perf_event_mmap_ctx(&cpuctx->ctx, mmap_event, vma->vm_flags & VM_EXEC);
+ ctx = rcu_dereference(current->perf_event_ctxp);
+ if (ctx)
+ perf_event_mmap_ctx(ctx, mmap_event, vma->vm_flags & VM_EXEC);
+ put_cpu_var(perf_cpu_context);
+ rcu_read_unlock();
+
+ kfree(buf);
+}
+
+void perf_event_mmap(struct vm_area_struct *vma)
+{
+ struct perf_mmap_event mmap_event;
+
+ if (!atomic_read(&nr_mmap_events))
+ return;
+
+ mmap_event = (struct perf_mmap_event){
+ .vma = vma,
+ /* .file_name */
+ /* .file_size */
+ .event_id = {
+ .header = {
+ .type = PERF_RECORD_MMAP,
+ .misc = PERF_RECORD_MISC_USER,
+ /* .size */
+ },
+ /* .pid */
+ /* .tid */
+ .start = vma->vm_start,
+ .len = vma->vm_end - vma->vm_start,
+ .pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT,
+ },
+ };
+
+ perf_event_mmap_event(&mmap_event);
+}
+
+/*
+ * IRQ throttle logging
+ */
+
+static void perf_log_throttle(struct perf_event *event, int enable)
+{
+ struct perf_output_handle handle;
+ int ret;
+
+ struct {
+ struct perf_event_header header;
+ u64 time;
+ u64 id;
+ u64 stream_id;
+ } throttle_event = {
+ .header = {
+ .type = PERF_RECORD_THROTTLE,
+ .misc = 0,
+ .size = sizeof(throttle_event),
+ },
+ .time = perf_clock(),
+ .id = primary_event_id(event),
+ .stream_id = event->id,
+ };
+
+ if (enable)
+ throttle_event.header.type = PERF_RECORD_UNTHROTTLE;
+
+ ret = perf_output_begin(&handle, event, sizeof(throttle_event), 1, 0);
+ if (ret)
+ return;
+
+ perf_output_put(&handle, throttle_event);
+ perf_output_end(&handle);
+}
+
+/*
+ * Generic event overflow handling, sampling.
+ */
+
+static int __perf_event_overflow(struct perf_event *event, int nmi,
+ int throttle, struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ int events = atomic_read(&event->event_limit);
+ struct hw_perf_event *hwc = &event->hw;
+ int ret = 0;
+
+ throttle = (throttle && event->pmu->unthrottle != NULL);
+
+ if (!throttle) {
+ hwc->interrupts++;
+ } else {
+ if (hwc->interrupts != MAX_INTERRUPTS) {
+ hwc->interrupts++;
+ if (HZ * hwc->interrupts >
+ (u64)sysctl_perf_event_sample_rate) {
+ hwc->interrupts = MAX_INTERRUPTS;
+ perf_log_throttle(event, 0);
+ ret = 1;
+ }
+ } else {
+ /*
+ * Keep re-disabling events even though on the previous
+ * pass we disabled it - just in case we raced with a
+ * sched-in and the event got enabled again:
+ */
+ ret = 1;
+ }
+ }
+
+ if (event->attr.freq) {
+ u64 now = perf_clock();
+ s64 delta = now - hwc->freq_time_stamp;
+
+ hwc->freq_time_stamp = now;
+
+ if (delta > 0 && delta < 2*TICK_NSEC)
+ perf_adjust_period(event, delta, hwc->last_period);
+ }
+
+ /*
+ * XXX event_limit might not quite work as expected on inherited
+ * events
+ */
+
+ event->pending_kill = POLL_IN;
+ if (events && atomic_dec_and_test(&event->event_limit)) {
+ ret = 1;
+ event->pending_kill = POLL_HUP;
+ if (nmi) {
+ event->pending_disable = 1;
+ perf_pending_queue(&event->pending,
+ perf_pending_event);
+ } else
+ perf_event_disable(event);
+ }
+
+ if (event->overflow_handler)
+ event->overflow_handler(event, nmi, data, regs);
+ else
+ perf_event_output(event, nmi, data, regs);
+
+ return ret;
+}
+
+int perf_event_overflow(struct perf_event *event, int nmi,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ return __perf_event_overflow(event, nmi, 1, data, regs);
+}
+
+/*
+ * Generic software event infrastructure
+ */
+
+/*
+ * We directly increment event->count and keep a second value in
+ * event->hw.period_left to count intervals. This period event
+ * is kept in the range [-sample_period, 0] so that we can use the
+ * sign as trigger.
+ */
+
+static u64 perf_swevent_set_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 period = hwc->last_period;
+ u64 nr, offset;
+ s64 old, val;
+
+ hwc->last_period = hwc->sample_period;
+
+again:
+ old = val = local64_read(&hwc->period_left);
+ if (val < 0)
+ return 0;
+
+ nr = div64_u64(period + val, period);
+ offset = nr * period;
+ val -= offset;
+ if (local64_cmpxchg(&hwc->period_left, old, val) != old)
+ goto again;
+
+ return nr;
+}
+
+static void perf_swevent_overflow(struct perf_event *event, u64 overflow,
+ int nmi, struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int throttle = 0;
+
+ data->period = event->hw.last_period;
+ if (!overflow)
+ overflow = perf_swevent_set_period(event);
+
+ if (hwc->interrupts == MAX_INTERRUPTS)
+ return;
+
+ for (; overflow; overflow--) {
+ if (__perf_event_overflow(event, nmi, throttle,
+ data, regs)) {
+ /*
+ * We inhibit the overflow from happening when
+ * hwc->interrupts == MAX_INTERRUPTS.
+ */
+ break;
+ }
+ throttle = 1;
+ }
+}
+
+static void perf_swevent_add(struct perf_event *event, u64 nr,
+ int nmi, struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ local64_add(nr, &event->count);
+
+ if (!regs)
+ return;
+
+ if (!hwc->sample_period)
+ return;
+
+ if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
+ return perf_swevent_overflow(event, 1, nmi, data, regs);
+
+ if (local64_add_negative(nr, &hwc->period_left))
+ return;
+
+ perf_swevent_overflow(event, 0, nmi, data, regs);
+}
+
+static int perf_exclude_event(struct perf_event *event,
+ struct pt_regs *regs)
+{
+ if (regs) {
+ if (event->attr.exclude_user && user_mode(regs))
+ return 1;
+
+ if (event->attr.exclude_kernel && !user_mode(regs))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int perf_swevent_match(struct perf_event *event,
+ enum perf_type_id type,
+ u32 event_id,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ if (event->attr.type != type)
+ return 0;
+
+ if (event->attr.config != event_id)
+ return 0;
+
+ if (perf_exclude_event(event, regs))
+ return 0;
+
+ return 1;
+}
+
+static inline u64 swevent_hash(u64 type, u32 event_id)
+{
+ u64 val = event_id | (type << 32);
+
+ return hash_64(val, SWEVENT_HLIST_BITS);
+}
+
+static inline struct hlist_head *
+__find_swevent_head(struct swevent_hlist *hlist, u64 type, u32 event_id)
+{
+ u64 hash = swevent_hash(type, event_id);
+
+ return &hlist->heads[hash];
+}
+
+/* For the read side: events when they trigger */
+static inline struct hlist_head *
+find_swevent_head_rcu(struct perf_cpu_context *ctx, u64 type, u32 event_id)
+{
+ struct swevent_hlist *hlist;
+
+ hlist = rcu_dereference(ctx->swevent_hlist);
+ if (!hlist)
+ return NULL;
+
+ return __find_swevent_head(hlist, type, event_id);
+}
+
+/* For the event head insertion and removal in the hlist */
+static inline struct hlist_head *
+find_swevent_head(struct perf_cpu_context *ctx, struct perf_event *event)
+{
+ struct swevent_hlist *hlist;
+ u32 event_id = event->attr.config;
+ u64 type = event->attr.type;
+
+ /*
+ * Event scheduling is always serialized against hlist allocation
+ * and release. Which makes the protected version suitable here.
+ * The context lock guarantees that.
+ */
+ hlist = rcu_dereference_protected(ctx->swevent_hlist,
+ lockdep_is_held(&event->ctx->lock));
+ if (!hlist)
+ return NULL;
+
+ return __find_swevent_head(hlist, type, event_id);
+}
+
+static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
+ u64 nr, int nmi,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct perf_cpu_context *cpuctx;
+ struct perf_event *event;
+ struct hlist_node *node;
+ struct hlist_head *head;
+
+ cpuctx = &__get_cpu_var(perf_cpu_context);
+
+ rcu_read_lock();
+
+ head = find_swevent_head_rcu(cpuctx, type, event_id);
+
+ if (!head)
+ goto end;
+
+ hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
+ if (perf_swevent_match(event, type, event_id, data, regs))
+ perf_swevent_add(event, nr, nmi, data, regs);
+ }
+end:
+ rcu_read_unlock();
+}
+
+int perf_swevent_get_recursion_context(void)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ int rctx;
+
+ if (in_nmi())
+ rctx = 3;
+ else if (in_irq())
+ rctx = 2;
+ else if (in_softirq())
+ rctx = 1;
+ else
+ rctx = 0;
+
+ if (cpuctx->recursion[rctx])
+ return -1;
+
+ cpuctx->recursion[rctx]++;
+ barrier();
+
+ return rctx;
+}
+EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context);
+
+void inline perf_swevent_put_recursion_context(int rctx)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ barrier();
+ cpuctx->recursion[rctx]--;
+}
+
+void __perf_sw_event(u32 event_id, u64 nr, int nmi,
+ struct pt_regs *regs, u64 addr)
+{
+ struct perf_sample_data data;
+ int rctx;
+
+ preempt_disable_notrace();
+ rctx = perf_swevent_get_recursion_context();
+ if (rctx < 0)
+ return;
+
+ perf_sample_data_init(&data, addr);
+
+ do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, nmi, &data, regs);
+
+ perf_swevent_put_recursion_context(rctx);
+ preempt_enable_notrace();
+}
+
+static void perf_swevent_read(struct perf_event *event)
+{
+}
+
+static int perf_swevent_enable(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct perf_cpu_context *cpuctx;
+ struct hlist_head *head;
+
+ cpuctx = &__get_cpu_var(perf_cpu_context);
+
+ if (hwc->sample_period) {
+ hwc->last_period = hwc->sample_period;
+ perf_swevent_set_period(event);
+ }
+
+ head = find_swevent_head(cpuctx, event);
+ if (WARN_ON_ONCE(!head))
+ return -EINVAL;
+
+ hlist_add_head_rcu(&event->hlist_entry, head);
+
+ return 0;
+}
+
+static void perf_swevent_disable(struct perf_event *event)
+{
+ hlist_del_rcu(&event->hlist_entry);
+}
+
+static void perf_swevent_void(struct perf_event *event)
+{
+}
+
+static int perf_swevent_int(struct perf_event *event)
+{
+ return 0;
+}
+
+static const struct pmu perf_ops_generic = {
+ .enable = perf_swevent_enable,
+ .disable = perf_swevent_disable,
+ .start = perf_swevent_int,
+ .stop = perf_swevent_void,
+ .read = perf_swevent_read,
+ .unthrottle = perf_swevent_void, /* hwc->interrupts already reset */
+};
+
+/*
+ * hrtimer based swevent callback
+ */
+
+static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
+{
+ enum hrtimer_restart ret = HRTIMER_RESTART;
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ struct perf_event *event;
+ u64 period;
+
+ event = container_of(hrtimer, struct perf_event, hw.hrtimer);
+ event->pmu->read(event);
+
+ perf_sample_data_init(&data, 0);
+ data.period = event->hw.last_period;
+ regs = get_irq_regs();
+
+ if (regs && !perf_exclude_event(event, regs)) {
+ if (!(event->attr.exclude_idle && current->pid == 0))
+ if (perf_event_overflow(event, 0, &data, regs))
+ ret = HRTIMER_NORESTART;
+ }
+
+ period = max_t(u64, 10000, event->hw.sample_period);
+ hrtimer_forward_now(hrtimer, ns_to_ktime(period));
+
+ return ret;
+}
+
+static void perf_swevent_start_hrtimer(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hwc->hrtimer.function = perf_swevent_hrtimer;
+ if (hwc->sample_period) {
+ u64 period;
+
+ if (hwc->remaining) {
+ if (hwc->remaining < 0)
+ period = 10000;
+ else
+ period = hwc->remaining;
+ hwc->remaining = 0;
+ } else {
+ period = max_t(u64, 10000, hwc->sample_period);
+ }
+ __hrtimer_start_range_ns(&hwc->hrtimer,
+ ns_to_ktime(period), 0,
+ HRTIMER_MODE_REL, 0);
+ }
+}
+
+static void perf_swevent_cancel_hrtimer(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (hwc->sample_period) {
+ ktime_t remaining = hrtimer_get_remaining(&hwc->hrtimer);
+ hwc->remaining = ktime_to_ns(remaining);
+
+ hrtimer_cancel(&hwc->hrtimer);
+ }
+}
+
+/*
+ * Software event: cpu wall time clock
+ */
+
+static void cpu_clock_perf_event_update(struct perf_event *event)
+{
+ int cpu = raw_smp_processor_id();
+ s64 prev;
+ u64 now;
+
+ now = cpu_clock(cpu);
+ prev = local64_xchg(&event->hw.prev_count, now);
+ local64_add(now - prev, &event->count);
+}
+
+static int cpu_clock_perf_event_enable(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int cpu = raw_smp_processor_id();
+
+ local64_set(&hwc->prev_count, cpu_clock(cpu));
+ perf_swevent_start_hrtimer(event);
+
+ return 0;
+}
+
+static void cpu_clock_perf_event_disable(struct perf_event *event)
+{
+ perf_swevent_cancel_hrtimer(event);
+ cpu_clock_perf_event_update(event);
+}
+
+static void cpu_clock_perf_event_read(struct perf_event *event)
+{
+ cpu_clock_perf_event_update(event);
+}
+
+static const struct pmu perf_ops_cpu_clock = {
+ .enable = cpu_clock_perf_event_enable,
+ .disable = cpu_clock_perf_event_disable,
+ .read = cpu_clock_perf_event_read,
+};
+
+/*
+ * Software event: task time clock
+ */
+
+static void task_clock_perf_event_update(struct perf_event *event, u64 now)
+{
+ u64 prev;
+ s64 delta;
+
+ prev = local64_xchg(&event->hw.prev_count, now);
+ delta = now - prev;
+ local64_add(delta, &event->count);
+}
+
+static int task_clock_perf_event_enable(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 now;
+
+ now = event->ctx->time;
+
+ local64_set(&hwc->prev_count, now);
+
+ perf_swevent_start_hrtimer(event);
+
+ return 0;
+}
+
+static void task_clock_perf_event_disable(struct perf_event *event)
+{
+ perf_swevent_cancel_hrtimer(event);
+ task_clock_perf_event_update(event, event->ctx->time);
+
+}
+
+static void task_clock_perf_event_read(struct perf_event *event)
+{
+ u64 time;
+
+ if (!in_nmi()) {
+ update_context_time(event->ctx);
+ time = event->ctx->time;
+ } else {
+ u64 now = perf_clock();
+ u64 delta = now - event->ctx->timestamp;
+ time = event->ctx->time + delta;
+ }
+
+ task_clock_perf_event_update(event, time);
+}
+
+static const struct pmu perf_ops_task_clock = {
+ .enable = task_clock_perf_event_enable,
+ .disable = task_clock_perf_event_disable,
+ .read = task_clock_perf_event_read,
+};
+
+/* Deref the hlist from the update side */
+static inline struct swevent_hlist *
+swevent_hlist_deref(struct perf_cpu_context *cpuctx)
+{
+ return rcu_dereference_protected(cpuctx->swevent_hlist,
+ lockdep_is_held(&cpuctx->hlist_mutex));
+}
+
+static void swevent_hlist_release_rcu(struct rcu_head *rcu_head)
+{
+ struct swevent_hlist *hlist;
+
+ hlist = container_of(rcu_head, struct swevent_hlist, rcu_head);
+ kfree(hlist);
+}
+
+static void swevent_hlist_release(struct perf_cpu_context *cpuctx)
+{
+ struct swevent_hlist *hlist = swevent_hlist_deref(cpuctx);
+
+ if (!hlist)
+ return;
+
+ rcu_assign_pointer(cpuctx->swevent_hlist, NULL);
+ call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu);
+}
+
+static void swevent_hlist_put_cpu(struct perf_event *event, int cpu)
+{
+ struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
+
+ mutex_lock(&cpuctx->hlist_mutex);
+
+ if (!--cpuctx->hlist_refcount)
+ swevent_hlist_release(cpuctx);
+
+ mutex_unlock(&cpuctx->hlist_mutex);
+}
+
+static void swevent_hlist_put(struct perf_event *event)
+{
+ int cpu;
+
+ if (event->cpu != -1) {
+ swevent_hlist_put_cpu(event, event->cpu);
+ return;
+ }
+
+ for_each_possible_cpu(cpu)
+ swevent_hlist_put_cpu(event, cpu);
+}
+
+static int swevent_hlist_get_cpu(struct perf_event *event, int cpu)
+{
+ struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
+ int err = 0;
+
+ mutex_lock(&cpuctx->hlist_mutex);
+
+ if (!swevent_hlist_deref(cpuctx) && cpu_online(cpu)) {
+ struct swevent_hlist *hlist;
+
+ hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
+ if (!hlist) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
+ }
+ cpuctx->hlist_refcount++;
+ exit:
+ mutex_unlock(&cpuctx->hlist_mutex);
+
+ return err;
+}
+
+static int swevent_hlist_get(struct perf_event *event)
+{
+ int err;
+ int cpu, failed_cpu;
+
+ if (event->cpu != -1)
+ return swevent_hlist_get_cpu(event, event->cpu);
+
+ get_online_cpus();
+ for_each_possible_cpu(cpu) {
+ err = swevent_hlist_get_cpu(event, cpu);
+ if (err) {
+ failed_cpu = cpu;
+ goto fail;
+ }
+ }
+ put_online_cpus();
+
+ return 0;
+ fail:
+ for_each_possible_cpu(cpu) {
+ if (cpu == failed_cpu)
+ break;
+ swevent_hlist_put_cpu(event, cpu);
+ }
+
+ put_online_cpus();
+ return err;
+}
+
+#ifdef CONFIG_EVENT_TRACING
+
+static const struct pmu perf_ops_tracepoint = {
+ .enable = perf_trace_enable,
+ .disable = perf_trace_disable,
+ .start = perf_swevent_int,
+ .stop = perf_swevent_void,
+ .read = perf_swevent_read,
+ .unthrottle = perf_swevent_void,
+};
+
+static int perf_tp_filter_match(struct perf_event *event,
+ struct perf_sample_data *data)
+{
+ void *record = data->raw->data;
+
+ if (likely(!event->filter) || filter_match_preds(event->filter, record))
+ return 1;
+ return 0;
+}
+
+static int perf_tp_event_match(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ /*
+ * All tracepoints are from kernel-space.
+ */
+ if (event->attr.exclude_kernel)
+ return 0;
+
+ if (!perf_tp_filter_match(event, data))
+ return 0;
+
+ return 1;
+}
+
+void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
+ struct pt_regs *regs, struct hlist_head *head, int rctx)
+{
+ struct perf_sample_data data;
+ struct perf_event *event;
+ struct hlist_node *node;
+
+ struct perf_raw_record raw = {
+ .size = entry_size,
+ .data = record,
+ };
+
+ perf_sample_data_init(&data, addr);
+ data.raw = &raw;
+
+ hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
+ if (perf_tp_event_match(event, &data, regs))
+ perf_swevent_add(event, count, 1, &data, regs);
+ }
+
+ perf_swevent_put_recursion_context(rctx);
+}
+EXPORT_SYMBOL_GPL(perf_tp_event);
+
+static void tp_perf_event_destroy(struct perf_event *event)
+{
+ perf_trace_destroy(event);
+}
+
+static const struct pmu *tp_perf_event_init(struct perf_event *event)
+{
+ int err;
+
+ /*
+ * Raw tracepoint data is a severe data leak, only allow root to
+ * have these.
+ */
+ if ((event->attr.sample_type & PERF_SAMPLE_RAW) &&
+ perf_paranoid_tracepoint_raw() &&
+ !capable(CAP_SYS_ADMIN))
+ return ERR_PTR(-EPERM);
+
+ err = perf_trace_init(event);
+ if (err)
+ return NULL;
+
+ event->destroy = tp_perf_event_destroy;
+
+ return &perf_ops_tracepoint;
+}
+
+static int perf_event_set_filter(struct perf_event *event, void __user *arg)
+{
+ char *filter_str;
+ int ret;
+
+ if (event->attr.type != PERF_TYPE_TRACEPOINT)
+ return -EINVAL;
+
+ filter_str = strndup_user(arg, PAGE_SIZE);
+ if (IS_ERR(filter_str))
+ return PTR_ERR(filter_str);
+
+ ret = ftrace_profile_set_filter(event, event->attr.config, filter_str);
+
+ kfree(filter_str);
+ return ret;
+}
+
+static void perf_event_free_filter(struct perf_event *event)
+{
+ ftrace_profile_free_filter(event);
+}
+
+#else
+
+static const struct pmu *tp_perf_event_init(struct perf_event *event)
+{
+ return NULL;
+}
+
+static int perf_event_set_filter(struct perf_event *event, void __user *arg)
+{
+ return -ENOENT;
+}
+
+static void perf_event_free_filter(struct perf_event *event)
+{
+}
+
+#endif /* CONFIG_EVENT_TRACING */
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+static void bp_perf_event_destroy(struct perf_event *event)
+{
+ release_bp_slot(event);
+}
+
+static const struct pmu *bp_perf_event_init(struct perf_event *bp)
+{
+ int err;
+
+ err = register_perf_hw_breakpoint(bp);
+ if (err)
+ return ERR_PTR(err);
+
+ bp->destroy = bp_perf_event_destroy;
+
+ return &perf_ops_bp;
+}
+
+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);
+
+ if (!perf_exclude_event(bp, regs))
+ perf_swevent_add(bp, 1, 1, &sample, regs);
+}
+#else
+static const struct pmu *bp_perf_event_init(struct perf_event *bp)
+{
+ return NULL;
+}
+
+void perf_bp_event(struct perf_event *bp, void *regs)
+{
+}
+#endif
+
+atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX];
+
+static void sw_perf_event_destroy(struct perf_event *event)
+{
+ u64 event_id = event->attr.config;
+
+ WARN_ON(event->parent);
+
+ atomic_dec(&perf_swevent_enabled[event_id]);
+ swevent_hlist_put(event);
+}
+
+static const struct pmu *sw_perf_event_init(struct perf_event *event)
+{
+ const struct pmu *pmu = NULL;
+ u64 event_id = event->attr.config;
+
+ /*
+ * Software events (currently) can't in general distinguish
+ * between user, kernel and hypervisor events.
+ * However, context switches and cpu migrations are considered
+ * to be kernel events, and page faults are never hypervisor
+ * events.
+ */
+ switch (event_id) {
+ case PERF_COUNT_SW_CPU_CLOCK:
+ pmu = &perf_ops_cpu_clock;
+
+ break;
+ case PERF_COUNT_SW_TASK_CLOCK:
+ /*
+ * If the user instantiates this as a per-cpu event,
+ * use the cpu_clock event instead.
+ */
+ if (event->ctx->task)
+ pmu = &perf_ops_task_clock;
+ else
+ pmu = &perf_ops_cpu_clock;
+
+ break;
+ case PERF_COUNT_SW_PAGE_FAULTS:
+ case PERF_COUNT_SW_PAGE_FAULTS_MIN:
+ case PERF_COUNT_SW_PAGE_FAULTS_MAJ:
+ case PERF_COUNT_SW_CONTEXT_SWITCHES:
+ case PERF_COUNT_SW_CPU_MIGRATIONS:
+ case PERF_COUNT_SW_ALIGNMENT_FAULTS:
+ case PERF_COUNT_SW_EMULATION_FAULTS:
+ if (!event->parent) {
+ int err;
+
+ err = swevent_hlist_get(event);
+ if (err)
+ return ERR_PTR(err);
+
+ atomic_inc(&perf_swevent_enabled[event_id]);
+ event->destroy = sw_perf_event_destroy;
+ }
+ pmu = &perf_ops_generic;
+ break;
+ }
+
+ return pmu;
+}
+
+/*
+ * Allocate and initialize a event structure
+ */
+static struct perf_event *
+perf_event_alloc(struct perf_event_attr *attr,
+ int cpu,
+ struct perf_event_context *ctx,
+ struct perf_event *group_leader,
+ struct perf_event *parent_event,
+ perf_overflow_handler_t overflow_handler,
+ gfp_t gfpflags)
+{
+ const struct pmu *pmu;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ long err;
+
+ event = kzalloc(sizeof(*event), gfpflags);
+ if (!event)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * Single events are their own group leaders, with an
+ * empty sibling list:
+ */
+ if (!group_leader)
+ group_leader = event;
+
+ mutex_init(&event->child_mutex);
+ INIT_LIST_HEAD(&event->child_list);
+
+ INIT_LIST_HEAD(&event->group_entry);
+ INIT_LIST_HEAD(&event->event_entry);
+ INIT_LIST_HEAD(&event->sibling_list);
+ init_waitqueue_head(&event->waitq);
+
+ mutex_init(&event->mmap_mutex);
+
+ event->cpu = cpu;
+ event->attr = *attr;
+ event->group_leader = group_leader;
+ event->pmu = NULL;
+ event->ctx = ctx;
+ event->oncpu = -1;
+
+ event->parent = parent_event;
+
+ event->ns = get_pid_ns(current->nsproxy->pid_ns);
+ event->id = atomic64_inc_return(&perf_event_id);
+
+ event->state = PERF_EVENT_STATE_INACTIVE;
+
+ if (!overflow_handler && parent_event)
+ overflow_handler = parent_event->overflow_handler;
+
+ event->overflow_handler = overflow_handler;
+
+ if (attr->disabled)
+ event->state = PERF_EVENT_STATE_OFF;
+
+ pmu = NULL;
+
+ hwc = &event->hw;
+ hwc->sample_period = attr->sample_period;
+ if (attr->freq && attr->sample_freq)
+ hwc->sample_period = 1;
+ hwc->last_period = hwc->sample_period;
+
+ local64_set(&hwc->period_left, hwc->sample_period);
+
+ /*
+ * we currently do not support PERF_FORMAT_GROUP on inherited events
+ */
+ if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
+ goto done;
+
+ switch (attr->type) {
+ case PERF_TYPE_RAW:
+ case PERF_TYPE_HARDWARE:
+ case PERF_TYPE_HW_CACHE:
+ pmu = hw_perf_event_init(event);
+ break;
+
+ case PERF_TYPE_SOFTWARE:
+ pmu = sw_perf_event_init(event);
+ break;
+
+ case PERF_TYPE_TRACEPOINT:
+ pmu = tp_perf_event_init(event);
+ break;
+
+ case PERF_TYPE_BREAKPOINT:
+ pmu = bp_perf_event_init(event);
+ break;
+
+
+ default:
+ break;
+ }
+done:
+ err = 0;
+ if (!pmu)
+ err = -EINVAL;
+ else if (IS_ERR(pmu))
+ err = PTR_ERR(pmu);
+
+ if (err) {
+ if (event->ns)
+ put_pid_ns(event->ns);
+ kfree(event);
+ return ERR_PTR(err);
+ }
+
+ event->pmu = pmu;
+
+ if (!event->parent) {
+ atomic_inc(&nr_events);
+ if (event->attr.mmap || event->attr.mmap_data)
+ atomic_inc(&nr_mmap_events);
+ if (event->attr.comm)
+ atomic_inc(&nr_comm_events);
+ if (event->attr.task)
+ atomic_inc(&nr_task_events);
+ }
+
+ return event;
+}
+
+static int perf_copy_attr(struct perf_event_attr __user *uattr,
+ struct perf_event_attr *attr)
+{
+ u32 size;
+ int ret;
+
+ if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0))
+ return -EFAULT;
+
+ /*
+ * zero the full structure, so that a short copy will be nice.
+ */
+ memset(attr, 0, sizeof(*attr));
+
+ ret = get_user(size, &uattr->size);
+ if (ret)
+ return ret;
+
+ if (size > PAGE_SIZE) /* silly large */
+ goto err_size;
+
+ if (!size) /* abi compat */
+ size = PERF_ATTR_SIZE_VER0;
+
+ if (size < PERF_ATTR_SIZE_VER0)
+ goto err_size;
+
+ /*
+ * If we're handed a bigger struct than we know of,
+ * ensure all the unknown bits are 0 - i.e. new
+ * user-space does not rely on any kernel feature
+ * extensions we dont know about yet.
+ */
+ if (size > sizeof(*attr)) {
+ unsigned char __user *addr;
+ unsigned char __user *end;
+ unsigned char val;
+
+ addr = (void __user *)uattr + sizeof(*attr);
+ end = (void __user *)uattr + size;
+
+ for (; addr < end; addr++) {
+ ret = get_user(val, addr);
+ if (ret)
+ return ret;
+ if (val)
+ goto err_size;
+ }
+ size = sizeof(*attr);
+ }
+
+ ret = copy_from_user(attr, uattr, size);
+ if (ret)
+ return -EFAULT;
+
+ /*
+ * If the type exists, the corresponding creation will verify
+ * the attr->config.
+ */
+ if (attr->type >= PERF_TYPE_MAX)
+ return -EINVAL;
+
+ if (attr->__reserved_1)
+ return -EINVAL;
+
+ if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
+ return -EINVAL;
+
+ if (attr->read_format & ~(PERF_FORMAT_MAX-1))
+ return -EINVAL;
+
+out:
+ return ret;
+
+err_size:
+ put_user(sizeof(*attr), &uattr->size);
+ ret = -E2BIG;
+ goto out;
+}
+
+static int
+perf_event_set_output(struct perf_event *event, struct perf_event *output_event)
+{
+ struct perf_buffer *buffer = NULL, *old_buffer = NULL;
+ int ret = -EINVAL;
+
+ if (!output_event)
+ goto set;
+
+ /* don't allow circular references */
+ if (event == output_event)
+ goto out;
+
+ /*
+ * Don't allow cross-cpu buffers
+ */
+ if (output_event->cpu != event->cpu)
+ goto out;
+
+ /*
+ * If its not a per-cpu buffer, it must be the same task.
+ */
+ if (output_event->cpu == -1 && output_event->ctx != event->ctx)
+ goto out;
+
+set:
+ mutex_lock(&event->mmap_mutex);
+ /* Can't redirect output if we've got an active mmap() */
+ if (atomic_read(&event->mmap_count))
+ goto unlock;
+
+ if (output_event) {
+ /* get the buffer we want to redirect to */
+ buffer = perf_buffer_get(output_event);
+ if (!buffer)
+ goto unlock;
+ }
+
+ old_buffer = event->buffer;
+ rcu_assign_pointer(event->buffer, buffer);
+ ret = 0;
+unlock:
+ mutex_unlock(&event->mmap_mutex);
+
+ if (old_buffer)
+ perf_buffer_put(old_buffer);
+out:
+ return ret;
+}
+
+/**
+ * sys_perf_event_open - open a performance event, associate it to a task/cpu
+ *
+ * @attr_uptr: event_id type attributes for monitoring/sampling
+ * @pid: target pid
+ * @cpu: target cpu
+ * @group_fd: group leader event fd
+ */
+SYSCALL_DEFINE5(perf_event_open,
+ struct perf_event_attr __user *, attr_uptr,
+ pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
+{
+ struct perf_event *event, *group_leader = NULL, *output_event = NULL;
+ struct perf_event_attr attr;
+ struct perf_event_context *ctx;
+ struct file *event_file = NULL;
+ struct file *group_file = NULL;
+ int event_fd;
+ int fput_needed = 0;
+ int err;
+
+ /* for future expandability... */
+ if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
+ return -EINVAL;
+
+ err = perf_copy_attr(attr_uptr, &attr);
+ if (err)
+ return err;
+
+ if (!attr.exclude_kernel) {
+ if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ }
+
+ if (attr.freq) {
+ if (attr.sample_freq > sysctl_perf_event_sample_rate)
+ return -EINVAL;
+ }
+
+ event_fd = get_unused_fd_flags(O_RDWR);
+ if (event_fd < 0)
+ return event_fd;
+
+ /*
+ * Get the target context (task or percpu):
+ */
+ ctx = find_get_context(pid, cpu);
+ if (IS_ERR(ctx)) {
+ err = PTR_ERR(ctx);
+ goto err_fd;
+ }
+
+ if (group_fd != -1) {
+ group_leader = perf_fget_light(group_fd, &fput_needed);
+ if (IS_ERR(group_leader)) {
+ err = PTR_ERR(group_leader);
+ goto err_put_context;
+ }
+ group_file = group_leader->filp;
+ if (flags & PERF_FLAG_FD_OUTPUT)
+ output_event = group_leader;
+ if (flags & PERF_FLAG_FD_NO_GROUP)
+ group_leader = NULL;
+ }
+
+ /*
+ * Look up the group leader (we will attach this event to it):
+ */
+ if (group_leader) {
+ err = -EINVAL;
+
+ /*
+ * Do not allow a recursive hierarchy (this new sibling
+ * becoming part of another group-sibling):
+ */
+ if (group_leader->group_leader != group_leader)
+ goto err_put_context;
+ /*
+ * Do not allow to attach to a group in a different
+ * task or CPU context:
+ */
+ if (group_leader->ctx != ctx)
+ goto err_put_context;
+ /*
+ * Only a group leader can be exclusive or pinned
+ */
+ if (attr.exclusive || attr.pinned)
+ goto err_put_context;
+ }
+
+ event = perf_event_alloc(&attr, cpu, ctx, group_leader,
+ NULL, NULL, GFP_KERNEL);
+ if (IS_ERR(event)) {
+ err = PTR_ERR(event);
+ goto err_put_context;
+ }
+
+ if (output_event) {
+ err = perf_event_set_output(event, output_event);
+ if (err)
+ goto err_free_put_context;
+ }
+
+ event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR);
+ if (IS_ERR(event_file)) {
+ err = PTR_ERR(event_file);
+ goto err_free_put_context;
+ }
+
+ event->filp = event_file;
+ WARN_ON_ONCE(ctx->parent_ctx);
+ mutex_lock(&ctx->mutex);
+ perf_install_in_context(ctx, event, cpu);
+ ++ctx->generation;
+ mutex_unlock(&ctx->mutex);
+
+ event->owner = current;
+ get_task_struct(current);
+ mutex_lock(¤t->perf_event_mutex);
+ list_add_tail(&event->owner_entry, ¤t->perf_event_list);
+ mutex_unlock(¤t->perf_event_mutex);
+
+ /*
+ * Drop the reference on the group_event after placing the
+ * new event on the sibling_list. This ensures destruction
+ * of the group leader will find the pointer to itself in
+ * perf_group_detach().
+ */
+ fput_light(group_file, fput_needed);
+ fd_install(event_fd, event_file);
+ return event_fd;
+
+err_free_put_context:
+ free_event(event);
+err_put_context:
+ fput_light(group_file, fput_needed);
+ put_ctx(ctx);
+err_fd:
+ put_unused_fd(event_fd);
+ return err;
+}
+
+/**
+ * perf_event_create_kernel_counter
+ *
+ * @attr: attributes of the counter to create
+ * @cpu: cpu in which the counter is bound
+ * @pid: task to profile
+ */
+struct perf_event *
+perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
+ pid_t pid,
+ perf_overflow_handler_t overflow_handler)
+{
+ struct perf_event *event;
+ struct perf_event_context *ctx;
+ int err;
+
+ /*
+ * Get the target context (task or percpu):
+ */
+
+ ctx = find_get_context(pid, cpu);
+ if (IS_ERR(ctx)) {
+ err = PTR_ERR(ctx);
+ goto err_exit;
+ }
+
+ event = perf_event_alloc(attr, cpu, ctx, NULL,
+ NULL, overflow_handler, GFP_KERNEL);
+ if (IS_ERR(event)) {
+ err = PTR_ERR(event);
+ goto err_put_context;
+ }
+
+ event->filp = NULL;
+ WARN_ON_ONCE(ctx->parent_ctx);
+ mutex_lock(&ctx->mutex);
+ perf_install_in_context(ctx, event, cpu);
+ ++ctx->generation;
+ mutex_unlock(&ctx->mutex);
+
+ event->owner = current;
+ get_task_struct(current);
+ mutex_lock(¤t->perf_event_mutex);
+ list_add_tail(&event->owner_entry, ¤t->perf_event_list);
+ mutex_unlock(¤t->perf_event_mutex);
+
+ return event;
+
+ err_put_context:
+ put_ctx(ctx);
+ err_exit:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);
+
+/*
+ * inherit a event from parent task to child task:
+ */
+static struct perf_event *
+inherit_event(struct perf_event *parent_event,
+ struct task_struct *parent,
+ struct perf_event_context *parent_ctx,
+ struct task_struct *child,
+ struct perf_event *group_leader,
+ struct perf_event_context *child_ctx)
+{
+ struct perf_event *child_event;
+
+ /*
+ * Instead of creating recursive hierarchies of events,
+ * we link inherited events back to the original parent,
+ * which has a filp for sure, which we use as the reference
+ * count:
+ */
+ if (parent_event->parent)
+ parent_event = parent_event->parent;
+
+ child_event = perf_event_alloc(&parent_event->attr,
+ parent_event->cpu, child_ctx,
+ group_leader, parent_event,
+ NULL, GFP_KERNEL);
+ if (IS_ERR(child_event))
+ return child_event;
+ get_ctx(child_ctx);
+
+ /*
+ * Make the child state follow the state of the parent event,
+ * not its attr.disabled bit. We hold the parent's mutex,
+ * so we won't race with perf_event_{en, dis}able_family.
+ */
+ if (parent_event->state >= PERF_EVENT_STATE_INACTIVE)
+ child_event->state = PERF_EVENT_STATE_INACTIVE;
+ else
+ child_event->state = PERF_EVENT_STATE_OFF;
+
+ if (parent_event->attr.freq) {
+ u64 sample_period = parent_event->hw.sample_period;
+ struct hw_perf_event *hwc = &child_event->hw;
+
+ hwc->sample_period = sample_period;
+ hwc->last_period = sample_period;
+
+ local64_set(&hwc->period_left, sample_period);
+ }
+
+ child_event->overflow_handler = parent_event->overflow_handler;
+
+ /*
+ * Link it up in the child's context:
+ */
+ add_event_to_ctx(child_event, child_ctx);
+
+ /*
+ * Get a reference to the parent filp - we will fput it
+ * when the child event exits. This is safe to do because
+ * we are in the parent and we know that the filp still
+ * exists and has a nonzero count:
+ */
+ atomic_long_inc(&parent_event->filp->f_count);
+
+ /*
+ * Link this into the parent event's child list
+ */
+ WARN_ON_ONCE(parent_event->ctx->parent_ctx);
+ mutex_lock(&parent_event->child_mutex);
+ list_add_tail(&child_event->child_list, &parent_event->child_list);
+ mutex_unlock(&parent_event->child_mutex);
+
+ return child_event;
+}
+
+static int inherit_group(struct perf_event *parent_event,
+ struct task_struct *parent,
+ struct perf_event_context *parent_ctx,
+ struct task_struct *child,
+ struct perf_event_context *child_ctx)
+{
+ struct perf_event *leader;
+ struct perf_event *sub;
+ struct perf_event *child_ctr;
+
+ leader = inherit_event(parent_event, parent, parent_ctx,
+ child, NULL, child_ctx);
+ if (IS_ERR(leader))
+ return PTR_ERR(leader);
+ list_for_each_entry(sub, &parent_event->sibling_list, group_entry) {
+ child_ctr = inherit_event(sub, parent, parent_ctx,
+ child, leader, child_ctx);
+ if (IS_ERR(child_ctr))
+ return PTR_ERR(child_ctr);
+ }
+ return 0;
+}
+
+static void sync_child_event(struct perf_event *child_event,
+ struct task_struct *child)
+{
+ struct perf_event *parent_event = child_event->parent;
+ u64 child_val;
+
+ if (child_event->attr.inherit_stat)
+ perf_event_read_event(child_event, child);
+
+ child_val = perf_event_count(child_event);
+
+ /*
+ * Add back the child's count to the parent's count:
+ */
+ atomic64_add(child_val, &parent_event->child_count);
+ atomic64_add(child_event->total_time_enabled,
+ &parent_event->child_total_time_enabled);
+ atomic64_add(child_event->total_time_running,
+ &parent_event->child_total_time_running);
+
+ /*
+ * Remove this event from the parent's list
+ */
+ WARN_ON_ONCE(parent_event->ctx->parent_ctx);
+ mutex_lock(&parent_event->child_mutex);
+ list_del_init(&child_event->child_list);
+ mutex_unlock(&parent_event->child_mutex);
+
+ /*
+ * Release the parent event, if this was the last
+ * reference to it.
+ */
+ fput(parent_event->filp);
+}
+
+static void
+__perf_event_exit_task(struct perf_event *child_event,
+ struct perf_event_context *child_ctx,
+ struct task_struct *child)
+{
+ struct perf_event *parent_event;
+
+ perf_event_remove_from_context(child_event);
+
+ parent_event = child_event->parent;
+ /*
+ * It can happen that parent exits first, and has events
+ * that are still around due to the child reference. These
+ * events need to be zapped - but otherwise linger.
+ */
+ if (parent_event) {
+ sync_child_event(child_event, child);
+ free_event(child_event);
+ }
+}
+
+/*
+ * When a child task exits, feed back event values to parent events.
+ */
+void perf_event_exit_task(struct task_struct *child)
+{
+ struct perf_event *child_event, *tmp;
+ struct perf_event_context *child_ctx;
+ unsigned long flags;
+
+ if (likely(!child->perf_event_ctxp)) {
+ perf_event_task(child, NULL, 0);
+ return;
+ }
+
+ local_irq_save(flags);
+ /*
+ * We can't reschedule here because interrupts are disabled,
+ * and either child is current or it is a task that can't be
+ * scheduled, so we are now safe from rescheduling changing
+ * our context.
+ */
+ child_ctx = child->perf_event_ctxp;
+ __perf_event_task_sched_out(child_ctx);
+
+ /*
+ * Take the context lock here so that if find_get_context is
+ * reading child->perf_event_ctxp, we wait until it has
+ * incremented the context's refcount before we do put_ctx below.
+ */
+ raw_spin_lock(&child_ctx->lock);
+ child->perf_event_ctxp = NULL;
+ /*
+ * If this context is a clone; unclone it so it can't get
+ * swapped to another process while we're removing all
+ * the events from it.
+ */
+ unclone_ctx(child_ctx);
+ update_context_time(child_ctx);
+ raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
+
+ /*
+ * Report the task dead after unscheduling the events so that we
+ * won't get any samples after PERF_RECORD_EXIT. We can however still
+ * get a few PERF_RECORD_READ events.
+ */
+ perf_event_task(child, child_ctx, 0);
+
+ /*
+ * We can recurse on the same lock type through:
+ *
+ * __perf_event_exit_task()
+ * sync_child_event()
+ * fput(parent_event->filp)
+ * perf_release()
+ * mutex_lock(&ctx->mutex)
+ *
+ * But since its the parent context it won't be the same instance.
+ */
+ mutex_lock(&child_ctx->mutex);
+
+again:
+ list_for_each_entry_safe(child_event, tmp, &child_ctx->pinned_groups,
+ group_entry)
+ __perf_event_exit_task(child_event, child_ctx, child);
+
+ list_for_each_entry_safe(child_event, tmp, &child_ctx->flexible_groups,
+ group_entry)
+ __perf_event_exit_task(child_event, child_ctx, child);
+
+ /*
+ * If the last event was a group event, it will have appended all
+ * its siblings to the list, but we obtained 'tmp' before that which
+ * will still point to the list head terminating the iteration.
+ */
+ if (!list_empty(&child_ctx->pinned_groups) ||
+ !list_empty(&child_ctx->flexible_groups))
+ goto again;
+
+ mutex_unlock(&child_ctx->mutex);
+
+ put_ctx(child_ctx);
+}
+
+static void perf_free_event(struct perf_event *event,
+ struct perf_event_context *ctx)
+{
+ struct perf_event *parent = event->parent;
+
+ if (WARN_ON_ONCE(!parent))
+ return;
+
+ mutex_lock(&parent->child_mutex);
+ list_del_init(&event->child_list);
+ mutex_unlock(&parent->child_mutex);
+
+ fput(parent->filp);
+
+ perf_group_detach(event);
+ list_del_event(event, ctx);
+ free_event(event);
+}
+
+/*
+ * free an unexposed, unused context as created by inheritance by
+ * init_task below, used by fork() in case of fail.
+ */
+void perf_event_free_task(struct task_struct *task)
+{
+ struct perf_event_context *ctx = task->perf_event_ctxp;
+ struct perf_event *event, *tmp;
+
+ if (!ctx)
+ return;
+
+ mutex_lock(&ctx->mutex);
+again:
+ list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry)
+ perf_free_event(event, ctx);
+
+ list_for_each_entry_safe(event, tmp, &ctx->flexible_groups,
+ group_entry)
+ perf_free_event(event, ctx);
+
+ if (!list_empty(&ctx->pinned_groups) ||
+ !list_empty(&ctx->flexible_groups))
+ goto again;
+
+ mutex_unlock(&ctx->mutex);
+
+ put_ctx(ctx);
+}
+
+static int
+inherit_task_group(struct perf_event *event, struct task_struct *parent,
+ struct perf_event_context *parent_ctx,
+ struct task_struct *child,
+ int *inherited_all)
+{
+ int ret;
+ struct perf_event_context *child_ctx = child->perf_event_ctxp;
+
+ if (!event->attr.inherit) {
+ *inherited_all = 0;
+ return 0;
+ }
+
+ if (!child_ctx) {
+ /*
+ * This is executed from the parent task context, so
+ * inherit events that have been marked for cloning.
+ * First allocate and initialize a context for the
+ * child.
+ */
+
+ child_ctx = kzalloc(sizeof(struct perf_event_context),
+ GFP_KERNEL);
+ if (!child_ctx)
+ return -ENOMEM;
+
+ __perf_event_init_context(child_ctx, child);
+ child->perf_event_ctxp = child_ctx;
+ get_task_struct(child);
+ }
+
+ ret = inherit_group(event, parent, parent_ctx,
+ child, child_ctx);
+
+ if (ret)
+ *inherited_all = 0;
+
+ return ret;
+}
+
+
+/*
+ * Initialize the perf_event context in task_struct
+ */
+int perf_event_init_task(struct task_struct *child)
+{
+ struct perf_event_context *child_ctx, *parent_ctx;
+ struct perf_event_context *cloned_ctx;
+ struct perf_event *event;
+ struct task_struct *parent = current;
+ int inherited_all = 1;
+ int ret = 0;
+
+ child->perf_event_ctxp = NULL;
+
+ mutex_init(&child->perf_event_mutex);
+ INIT_LIST_HEAD(&child->perf_event_list);
+
+ if (likely(!parent->perf_event_ctxp))
+ return 0;
+
+ /*
+ * If the parent's context is a clone, pin it so it won't get
+ * swapped under us.
+ */
+ parent_ctx = perf_pin_task_context(parent);
+
+ /*
+ * No need to check if parent_ctx != NULL here; since we saw
+ * it non-NULL earlier, the only reason for it to become NULL
+ * is if we exit, and since we're currently in the middle of
+ * a fork we can't be exiting at the same time.
+ */
+
+ /*
+ * Lock the parent list. No need to lock the child - not PID
+ * hashed yet and not running, so nobody can access it.
+ */
+ mutex_lock(&parent_ctx->mutex);
+
+ /*
+ * We dont have to disable NMIs - we are only looking at
+ * the list, not manipulating it:
+ */
+ list_for_each_entry(event, &parent_ctx->pinned_groups, group_entry) {
+ ret = inherit_task_group(event, parent, parent_ctx, child,
+ &inherited_all);
+ if (ret)
+ break;
+ }
+
+ list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) {
+ ret = inherit_task_group(event, parent, parent_ctx, child,
+ &inherited_all);
+ if (ret)
+ break;
+ }
+
+ child_ctx = child->perf_event_ctxp;
+
+ if (child_ctx && inherited_all) {
+ /*
+ * Mark the child context as a clone of the parent
+ * context, or of whatever the parent is a clone of.
+ * Note that if the parent is a clone, it could get
+ * uncloned at any point, but that doesn't matter
+ * because the list of events and the generation
+ * count can't have changed since we took the mutex.
+ */
+ cloned_ctx = rcu_dereference(parent_ctx->parent_ctx);
+ if (cloned_ctx) {
+ child_ctx->parent_ctx = cloned_ctx;
+ child_ctx->parent_gen = parent_ctx->parent_gen;
+ } else {
+ child_ctx->parent_ctx = parent_ctx;
+ child_ctx->parent_gen = parent_ctx->generation;
+ }
+ get_ctx(child_ctx->parent_ctx);
+ }
+
+ mutex_unlock(&parent_ctx->mutex);
+
+ perf_unpin_context(parent_ctx);
+
+ return ret;
+}
+
+static void __init perf_event_init_all_cpus(void)
+{
+ int cpu;
+ struct perf_cpu_context *cpuctx;
+
+ for_each_possible_cpu(cpu) {
+ cpuctx = &per_cpu(perf_cpu_context, cpu);
+ mutex_init(&cpuctx->hlist_mutex);
+ __perf_event_init_context(&cpuctx->ctx, NULL);
+ }
+}
+
+static void __cpuinit perf_event_init_cpu(int cpu)
+{
+ struct perf_cpu_context *cpuctx;
+
+ cpuctx = &per_cpu(perf_cpu_context, cpu);
+
+ spin_lock(&perf_resource_lock);
+ cpuctx->max_pertask = perf_max_events - perf_reserved_percpu;
+ spin_unlock(&perf_resource_lock);
+
+ mutex_lock(&cpuctx->hlist_mutex);
+ if (cpuctx->hlist_refcount > 0) {
+ struct swevent_hlist *hlist;
+
+ hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
+ WARN_ON_ONCE(!hlist);
+ rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
+ }
+ mutex_unlock(&cpuctx->hlist_mutex);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void __perf_event_exit_cpu(void *info)
+{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
+ struct perf_event_context *ctx = &cpuctx->ctx;
+ struct perf_event *event, *tmp;
+
+ list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry)
+ __perf_event_remove_from_context(event);
+ list_for_each_entry_safe(event, tmp, &ctx->flexible_groups, group_entry)
+ __perf_event_remove_from_context(event);
+}
+static void perf_event_exit_cpu(int cpu)
+{
+ struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
+ struct perf_event_context *ctx = &cpuctx->ctx;
+
+ mutex_lock(&cpuctx->hlist_mutex);
+ swevent_hlist_release(cpuctx);
+ mutex_unlock(&cpuctx->hlist_mutex);
+
+ mutex_lock(&ctx->mutex);
+ smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1);
+ mutex_unlock(&ctx->mutex);
+}
+#else
+static inline void perf_event_exit_cpu(int cpu) { }
+#endif
+
+static int __cpuinit
+perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (long)hcpu;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+
+ case CPU_UP_PREPARE:
+ case CPU_DOWN_FAILED:
+ perf_event_init_cpu(cpu);
+ break;
+
+ case CPU_UP_CANCELED:
+ case CPU_DOWN_PREPARE:
+ perf_event_exit_cpu(cpu);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+/*
+ * This has to have a higher priority than migration_notifier in sched.c.
+ */
+static struct notifier_block __cpuinitdata perf_cpu_nb = {
+ .notifier_call = perf_cpu_notify,
+ .priority = 20,
+};
+
+void __init perf_event_init(void)
+{
+ perf_event_init_all_cpus();
+ perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_UP_PREPARE,
+ (void *)(long)smp_processor_id());
+ perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_ONLINE,
+ (void *)(long)smp_processor_id());
+ register_cpu_notifier(&perf_cpu_nb);
+}
+
+static ssize_t perf_show_reserve_percpu(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", perf_reserved_percpu);
+}
+
+static ssize_t
+perf_set_reserve_percpu(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct perf_cpu_context *cpuctx;
+ unsigned long val;
+ int err, cpu, mpt;
+
+ err = strict_strtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > perf_max_events)
+ return -EINVAL;
+
+ spin_lock(&perf_resource_lock);
+ perf_reserved_percpu = val;
+ for_each_online_cpu(cpu) {
+ cpuctx = &per_cpu(perf_cpu_context, cpu);
+ raw_spin_lock_irq(&cpuctx->ctx.lock);
+ mpt = min(perf_max_events - cpuctx->ctx.nr_events,
+ perf_max_events - perf_reserved_percpu);
+ cpuctx->max_pertask = mpt;
+ raw_spin_unlock_irq(&cpuctx->ctx.lock);
+ }
+ spin_unlock(&perf_resource_lock);
+
+ return count;
+}
+
+static ssize_t perf_show_overcommit(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", perf_overcommit);
+}
+
+static ssize_t
+perf_set_overcommit(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int err;
+
+ err = strict_strtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ spin_lock(&perf_resource_lock);
+ perf_overcommit = val;
+ spin_unlock(&perf_resource_lock);
+
+ return count;
+}
+
+static SYSDEV_CLASS_ATTR(
+ reserve_percpu,
+ 0644,
+ perf_show_reserve_percpu,
+ perf_set_reserve_percpu
+ );
+
+static SYSDEV_CLASS_ATTR(
+ overcommit,
+ 0644,
+ perf_show_overcommit,
+ perf_set_overcommit
+ );
+
+static struct attribute *perfclass_attrs[] = {
+ &attr_reserve_percpu.attr,
+ &attr_overcommit.attr,
+ NULL
+};
+
+static struct attribute_group perfclass_attr_group = {
+ .attrs = perfclass_attrs,
+ .name = "perf_events",
+};
+
+static int __init perf_event_sysfs_init(void)
+{
+ return sysfs_create_group(&cpu_sysdev_class.kset.kobj,
+ &perfclass_attr_group);
+}
+device_initcall(perf_event_sysfs_init);
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
deleted file mode 100644
index b98bed3..0000000
--- a/kernel/perf_event.c
+++ /dev/null
@@ -1,5895 +0,0 @@
-/*
- * Performance events core code:
- *
- * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
- * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
- * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
- *
- * For licensing details see kernel-base/COPYING
- */
-
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/cpu.h>
-#include <linux/smp.h>
-#include <linux/file.h>
-#include <linux/poll.h>
-#include <linux/slab.h>
-#include <linux/hash.h>
-#include <linux/sysfs.h>
-#include <linux/dcache.h>
-#include <linux/percpu.h>
-#include <linux/ptrace.h>
-#include <linux/vmstat.h>
-#include <linux/vmalloc.h>
-#include <linux/hardirq.h>
-#include <linux/rculist.h>
-#include <linux/uaccess.h>
-#include <linux/syscalls.h>
-#include <linux/anon_inodes.h>
-#include <linux/kernel_stat.h>
-#include <linux/perf_event.h>
-#include <linux/ftrace_event.h>
-#include <linux/hw_breakpoint.h>
-
-#include <asm/irq_regs.h>
-
-/*
- * Each CPU has a list of per CPU events:
- */
-static DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context);
-
-int perf_max_events __read_mostly = 1;
-static int perf_reserved_percpu __read_mostly;
-static int perf_overcommit __read_mostly = 1;
-
-static atomic_t nr_events __read_mostly;
-static atomic_t nr_mmap_events __read_mostly;
-static atomic_t nr_comm_events __read_mostly;
-static atomic_t nr_task_events __read_mostly;
-
-/*
- * perf event paranoia level:
- * -1 - not paranoid at all
- * 0 - disallow raw tracepoint access for unpriv
- * 1 - disallow cpu events for unpriv
- * 2 - disallow kernel profiling for unpriv
- */
-int sysctl_perf_event_paranoid __read_mostly = 1;
-
-int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */
-
-/*
- * max perf event sample rate
- */
-int sysctl_perf_event_sample_rate __read_mostly = 100000;
-
-static atomic64_t perf_event_id;
-
-/*
- * Lock for (sysadmin-configurable) event reservations:
- */
-static DEFINE_SPINLOCK(perf_resource_lock);
-
-/*
- * Architecture provided APIs - weak aliases:
- */
-extern __weak const struct pmu *hw_perf_event_init(struct perf_event *event)
-{
- return NULL;
-}
-
-void __weak hw_perf_disable(void) { barrier(); }
-void __weak hw_perf_enable(void) { barrier(); }
-
-void __weak perf_event_print_debug(void) { }
-
-static DEFINE_PER_CPU(int, perf_disable_count);
-
-void perf_disable(void)
-{
- if (!__get_cpu_var(perf_disable_count)++)
- hw_perf_disable();
-}
-
-void perf_enable(void)
-{
- if (!--__get_cpu_var(perf_disable_count))
- hw_perf_enable();
-}
-
-static void get_ctx(struct perf_event_context *ctx)
-{
- WARN_ON(!atomic_inc_not_zero(&ctx->refcount));
-}
-
-static void free_ctx(struct rcu_head *head)
-{
- struct perf_event_context *ctx;
-
- ctx = container_of(head, struct perf_event_context, rcu_head);
- kfree(ctx);
-}
-
-static void put_ctx(struct perf_event_context *ctx)
-{
- if (atomic_dec_and_test(&ctx->refcount)) {
- if (ctx->parent_ctx)
- put_ctx(ctx->parent_ctx);
- if (ctx->task)
- put_task_struct(ctx->task);
- call_rcu(&ctx->rcu_head, free_ctx);
- }
-}
-
-static void unclone_ctx(struct perf_event_context *ctx)
-{
- if (ctx->parent_ctx) {
- put_ctx(ctx->parent_ctx);
- ctx->parent_ctx = NULL;
- }
-}
-
-/*
- * If we inherit events we want to return the parent event id
- * to userspace.
- */
-static u64 primary_event_id(struct perf_event *event)
-{
- u64 id = event->id;
-
- if (event->parent)
- id = event->parent->id;
-
- return id;
-}
-
-/*
- * Get the perf_event_context for a task and lock it.
- * This has to cope with with the fact that until it is locked,
- * the context could get moved to another task.
- */
-static struct perf_event_context *
-perf_lock_task_context(struct task_struct *task, unsigned long *flags)
-{
- struct perf_event_context *ctx;
-
- rcu_read_lock();
- retry:
- ctx = rcu_dereference(task->perf_event_ctxp);
- if (ctx) {
- /*
- * If this context is a clone of another, it might
- * get swapped for another underneath us by
- * perf_event_task_sched_out, though the
- * rcu_read_lock() protects us from any context
- * getting freed. Lock the context and check if it
- * got swapped before we could get the lock, and retry
- * if so. If we locked the right context, then it
- * can't get swapped on us any more.
- */
- raw_spin_lock_irqsave(&ctx->lock, *flags);
- if (ctx != rcu_dereference(task->perf_event_ctxp)) {
- raw_spin_unlock_irqrestore(&ctx->lock, *flags);
- goto retry;
- }
-
- if (!atomic_inc_not_zero(&ctx->refcount)) {
- raw_spin_unlock_irqrestore(&ctx->lock, *flags);
- ctx = NULL;
- }
- }
- rcu_read_unlock();
- return ctx;
-}
-
-/*
- * Get the context for a task and increment its pin_count so it
- * can't get swapped to another task. This also increments its
- * reference count so that the context can't get freed.
- */
-static struct perf_event_context *perf_pin_task_context(struct task_struct *task)
-{
- struct perf_event_context *ctx;
- unsigned long flags;
-
- ctx = perf_lock_task_context(task, &flags);
- if (ctx) {
- ++ctx->pin_count;
- raw_spin_unlock_irqrestore(&ctx->lock, flags);
- }
- return ctx;
-}
-
-static void perf_unpin_context(struct perf_event_context *ctx)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&ctx->lock, flags);
- --ctx->pin_count;
- raw_spin_unlock_irqrestore(&ctx->lock, flags);
- put_ctx(ctx);
-}
-
-static inline u64 perf_clock(void)
-{
- return local_clock();
-}
-
-/*
- * Update the record of the current time in a context.
- */
-static void update_context_time(struct perf_event_context *ctx)
-{
- u64 now = perf_clock();
-
- ctx->time += now - ctx->timestamp;
- ctx->timestamp = now;
-}
-
-/*
- * Update the total_time_enabled and total_time_running fields for a event.
- */
-static void update_event_times(struct perf_event *event)
-{
- struct perf_event_context *ctx = event->ctx;
- u64 run_end;
-
- if (event->state < PERF_EVENT_STATE_INACTIVE ||
- event->group_leader->state < PERF_EVENT_STATE_INACTIVE)
- return;
-
- if (ctx->is_active)
- run_end = ctx->time;
- else
- run_end = event->tstamp_stopped;
-
- event->total_time_enabled = run_end - event->tstamp_enabled;
-
- if (event->state == PERF_EVENT_STATE_INACTIVE)
- run_end = event->tstamp_stopped;
- else
- run_end = ctx->time;
-
- event->total_time_running = run_end - event->tstamp_running;
-}
-
-/*
- * Update total_time_enabled and total_time_running for all events in a group.
- */
-static void update_group_times(struct perf_event *leader)
-{
- struct perf_event *event;
-
- update_event_times(leader);
- list_for_each_entry(event, &leader->sibling_list, group_entry)
- update_event_times(event);
-}
-
-static struct list_head *
-ctx_group_list(struct perf_event *event, struct perf_event_context *ctx)
-{
- if (event->attr.pinned)
- return &ctx->pinned_groups;
- else
- return &ctx->flexible_groups;
-}
-
-/*
- * Add a event from the lists for its context.
- * Must be called with ctx->mutex and ctx->lock held.
- */
-static void
-list_add_event(struct perf_event *event, struct perf_event_context *ctx)
-{
- WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
- event->attach_state |= PERF_ATTACH_CONTEXT;
-
- /*
- * If we're a stand alone event or group leader, we go to the context
- * list, group events are kept attached to the group so that
- * perf_group_detach can, at all times, locate all siblings.
- */
- if (event->group_leader == event) {
- struct list_head *list;
-
- if (is_software_event(event))
- event->group_flags |= PERF_GROUP_SOFTWARE;
-
- list = ctx_group_list(event, ctx);
- list_add_tail(&event->group_entry, list);
- }
-
- list_add_rcu(&event->event_entry, &ctx->event_list);
- ctx->nr_events++;
- if (event->attr.inherit_stat)
- ctx->nr_stat++;
-}
-
-static void perf_group_attach(struct perf_event *event)
-{
- struct perf_event *group_leader = event->group_leader;
-
- WARN_ON_ONCE(event->attach_state & PERF_ATTACH_GROUP);
- event->attach_state |= PERF_ATTACH_GROUP;
-
- if (group_leader == event)
- return;
-
- if (group_leader->group_flags & PERF_GROUP_SOFTWARE &&
- !is_software_event(event))
- group_leader->group_flags &= ~PERF_GROUP_SOFTWARE;
-
- list_add_tail(&event->group_entry, &group_leader->sibling_list);
- group_leader->nr_siblings++;
-}
-
-/*
- * Remove a event from the lists for its context.
- * Must be called with ctx->mutex and ctx->lock held.
- */
-static void
-list_del_event(struct perf_event *event, struct perf_event_context *ctx)
-{
- /*
- * We can have double detach due to exit/hot-unplug + close.
- */
- if (!(event->attach_state & PERF_ATTACH_CONTEXT))
- return;
-
- event->attach_state &= ~PERF_ATTACH_CONTEXT;
-
- ctx->nr_events--;
- if (event->attr.inherit_stat)
- ctx->nr_stat--;
-
- list_del_rcu(&event->event_entry);
-
- if (event->group_leader == event)
- list_del_init(&event->group_entry);
-
- update_group_times(event);
-
- /*
- * If event was in error state, then keep it
- * that way, otherwise bogus counts will be
- * returned on read(). The only way to get out
- * of error state is by explicit re-enabling
- * of the event
- */
- if (event->state > PERF_EVENT_STATE_OFF)
- event->state = PERF_EVENT_STATE_OFF;
-}
-
-static void perf_group_detach(struct perf_event *event)
-{
- struct perf_event *sibling, *tmp;
- struct list_head *list = NULL;
-
- /*
- * We can have double detach due to exit/hot-unplug + close.
- */
- if (!(event->attach_state & PERF_ATTACH_GROUP))
- return;
-
- event->attach_state &= ~PERF_ATTACH_GROUP;
-
- /*
- * If this is a sibling, remove it from its group.
- */
- if (event->group_leader != event) {
- list_del_init(&event->group_entry);
- event->group_leader->nr_siblings--;
- return;
- }
-
- if (!list_empty(&event->group_entry))
- list = &event->group_entry;
-
- /*
- * If this was a group event with sibling events then
- * upgrade the siblings to singleton events by adding them
- * to whatever list we are on.
- */
- list_for_each_entry_safe(sibling, tmp, &event->sibling_list, group_entry) {
- if (list)
- list_move_tail(&sibling->group_entry, list);
- sibling->group_leader = sibling;
-
- /* Inherit group flags from the previous leader */
- sibling->group_flags = event->group_flags;
- }
-}
-
-static inline int
-event_filter_match(struct perf_event *event)
-{
- return event->cpu == -1 || event->cpu == smp_processor_id();
-}
-
-static void
-event_sched_out(struct perf_event *event,
- struct perf_cpu_context *cpuctx,
- struct perf_event_context *ctx)
-{
- u64 delta;
- /*
- * An event which could not be activated because of
- * filter mismatch still needs to have its timings
- * maintained, otherwise bogus information is return
- * via read() for time_enabled, time_running:
- */
- if (event->state == PERF_EVENT_STATE_INACTIVE
- && !event_filter_match(event)) {
- delta = ctx->time - event->tstamp_stopped;
- event->tstamp_running += delta;
- event->tstamp_stopped = ctx->time;
- }
-
- if (event->state != PERF_EVENT_STATE_ACTIVE)
- return;
-
- event->state = PERF_EVENT_STATE_INACTIVE;
- if (event->pending_disable) {
- event->pending_disable = 0;
- event->state = PERF_EVENT_STATE_OFF;
- }
- event->tstamp_stopped = ctx->time;
- event->pmu->disable(event);
- event->oncpu = -1;
-
- if (!is_software_event(event))
- cpuctx->active_oncpu--;
- ctx->nr_active--;
- if (event->attr.exclusive || !cpuctx->active_oncpu)
- cpuctx->exclusive = 0;
-}
-
-static void
-group_sched_out(struct perf_event *group_event,
- struct perf_cpu_context *cpuctx,
- struct perf_event_context *ctx)
-{
- struct perf_event *event;
- int state = group_event->state;
-
- event_sched_out(group_event, cpuctx, ctx);
-
- /*
- * Schedule out siblings (if any):
- */
- list_for_each_entry(event, &group_event->sibling_list, group_entry)
- event_sched_out(event, cpuctx, ctx);
-
- if (state == PERF_EVENT_STATE_ACTIVE && group_event->attr.exclusive)
- cpuctx->exclusive = 0;
-}
-
-/*
- * Cross CPU call to remove a performance event
- *
- * We disable the event on the hardware level first. After that we
- * remove it from the context list.
- */
-static void __perf_event_remove_from_context(void *info)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event *event = info;
- struct perf_event_context *ctx = event->ctx;
-
- /*
- * If this is a task context, we need to check whether it is
- * the current task context of this cpu. If not it has been
- * scheduled out before the smp call arrived.
- */
- if (ctx->task && cpuctx->task_ctx != ctx)
- return;
-
- raw_spin_lock(&ctx->lock);
- /*
- * Protect the list operation against NMI by disabling the
- * events on a global level.
- */
- perf_disable();
-
- event_sched_out(event, cpuctx, ctx);
-
- list_del_event(event, ctx);
-
- if (!ctx->task) {
- /*
- * Allow more per task events with respect to the
- * reservation:
- */
- cpuctx->max_pertask =
- min(perf_max_events - ctx->nr_events,
- perf_max_events - perf_reserved_percpu);
- }
-
- perf_enable();
- raw_spin_unlock(&ctx->lock);
-}
-
-
-/*
- * Remove the event from a task's (or a CPU's) list of events.
- *
- * Must be called with ctx->mutex held.
- *
- * CPU events are removed with a smp call. For task events we only
- * call when the task is on a CPU.
- *
- * If event->ctx is a cloned context, callers must make sure that
- * every task struct that event->ctx->task could possibly point to
- * remains valid. This is OK when called from perf_release since
- * that only calls us on the top-level context, which can't be a clone.
- * When called from perf_event_exit_task, it's OK because the
- * context has been detached from its task.
- */
-static void perf_event_remove_from_context(struct perf_event *event)
-{
- struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
-
- if (!task) {
- /*
- * Per cpu events are removed via an smp call and
- * the removal is always successful.
- */
- smp_call_function_single(event->cpu,
- __perf_event_remove_from_context,
- event, 1);
- return;
- }
-
-retry:
- task_oncpu_function_call(task, __perf_event_remove_from_context,
- event);
-
- raw_spin_lock_irq(&ctx->lock);
- /*
- * If the context is active we need to retry the smp call.
- */
- if (ctx->nr_active && !list_empty(&event->group_entry)) {
- raw_spin_unlock_irq(&ctx->lock);
- goto retry;
- }
-
- /*
- * The lock prevents that this context is scheduled in so we
- * can remove the event safely, if the call above did not
- * succeed.
- */
- if (!list_empty(&event->group_entry))
- list_del_event(event, ctx);
- raw_spin_unlock_irq(&ctx->lock);
-}
-
-/*
- * Cross CPU call to disable a performance event
- */
-static void __perf_event_disable(void *info)
-{
- struct perf_event *event = info;
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = event->ctx;
-
- /*
- * If this is a per-task event, need to check whether this
- * event's task is the current task on this cpu.
- */
- if (ctx->task && cpuctx->task_ctx != ctx)
- return;
-
- raw_spin_lock(&ctx->lock);
-
- /*
- * If the event is on, turn it off.
- * If it is in error state, leave it in error state.
- */
- if (event->state >= PERF_EVENT_STATE_INACTIVE) {
- update_context_time(ctx);
- update_group_times(event);
- if (event == event->group_leader)
- group_sched_out(event, cpuctx, ctx);
- else
- event_sched_out(event, cpuctx, ctx);
- event->state = PERF_EVENT_STATE_OFF;
- }
-
- raw_spin_unlock(&ctx->lock);
-}
-
-/*
- * Disable a event.
- *
- * If event->ctx is a cloned context, callers must make sure that
- * every task struct that event->ctx->task could possibly point to
- * remains valid. This condition is satisifed when called through
- * perf_event_for_each_child or perf_event_for_each because they
- * hold the top-level event's child_mutex, so any descendant that
- * goes to exit will block in sync_child_event.
- * When called from perf_pending_event it's OK because event->ctx
- * is the current context on this CPU and preemption is disabled,
- * hence we can't get into perf_event_task_sched_out for this context.
- */
-void perf_event_disable(struct perf_event *event)
-{
- struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
-
- if (!task) {
- /*
- * Disable the event on the cpu that it's on
- */
- smp_call_function_single(event->cpu, __perf_event_disable,
- event, 1);
- return;
- }
-
- retry:
- task_oncpu_function_call(task, __perf_event_disable, event);
-
- raw_spin_lock_irq(&ctx->lock);
- /*
- * If the event is still active, we need to retry the cross-call.
- */
- if (event->state == PERF_EVENT_STATE_ACTIVE) {
- raw_spin_unlock_irq(&ctx->lock);
- goto retry;
- }
-
- /*
- * Since we have the lock this context can't be scheduled
- * in, so we can change the state safely.
- */
- if (event->state == PERF_EVENT_STATE_INACTIVE) {
- update_group_times(event);
- event->state = PERF_EVENT_STATE_OFF;
- }
-
- raw_spin_unlock_irq(&ctx->lock);
-}
-
-static int
-event_sched_in(struct perf_event *event,
- struct perf_cpu_context *cpuctx,
- struct perf_event_context *ctx)
-{
- if (event->state <= PERF_EVENT_STATE_OFF)
- return 0;
-
- event->state = PERF_EVENT_STATE_ACTIVE;
- event->oncpu = smp_processor_id();
- /*
- * The new state must be visible before we turn it on in the hardware:
- */
- smp_wmb();
-
- if (event->pmu->enable(event)) {
- event->state = PERF_EVENT_STATE_INACTIVE;
- event->oncpu = -1;
- return -EAGAIN;
- }
-
- event->tstamp_running += ctx->time - event->tstamp_stopped;
-
- if (!is_software_event(event))
- cpuctx->active_oncpu++;
- ctx->nr_active++;
-
- if (event->attr.exclusive)
- cpuctx->exclusive = 1;
-
- return 0;
-}
-
-static int
-group_sched_in(struct perf_event *group_event,
- struct perf_cpu_context *cpuctx,
- struct perf_event_context *ctx)
-{
- struct perf_event *event, *partial_group = NULL;
- const struct pmu *pmu = group_event->pmu;
- bool txn = false;
-
- if (group_event->state == PERF_EVENT_STATE_OFF)
- return 0;
-
- /* Check if group transaction availabe */
- if (pmu->start_txn)
- txn = true;
-
- if (txn)
- pmu->start_txn(pmu);
-
- if (event_sched_in(group_event, cpuctx, ctx)) {
- if (txn)
- pmu->cancel_txn(pmu);
- return -EAGAIN;
- }
-
- /*
- * Schedule in siblings as one group (if any):
- */
- list_for_each_entry(event, &group_event->sibling_list, group_entry) {
- if (event_sched_in(event, cpuctx, ctx)) {
- partial_group = event;
- goto group_error;
- }
- }
-
- if (!txn || !pmu->commit_txn(pmu))
- return 0;
-
-group_error:
- /*
- * Groups can be scheduled in as one unit only, so undo any
- * partial group before returning:
- */
- list_for_each_entry(event, &group_event->sibling_list, group_entry) {
- if (event == partial_group)
- break;
- event_sched_out(event, cpuctx, ctx);
- }
- event_sched_out(group_event, cpuctx, ctx);
-
- if (txn)
- pmu->cancel_txn(pmu);
-
- return -EAGAIN;
-}
-
-/*
- * Work out whether we can put this event group on the CPU now.
- */
-static int group_can_go_on(struct perf_event *event,
- struct perf_cpu_context *cpuctx,
- int can_add_hw)
-{
- /*
- * Groups consisting entirely of software events can always go on.
- */
- if (event->group_flags & PERF_GROUP_SOFTWARE)
- return 1;
- /*
- * If an exclusive group is already on, no other hardware
- * events can go on.
- */
- if (cpuctx->exclusive)
- return 0;
- /*
- * If this group is exclusive and there are already
- * events on the CPU, it can't go on.
- */
- if (event->attr.exclusive && cpuctx->active_oncpu)
- return 0;
- /*
- * Otherwise, try to add it if all previous groups were able
- * to go on.
- */
- return can_add_hw;
-}
-
-static void add_event_to_ctx(struct perf_event *event,
- struct perf_event_context *ctx)
-{
- list_add_event(event, ctx);
- perf_group_attach(event);
- event->tstamp_enabled = ctx->time;
- event->tstamp_running = ctx->time;
- event->tstamp_stopped = ctx->time;
-}
-
-/*
- * Cross CPU call to install and enable a performance event
- *
- * Must be called with ctx->mutex held
- */
-static void __perf_install_in_context(void *info)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event *event = info;
- struct perf_event_context *ctx = event->ctx;
- struct perf_event *leader = event->group_leader;
- int err;
-
- /*
- * If this is a task context, we need to check whether it is
- * the current task context of this cpu. If not it has been
- * scheduled out before the smp call arrived.
- * Or possibly this is the right context but it isn't
- * on this cpu because it had no events.
- */
- if (ctx->task && cpuctx->task_ctx != ctx) {
- if (cpuctx->task_ctx || ctx->task != current)
- return;
- cpuctx->task_ctx = ctx;
- }
-
- raw_spin_lock(&ctx->lock);
- ctx->is_active = 1;
- update_context_time(ctx);
-
- /*
- * Protect the list operation against NMI by disabling the
- * events on a global level. NOP for non NMI based events.
- */
- perf_disable();
-
- add_event_to_ctx(event, ctx);
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- goto unlock;
-
- /*
- * Don't put the event on if it is disabled or if
- * it is in a group and the group isn't on.
- */
- if (event->state != PERF_EVENT_STATE_INACTIVE ||
- (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE))
- goto unlock;
-
- /*
- * An exclusive event can't go on if there are already active
- * hardware events, and no hardware event can go on if there
- * is already an exclusive event on.
- */
- if (!group_can_go_on(event, cpuctx, 1))
- err = -EEXIST;
- else
- err = event_sched_in(event, cpuctx, ctx);
-
- if (err) {
- /*
- * This event couldn't go on. If it is in a group
- * then we have to pull the whole group off.
- * If the event group is pinned then put it in error state.
- */
- if (leader != event)
- group_sched_out(leader, cpuctx, ctx);
- if (leader->attr.pinned) {
- update_group_times(leader);
- leader->state = PERF_EVENT_STATE_ERROR;
- }
- }
-
- if (!err && !ctx->task && cpuctx->max_pertask)
- cpuctx->max_pertask--;
-
- unlock:
- perf_enable();
-
- raw_spin_unlock(&ctx->lock);
-}
-
-/*
- * Attach a performance event to a context
- *
- * First we add the event to the list with the hardware enable bit
- * in event->hw_config cleared.
- *
- * If the event is attached to a task which is on a CPU we use a smp
- * call to enable it in the task context. The task might have been
- * scheduled away, but we check this in the smp call again.
- *
- * Must be called with ctx->mutex held.
- */
-static void
-perf_install_in_context(struct perf_event_context *ctx,
- struct perf_event *event,
- int cpu)
-{
- struct task_struct *task = ctx->task;
-
- if (!task) {
- /*
- * Per cpu events are installed via an smp call and
- * the install is always successful.
- */
- smp_call_function_single(cpu, __perf_install_in_context,
- event, 1);
- return;
- }
-
-retry:
- task_oncpu_function_call(task, __perf_install_in_context,
- event);
-
- raw_spin_lock_irq(&ctx->lock);
- /*
- * we need to retry the smp call.
- */
- if (ctx->is_active && list_empty(&event->group_entry)) {
- raw_spin_unlock_irq(&ctx->lock);
- goto retry;
- }
-
- /*
- * The lock prevents that this context is scheduled in so we
- * can add the event safely, if it the call above did not
- * succeed.
- */
- if (list_empty(&event->group_entry))
- add_event_to_ctx(event, ctx);
- raw_spin_unlock_irq(&ctx->lock);
-}
-
-/*
- * Put a event into inactive state and update time fields.
- * Enabling the leader of a group effectively enables all
- * the group members that aren't explicitly disabled, so we
- * have to update their ->tstamp_enabled also.
- * Note: this works for group members as well as group leaders
- * since the non-leader members' sibling_lists will be empty.
- */
-static void __perf_event_mark_enabled(struct perf_event *event,
- struct perf_event_context *ctx)
-{
- struct perf_event *sub;
-
- event->state = PERF_EVENT_STATE_INACTIVE;
- event->tstamp_enabled = ctx->time - event->total_time_enabled;
- list_for_each_entry(sub, &event->sibling_list, group_entry)
- if (sub->state >= PERF_EVENT_STATE_INACTIVE)
- sub->tstamp_enabled =
- ctx->time - sub->total_time_enabled;
-}
-
-/*
- * Cross CPU call to enable a performance event
- */
-static void __perf_event_enable(void *info)
-{
- struct perf_event *event = info;
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = event->ctx;
- struct perf_event *leader = event->group_leader;
- int err;
-
- /*
- * If this is a per-task event, need to check whether this
- * event's task is the current task on this cpu.
- */
- if (ctx->task && cpuctx->task_ctx != ctx) {
- if (cpuctx->task_ctx || ctx->task != current)
- return;
- cpuctx->task_ctx = ctx;
- }
-
- raw_spin_lock(&ctx->lock);
- ctx->is_active = 1;
- update_context_time(ctx);
-
- if (event->state >= PERF_EVENT_STATE_INACTIVE)
- goto unlock;
- __perf_event_mark_enabled(event, ctx);
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- goto unlock;
-
- /*
- * If the event is in a group and isn't the group leader,
- * then don't put it on unless the group is on.
- */
- if (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE)
- goto unlock;
-
- if (!group_can_go_on(event, cpuctx, 1)) {
- err = -EEXIST;
- } else {
- perf_disable();
- if (event == leader)
- err = group_sched_in(event, cpuctx, ctx);
- else
- err = event_sched_in(event, cpuctx, ctx);
- perf_enable();
- }
-
- if (err) {
- /*
- * If this event can't go on and it's part of a
- * group, then the whole group has to come off.
- */
- if (leader != event)
- group_sched_out(leader, cpuctx, ctx);
- if (leader->attr.pinned) {
- update_group_times(leader);
- leader->state = PERF_EVENT_STATE_ERROR;
- }
- }
-
- unlock:
- raw_spin_unlock(&ctx->lock);
-}
-
-/*
- * Enable a event.
- *
- * If event->ctx is a cloned context, callers must make sure that
- * every task struct that event->ctx->task could possibly point to
- * remains valid. This condition is satisfied when called through
- * perf_event_for_each_child or perf_event_for_each as described
- * for perf_event_disable.
- */
-void perf_event_enable(struct perf_event *event)
-{
- struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
-
- if (!task) {
- /*
- * Enable the event on the cpu that it's on
- */
- smp_call_function_single(event->cpu, __perf_event_enable,
- event, 1);
- return;
- }
-
- raw_spin_lock_irq(&ctx->lock);
- if (event->state >= PERF_EVENT_STATE_INACTIVE)
- goto out;
-
- /*
- * If the event is in error state, clear that first.
- * That way, if we see the event in error state below, we
- * know that it has gone back into error state, as distinct
- * from the task having been scheduled away before the
- * cross-call arrived.
- */
- if (event->state == PERF_EVENT_STATE_ERROR)
- event->state = PERF_EVENT_STATE_OFF;
-
- retry:
- raw_spin_unlock_irq(&ctx->lock);
- task_oncpu_function_call(task, __perf_event_enable, event);
-
- raw_spin_lock_irq(&ctx->lock);
-
- /*
- * If the context is active and the event is still off,
- * we need to retry the cross-call.
- */
- if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF)
- goto retry;
-
- /*
- * Since we have the lock this context can't be scheduled
- * in, so we can change the state safely.
- */
- if (event->state == PERF_EVENT_STATE_OFF)
- __perf_event_mark_enabled(event, ctx);
-
- out:
- raw_spin_unlock_irq(&ctx->lock);
-}
-
-static int perf_event_refresh(struct perf_event *event, int refresh)
-{
- /*
- * not supported on inherited events
- */
- if (event->attr.inherit)
- return -EINVAL;
-
- atomic_add(refresh, &event->event_limit);
- perf_event_enable(event);
-
- return 0;
-}
-
-enum event_type_t {
- EVENT_FLEXIBLE = 0x1,
- EVENT_PINNED = 0x2,
- EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED,
-};
-
-static void ctx_sched_out(struct perf_event_context *ctx,
- struct perf_cpu_context *cpuctx,
- enum event_type_t event_type)
-{
- struct perf_event *event;
-
- raw_spin_lock(&ctx->lock);
- ctx->is_active = 0;
- if (likely(!ctx->nr_events))
- goto out;
- update_context_time(ctx);
-
- perf_disable();
- if (!ctx->nr_active)
- goto out_enable;
-
- if (event_type & EVENT_PINNED)
- list_for_each_entry(event, &ctx->pinned_groups, group_entry)
- group_sched_out(event, cpuctx, ctx);
-
- if (event_type & EVENT_FLEXIBLE)
- list_for_each_entry(event, &ctx->flexible_groups, group_entry)
- group_sched_out(event, cpuctx, ctx);
-
- out_enable:
- perf_enable();
- out:
- raw_spin_unlock(&ctx->lock);
-}
-
-/*
- * Test whether two contexts are equivalent, i.e. whether they
- * have both been cloned from the same version of the same context
- * and they both have the same number of enabled events.
- * If the number of enabled events is the same, then the set
- * of enabled events should be the same, because these are both
- * inherited contexts, therefore we can't access individual events
- * in them directly with an fd; we can only enable/disable all
- * events via prctl, or enable/disable all events in a family
- * via ioctl, which will have the same effect on both contexts.
- */
-static int context_equiv(struct perf_event_context *ctx1,
- struct perf_event_context *ctx2)
-{
- return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx
- && ctx1->parent_gen == ctx2->parent_gen
- && !ctx1->pin_count && !ctx2->pin_count;
-}
-
-static void __perf_event_sync_stat(struct perf_event *event,
- struct perf_event *next_event)
-{
- u64 value;
-
- if (!event->attr.inherit_stat)
- return;
-
- /*
- * Update the event value, we cannot use perf_event_read()
- * because we're in the middle of a context switch and have IRQs
- * disabled, which upsets smp_call_function_single(), however
- * we know the event must be on the current CPU, therefore we
- * don't need to use it.
- */
- switch (event->state) {
- case PERF_EVENT_STATE_ACTIVE:
- event->pmu->read(event);
- /* fall-through */
-
- case PERF_EVENT_STATE_INACTIVE:
- update_event_times(event);
- break;
-
- default:
- break;
- }
-
- /*
- * In order to keep per-task stats reliable we need to flip the event
- * values when we flip the contexts.
- */
- value = local64_read(&next_event->count);
- value = local64_xchg(&event->count, value);
- local64_set(&next_event->count, value);
-
- swap(event->total_time_enabled, next_event->total_time_enabled);
- swap(event->total_time_running, next_event->total_time_running);
-
- /*
- * Since we swizzled the values, update the user visible data too.
- */
- perf_event_update_userpage(event);
- perf_event_update_userpage(next_event);
-}
-
-#define list_next_entry(pos, member) \
- list_entry(pos->member.next, typeof(*pos), member)
-
-static void perf_event_sync_stat(struct perf_event_context *ctx,
- struct perf_event_context *next_ctx)
-{
- struct perf_event *event, *next_event;
-
- if (!ctx->nr_stat)
- return;
-
- update_context_time(ctx);
-
- event = list_first_entry(&ctx->event_list,
- struct perf_event, event_entry);
-
- next_event = list_first_entry(&next_ctx->event_list,
- struct perf_event, event_entry);
-
- while (&event->event_entry != &ctx->event_list &&
- &next_event->event_entry != &next_ctx->event_list) {
-
- __perf_event_sync_stat(event, next_event);
-
- event = list_next_entry(event, event_entry);
- next_event = list_next_entry(next_event, event_entry);
- }
-}
-
-/*
- * Called from scheduler to remove the events of the current task,
- * with interrupts disabled.
- *
- * We stop each event and update the event value in event->count.
- *
- * This does not protect us against NMI, but disable()
- * sets the disabled bit in the control field of event _before_
- * 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)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = task->perf_event_ctxp;
- struct perf_event_context *next_ctx;
- struct perf_event_context *parent;
- int do_switch = 1;
-
- perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
-
- if (likely(!ctx || !cpuctx->task_ctx))
- return;
-
- rcu_read_lock();
- parent = rcu_dereference(ctx->parent_ctx);
- next_ctx = next->perf_event_ctxp;
- if (parent && next_ctx &&
- rcu_dereference(next_ctx->parent_ctx) == parent) {
- /*
- * Looks like the two contexts are clones, so we might be
- * able to optimize the context switch. We lock both
- * contexts and check that they are clones under the
- * lock (including re-checking that neither has been
- * uncloned in the meantime). It doesn't matter which
- * order we take the locks because no other cpu could
- * be trying to lock both of these tasks.
- */
- raw_spin_lock(&ctx->lock);
- raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING);
- if (context_equiv(ctx, next_ctx)) {
- /*
- * XXX do we need a memory barrier of sorts
- * wrt to rcu_dereference() of perf_event_ctxp
- */
- task->perf_event_ctxp = next_ctx;
- next->perf_event_ctxp = ctx;
- ctx->task = next;
- next_ctx->task = task;
- do_switch = 0;
-
- perf_event_sync_stat(ctx, next_ctx);
- }
- raw_spin_unlock(&next_ctx->lock);
- raw_spin_unlock(&ctx->lock);
- }
- rcu_read_unlock();
-
- if (do_switch) {
- ctx_sched_out(ctx, cpuctx, EVENT_ALL);
- cpuctx->task_ctx = NULL;
- }
-}
-
-static void task_ctx_sched_out(struct perf_event_context *ctx,
- enum event_type_t event_type)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
-
- if (!cpuctx->task_ctx)
- return;
-
- if (WARN_ON_ONCE(ctx != cpuctx->task_ctx))
- return;
-
- ctx_sched_out(ctx, cpuctx, event_type);
- cpuctx->task_ctx = NULL;
-}
-
-/*
- * Called with IRQs disabled
- */
-static void __perf_event_task_sched_out(struct perf_event_context *ctx)
-{
- task_ctx_sched_out(ctx, EVENT_ALL);
-}
-
-/*
- * Called with IRQs disabled
- */
-static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx,
- enum event_type_t event_type)
-{
- ctx_sched_out(&cpuctx->ctx, cpuctx, event_type);
-}
-
-static void
-ctx_pinned_sched_in(struct perf_event_context *ctx,
- struct perf_cpu_context *cpuctx)
-{
- struct perf_event *event;
-
- list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
- if (event->state <= PERF_EVENT_STATE_OFF)
- continue;
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- continue;
-
- if (group_can_go_on(event, cpuctx, 1))
- group_sched_in(event, cpuctx, ctx);
-
- /*
- * If this pinned group hasn't been scheduled,
- * put it in error state.
- */
- if (event->state == PERF_EVENT_STATE_INACTIVE) {
- update_group_times(event);
- event->state = PERF_EVENT_STATE_ERROR;
- }
- }
-}
-
-static void
-ctx_flexible_sched_in(struct perf_event_context *ctx,
- struct perf_cpu_context *cpuctx)
-{
- struct perf_event *event;
- int can_add_hw = 1;
-
- list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
- /* Ignore events in OFF or ERROR state */
- if (event->state <= PERF_EVENT_STATE_OFF)
- continue;
- /*
- * Listen to the 'cpu' scheduling filter constraint
- * of events:
- */
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- continue;
-
- if (group_can_go_on(event, cpuctx, can_add_hw))
- if (group_sched_in(event, cpuctx, ctx))
- can_add_hw = 0;
- }
-}
-
-static void
-ctx_sched_in(struct perf_event_context *ctx,
- struct perf_cpu_context *cpuctx,
- enum event_type_t event_type)
-{
- raw_spin_lock(&ctx->lock);
- ctx->is_active = 1;
- if (likely(!ctx->nr_events))
- goto out;
-
- ctx->timestamp = perf_clock();
-
- perf_disable();
-
- /*
- * First go through the list and put on any pinned groups
- * in order to give them the best chance of going on.
- */
- if (event_type & EVENT_PINNED)
- ctx_pinned_sched_in(ctx, cpuctx);
-
- /* Then walk through the lower prio flexible groups */
- if (event_type & EVENT_FLEXIBLE)
- ctx_flexible_sched_in(ctx, cpuctx);
-
- perf_enable();
- out:
- raw_spin_unlock(&ctx->lock);
-}
-
-static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
- enum event_type_t event_type)
-{
- struct perf_event_context *ctx = &cpuctx->ctx;
-
- ctx_sched_in(ctx, cpuctx, event_type);
-}
-
-static void task_ctx_sched_in(struct task_struct *task,
- enum event_type_t event_type)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = task->perf_event_ctxp;
-
- if (likely(!ctx))
- return;
- if (cpuctx->task_ctx == ctx)
- return;
- ctx_sched_in(ctx, cpuctx, event_type);
- cpuctx->task_ctx = ctx;
-}
-/*
- * Called from scheduler to add the events of the current task
- * with interrupts disabled.
- *
- * We restore the event value and then enable it.
- *
- * This does not protect us against NMI, but enable()
- * sets the enabled bit in the control field of event _before_
- * 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 *task)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = task->perf_event_ctxp;
-
- if (likely(!ctx))
- return;
-
- if (cpuctx->task_ctx == ctx)
- return;
-
- perf_disable();
-
- /*
- * We want to keep the following priority order:
- * cpu pinned (that don't need to move), task pinned,
- * cpu flexible, task flexible.
- */
- cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
-
- ctx_sched_in(ctx, cpuctx, EVENT_PINNED);
- cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE);
- ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE);
-
- cpuctx->task_ctx = ctx;
-
- perf_enable();
-}
-
-#define MAX_INTERRUPTS (~0ULL)
-
-static void perf_log_throttle(struct perf_event *event, int enable);
-
-static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count)
-{
- u64 frequency = event->attr.sample_freq;
- u64 sec = NSEC_PER_SEC;
- u64 divisor, dividend;
-
- int count_fls, nsec_fls, frequency_fls, sec_fls;
-
- count_fls = fls64(count);
- nsec_fls = fls64(nsec);
- frequency_fls = fls64(frequency);
- sec_fls = 30;
-
- /*
- * We got @count in @nsec, with a target of sample_freq HZ
- * the target period becomes:
- *
- * @count * 10^9
- * period = -------------------
- * @nsec * sample_freq
- *
- */
-
- /*
- * Reduce accuracy by one bit such that @a and @b converge
- * to a similar magnitude.
- */
-#define REDUCE_FLS(a, b) \
-do { \
- if (a##_fls > b##_fls) { \
- a >>= 1; \
- a##_fls--; \
- } else { \
- b >>= 1; \
- b##_fls--; \
- } \
-} while (0)
-
- /*
- * Reduce accuracy until either term fits in a u64, then proceed with
- * the other, so that finally we can do a u64/u64 division.
- */
- while (count_fls + sec_fls > 64 && nsec_fls + frequency_fls > 64) {
- REDUCE_FLS(nsec, frequency);
- REDUCE_FLS(sec, count);
- }
-
- if (count_fls + sec_fls > 64) {
- divisor = nsec * frequency;
-
- while (count_fls + sec_fls > 64) {
- REDUCE_FLS(count, sec);
- divisor >>= 1;
- }
-
- dividend = count * sec;
- } else {
- dividend = count * sec;
-
- while (nsec_fls + frequency_fls > 64) {
- REDUCE_FLS(nsec, frequency);
- dividend >>= 1;
- }
-
- divisor = nsec * frequency;
- }
-
- if (!divisor)
- return dividend;
-
- return div64_u64(dividend, divisor);
-}
-
-static void perf_event_stop(struct perf_event *event)
-{
- if (!event->pmu->stop)
- return event->pmu->disable(event);
-
- return event->pmu->stop(event);
-}
-
-static int perf_event_start(struct perf_event *event)
-{
- if (!event->pmu->start)
- return event->pmu->enable(event);
-
- return event->pmu->start(event);
-}
-
-static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count)
-{
- struct hw_perf_event *hwc = &event->hw;
- s64 period, sample_period;
- s64 delta;
-
- period = perf_calculate_period(event, nsec, count);
-
- delta = (s64)(period - hwc->sample_period);
- delta = (delta + 7) / 8; /* low pass filter */
-
- sample_period = hwc->sample_period + delta;
-
- if (!sample_period)
- sample_period = 1;
-
- hwc->sample_period = sample_period;
-
- if (local64_read(&hwc->period_left) > 8*sample_period) {
- perf_disable();
- perf_event_stop(event);
- local64_set(&hwc->period_left, 0);
- perf_event_start(event);
- perf_enable();
- }
-}
-
-static void perf_ctx_adjust_freq(struct perf_event_context *ctx)
-{
- struct perf_event *event;
- struct hw_perf_event *hwc;
- u64 interrupts, now;
- s64 delta;
-
- raw_spin_lock(&ctx->lock);
- list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
- if (event->state != PERF_EVENT_STATE_ACTIVE)
- continue;
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- continue;
-
- hwc = &event->hw;
-
- interrupts = hwc->interrupts;
- hwc->interrupts = 0;
-
- /*
- * unthrottle events on the tick
- */
- if (interrupts == MAX_INTERRUPTS) {
- perf_log_throttle(event, 1);
- perf_disable();
- event->pmu->unthrottle(event);
- perf_enable();
- }
-
- if (!event->attr.freq || !event->attr.sample_freq)
- continue;
-
- perf_disable();
- event->pmu->read(event);
- now = local64_read(&event->count);
- delta = now - hwc->freq_count_stamp;
- hwc->freq_count_stamp = now;
-
- if (delta > 0)
- perf_adjust_period(event, TICK_NSEC, delta);
- perf_enable();
- }
- raw_spin_unlock(&ctx->lock);
-}
-
-/*
- * Round-robin a context's events:
- */
-static void rotate_ctx(struct perf_event_context *ctx)
-{
- raw_spin_lock(&ctx->lock);
-
- /* Rotate the first entry last of non-pinned groups */
- list_rotate_left(&ctx->flexible_groups);
-
- raw_spin_unlock(&ctx->lock);
-}
-
-void perf_event_task_tick(struct task_struct *curr)
-{
- struct perf_cpu_context *cpuctx;
- struct perf_event_context *ctx;
- int rotate = 0;
-
- if (!atomic_read(&nr_events))
- return;
-
- cpuctx = &__get_cpu_var(perf_cpu_context);
- if (cpuctx->ctx.nr_events &&
- cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
- rotate = 1;
-
- ctx = curr->perf_event_ctxp;
- if (ctx && ctx->nr_events && ctx->nr_events != ctx->nr_active)
- rotate = 1;
-
- perf_ctx_adjust_freq(&cpuctx->ctx);
- if (ctx)
- perf_ctx_adjust_freq(ctx);
-
- if (!rotate)
- return;
-
- perf_disable();
- cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
- if (ctx)
- task_ctx_sched_out(ctx, EVENT_FLEXIBLE);
-
- rotate_ctx(&cpuctx->ctx);
- if (ctx)
- rotate_ctx(ctx);
-
- cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE);
- if (ctx)
- task_ctx_sched_in(curr, EVENT_FLEXIBLE);
- perf_enable();
-}
-
-static int event_enable_on_exec(struct perf_event *event,
- struct perf_event_context *ctx)
-{
- if (!event->attr.enable_on_exec)
- return 0;
-
- event->attr.enable_on_exec = 0;
- if (event->state >= PERF_EVENT_STATE_INACTIVE)
- return 0;
-
- __perf_event_mark_enabled(event, ctx);
-
- return 1;
-}
-
-/*
- * Enable all of a task's events that have been marked enable-on-exec.
- * This expects task == current.
- */
-static void perf_event_enable_on_exec(struct task_struct *task)
-{
- struct perf_event_context *ctx;
- struct perf_event *event;
- unsigned long flags;
- int enabled = 0;
- int ret;
-
- local_irq_save(flags);
- ctx = task->perf_event_ctxp;
- if (!ctx || !ctx->nr_events)
- goto out;
-
- __perf_event_task_sched_out(ctx);
-
- raw_spin_lock(&ctx->lock);
-
- list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
- ret = event_enable_on_exec(event, ctx);
- if (ret)
- enabled = 1;
- }
-
- list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
- ret = event_enable_on_exec(event, ctx);
- if (ret)
- enabled = 1;
- }
-
- /*
- * Unclone this context if we enabled any event.
- */
- if (enabled)
- unclone_ctx(ctx);
-
- raw_spin_unlock(&ctx->lock);
-
- perf_event_task_sched_in(task);
- out:
- local_irq_restore(flags);
-}
-
-/*
- * Cross CPU call to read the hardware event
- */
-static void __perf_event_read(void *info)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event *event = info;
- struct perf_event_context *ctx = event->ctx;
-
- /*
- * If this is a task context, we need to check whether it is
- * the current task context of this cpu. If not it has been
- * scheduled out before the smp call arrived. In that case
- * event->count would have been updated to a recent sample
- * when the event was scheduled out.
- */
- if (ctx->task && cpuctx->task_ctx != ctx)
- return;
-
- raw_spin_lock(&ctx->lock);
- update_context_time(ctx);
- update_event_times(event);
- raw_spin_unlock(&ctx->lock);
-
- event->pmu->read(event);
-}
-
-static inline u64 perf_event_count(struct perf_event *event)
-{
- return local64_read(&event->count) + atomic64_read(&event->child_count);
-}
-
-static u64 perf_event_read(struct perf_event *event)
-{
- /*
- * If event is enabled and currently active on a CPU, update the
- * value in the event structure:
- */
- if (event->state == PERF_EVENT_STATE_ACTIVE) {
- smp_call_function_single(event->oncpu,
- __perf_event_read, event, 1);
- } else if (event->state == PERF_EVENT_STATE_INACTIVE) {
- struct perf_event_context *ctx = event->ctx;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&ctx->lock, flags);
- update_context_time(ctx);
- update_event_times(event);
- raw_spin_unlock_irqrestore(&ctx->lock, flags);
- }
-
- return perf_event_count(event);
-}
-
-/*
- * Initialize the perf_event context in a task_struct:
- */
-static void
-__perf_event_init_context(struct perf_event_context *ctx,
- struct task_struct *task)
-{
- raw_spin_lock_init(&ctx->lock);
- mutex_init(&ctx->mutex);
- INIT_LIST_HEAD(&ctx->pinned_groups);
- INIT_LIST_HEAD(&ctx->flexible_groups);
- INIT_LIST_HEAD(&ctx->event_list);
- atomic_set(&ctx->refcount, 1);
- ctx->task = task;
-}
-
-static struct perf_event_context *find_get_context(pid_t pid, int cpu)
-{
- struct perf_event_context *ctx;
- struct perf_cpu_context *cpuctx;
- struct task_struct *task;
- unsigned long flags;
- int err;
-
- if (pid == -1 && cpu != -1) {
- /* Must be root to operate on a CPU event: */
- if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
- return ERR_PTR(-EACCES);
-
- if (cpu < 0 || cpu >= nr_cpumask_bits)
- return ERR_PTR(-EINVAL);
-
- /*
- * We could be clever and allow to attach a event to an
- * offline CPU and activate it when the CPU comes up, but
- * that's for later.
- */
- if (!cpu_online(cpu))
- return ERR_PTR(-ENODEV);
-
- cpuctx = &per_cpu(perf_cpu_context, cpu);
- ctx = &cpuctx->ctx;
- get_ctx(ctx);
-
- return ctx;
- }
-
- rcu_read_lock();
- if (!pid)
- task = current;
- else
- task = find_task_by_vpid(pid);
- if (task)
- get_task_struct(task);
- rcu_read_unlock();
-
- if (!task)
- return ERR_PTR(-ESRCH);
-
- /*
- * Can't attach events to a dying task.
- */
- err = -ESRCH;
- if (task->flags & PF_EXITING)
- goto errout;
-
- /* Reuse ptrace permission checks for now. */
- err = -EACCES;
- if (!ptrace_may_access(task, PTRACE_MODE_READ))
- goto errout;
-
- retry:
- ctx = perf_lock_task_context(task, &flags);
- if (ctx) {
- unclone_ctx(ctx);
- raw_spin_unlock_irqrestore(&ctx->lock, flags);
- }
-
- if (!ctx) {
- ctx = kzalloc(sizeof(struct perf_event_context), GFP_KERNEL);
- err = -ENOMEM;
- if (!ctx)
- goto errout;
- __perf_event_init_context(ctx, task);
- get_ctx(ctx);
- if (cmpxchg(&task->perf_event_ctxp, NULL, ctx)) {
- /*
- * We raced with some other task; use
- * the context they set.
- */
- kfree(ctx);
- goto retry;
- }
- get_task_struct(task);
- }
-
- put_task_struct(task);
- return ctx;
-
- errout:
- put_task_struct(task);
- return ERR_PTR(err);
-}
-
-static void perf_event_free_filter(struct perf_event *event);
-
-static void free_event_rcu(struct rcu_head *head)
-{
- struct perf_event *event;
-
- event = container_of(head, struct perf_event, rcu_head);
- if (event->ns)
- put_pid_ns(event->ns);
- perf_event_free_filter(event);
- kfree(event);
-}
-
-static void perf_pending_sync(struct perf_event *event);
-static void perf_buffer_put(struct perf_buffer *buffer);
-
-static void free_event(struct perf_event *event)
-{
- perf_pending_sync(event);
-
- if (!event->parent) {
- atomic_dec(&nr_events);
- if (event->attr.mmap || event->attr.mmap_data)
- atomic_dec(&nr_mmap_events);
- if (event->attr.comm)
- atomic_dec(&nr_comm_events);
- if (event->attr.task)
- atomic_dec(&nr_task_events);
- }
-
- if (event->buffer) {
- perf_buffer_put(event->buffer);
- event->buffer = NULL;
- }
-
- if (event->destroy)
- event->destroy(event);
-
- put_ctx(event->ctx);
- call_rcu(&event->rcu_head, free_event_rcu);
-}
-
-int perf_event_release_kernel(struct perf_event *event)
-{
- struct perf_event_context *ctx = event->ctx;
-
- /*
- * Remove from the PMU, can't get re-enabled since we got
- * here because the last ref went.
- */
- perf_event_disable(event);
-
- WARN_ON_ONCE(ctx->parent_ctx);
- /*
- * There are two ways this annotation is useful:
- *
- * 1) there is a lock recursion from perf_event_exit_task
- * see the comment there.
- *
- * 2) there is a lock-inversion with mmap_sem through
- * perf_event_read_group(), which takes faults while
- * holding ctx->mutex, however this is called after
- * the last filedesc died, so there is no possibility
- * to trigger the AB-BA case.
- */
- mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING);
- raw_spin_lock_irq(&ctx->lock);
- perf_group_detach(event);
- list_del_event(event, ctx);
- raw_spin_unlock_irq(&ctx->lock);
- mutex_unlock(&ctx->mutex);
-
- mutex_lock(&event->owner->perf_event_mutex);
- list_del_init(&event->owner_entry);
- mutex_unlock(&event->owner->perf_event_mutex);
- put_task_struct(event->owner);
-
- free_event(event);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(perf_event_release_kernel);
-
-/*
- * Called when the last reference to the file is gone.
- */
-static int perf_release(struct inode *inode, struct file *file)
-{
- struct perf_event *event = file->private_data;
-
- file->private_data = NULL;
-
- return perf_event_release_kernel(event);
-}
-
-static int perf_event_read_size(struct perf_event *event)
-{
- int entry = sizeof(u64); /* value */
- int size = 0;
- int nr = 1;
-
- if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- size += sizeof(u64);
-
- if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- size += sizeof(u64);
-
- if (event->attr.read_format & PERF_FORMAT_ID)
- entry += sizeof(u64);
-
- if (event->attr.read_format & PERF_FORMAT_GROUP) {
- nr += event->group_leader->nr_siblings;
- size += sizeof(u64);
- }
-
- size += entry * nr;
-
- return size;
-}
-
-u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
-{
- struct perf_event *child;
- u64 total = 0;
-
- *enabled = 0;
- *running = 0;
-
- mutex_lock(&event->child_mutex);
- total += perf_event_read(event);
- *enabled += event->total_time_enabled +
- atomic64_read(&event->child_total_time_enabled);
- *running += event->total_time_running +
- atomic64_read(&event->child_total_time_running);
-
- list_for_each_entry(child, &event->child_list, child_list) {
- total += perf_event_read(child);
- *enabled += child->total_time_enabled;
- *running += child->total_time_running;
- }
- mutex_unlock(&event->child_mutex);
-
- return total;
-}
-EXPORT_SYMBOL_GPL(perf_event_read_value);
-
-static int perf_event_read_group(struct perf_event *event,
- u64 read_format, char __user *buf)
-{
- struct perf_event *leader = event->group_leader, *sub;
- int n = 0, size = 0, ret = -EFAULT;
- struct perf_event_context *ctx = leader->ctx;
- u64 values[5];
- u64 count, enabled, running;
-
- mutex_lock(&ctx->mutex);
- count = perf_event_read_value(leader, &enabled, &running);
-
- values[n++] = 1 + leader->nr_siblings;
- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- values[n++] = enabled;
- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- values[n++] = running;
- values[n++] = count;
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(leader);
-
- size = n * sizeof(u64);
-
- if (copy_to_user(buf, values, size))
- goto unlock;
-
- ret = size;
-
- list_for_each_entry(sub, &leader->sibling_list, group_entry) {
- n = 0;
-
- values[n++] = perf_event_read_value(sub, &enabled, &running);
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(sub);
-
- size = n * sizeof(u64);
-
- if (copy_to_user(buf + ret, values, size)) {
- ret = -EFAULT;
- goto unlock;
- }
-
- ret += size;
- }
-unlock:
- mutex_unlock(&ctx->mutex);
-
- return ret;
-}
-
-static int perf_event_read_one(struct perf_event *event,
- u64 read_format, char __user *buf)
-{
- u64 enabled, running;
- u64 values[4];
- int n = 0;
-
- values[n++] = perf_event_read_value(event, &enabled, &running);
- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- values[n++] = enabled;
- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- values[n++] = running;
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(event);
-
- if (copy_to_user(buf, values, n * sizeof(u64)))
- return -EFAULT;
-
- return n * sizeof(u64);
-}
-
-/*
- * Read the performance event - simple non blocking version for now
- */
-static ssize_t
-perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
-{
- u64 read_format = event->attr.read_format;
- int ret;
-
- /*
- * Return end-of-file for a read on a event that is in
- * error state (i.e. because it was pinned but it couldn't be
- * scheduled on to the CPU at some point).
- */
- if (event->state == PERF_EVENT_STATE_ERROR)
- return 0;
-
- if (count < perf_event_read_size(event))
- return -ENOSPC;
-
- WARN_ON_ONCE(event->ctx->parent_ctx);
- if (read_format & PERF_FORMAT_GROUP)
- ret = perf_event_read_group(event, read_format, buf);
- else
- ret = perf_event_read_one(event, read_format, buf);
-
- return ret;
-}
-
-static ssize_t
-perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
- struct perf_event *event = file->private_data;
-
- return perf_read_hw(event, buf, count);
-}
-
-static unsigned int perf_poll(struct file *file, poll_table *wait)
-{
- struct perf_event *event = file->private_data;
- struct perf_buffer *buffer;
- unsigned int events = POLL_HUP;
-
- rcu_read_lock();
- buffer = rcu_dereference(event->buffer);
- if (buffer)
- events = atomic_xchg(&buffer->poll, 0);
- rcu_read_unlock();
-
- poll_wait(file, &event->waitq, wait);
-
- return events;
-}
-
-static void perf_event_reset(struct perf_event *event)
-{
- (void)perf_event_read(event);
- local64_set(&event->count, 0);
- perf_event_update_userpage(event);
-}
-
-/*
- * Holding the top-level event's child_mutex means that any
- * descendant process that has inherited this event will block
- * in sync_child_event if it goes to exit, thus satisfying the
- * task existence requirements of perf_event_enable/disable.
- */
-static void perf_event_for_each_child(struct perf_event *event,
- void (*func)(struct perf_event *))
-{
- struct perf_event *child;
-
- WARN_ON_ONCE(event->ctx->parent_ctx);
- mutex_lock(&event->child_mutex);
- func(event);
- list_for_each_entry(child, &event->child_list, child_list)
- func(child);
- mutex_unlock(&event->child_mutex);
-}
-
-static void perf_event_for_each(struct perf_event *event,
- void (*func)(struct perf_event *))
-{
- struct perf_event_context *ctx = event->ctx;
- struct perf_event *sibling;
-
- WARN_ON_ONCE(ctx->parent_ctx);
- mutex_lock(&ctx->mutex);
- event = event->group_leader;
-
- perf_event_for_each_child(event, func);
- func(event);
- list_for_each_entry(sibling, &event->sibling_list, group_entry)
- perf_event_for_each_child(event, func);
- mutex_unlock(&ctx->mutex);
-}
-
-static int perf_event_period(struct perf_event *event, u64 __user *arg)
-{
- struct perf_event_context *ctx = event->ctx;
- int ret = 0;
- u64 value;
-
- if (!event->attr.sample_period)
- return -EINVAL;
-
- if (copy_from_user(&value, arg, sizeof(value)))
- return -EFAULT;
-
- if (!value)
- return -EINVAL;
-
- raw_spin_lock_irq(&ctx->lock);
- if (event->attr.freq) {
- if (value > sysctl_perf_event_sample_rate) {
- ret = -EINVAL;
- goto unlock;
- }
-
- event->attr.sample_freq = value;
- } else {
- event->attr.sample_period = value;
- event->hw.sample_period = value;
- }
-unlock:
- raw_spin_unlock_irq(&ctx->lock);
-
- return ret;
-}
-
-static const struct file_operations perf_fops;
-
-static struct perf_event *perf_fget_light(int fd, int *fput_needed)
-{
- struct file *file;
-
- file = fget_light(fd, fput_needed);
- if (!file)
- return ERR_PTR(-EBADF);
-
- if (file->f_op != &perf_fops) {
- fput_light(file, *fput_needed);
- *fput_needed = 0;
- return ERR_PTR(-EBADF);
- }
-
- return file->private_data;
-}
-
-static int perf_event_set_output(struct perf_event *event,
- struct perf_event *output_event);
-static int perf_event_set_filter(struct perf_event *event, void __user *arg);
-
-static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct perf_event *event = file->private_data;
- void (*func)(struct perf_event *);
- u32 flags = arg;
-
- switch (cmd) {
- case PERF_EVENT_IOC_ENABLE:
- func = perf_event_enable;
- break;
- case PERF_EVENT_IOC_DISABLE:
- func = perf_event_disable;
- break;
- case PERF_EVENT_IOC_RESET:
- func = perf_event_reset;
- break;
-
- case PERF_EVENT_IOC_REFRESH:
- return perf_event_refresh(event, arg);
-
- case PERF_EVENT_IOC_PERIOD:
- return perf_event_period(event, (u64 __user *)arg);
-
- case PERF_EVENT_IOC_SET_OUTPUT:
- {
- struct perf_event *output_event = NULL;
- int fput_needed = 0;
- int ret;
-
- if (arg != -1) {
- output_event = perf_fget_light(arg, &fput_needed);
- if (IS_ERR(output_event))
- return PTR_ERR(output_event);
- }
-
- ret = perf_event_set_output(event, output_event);
- if (output_event)
- fput_light(output_event->filp, fput_needed);
-
- return ret;
- }
-
- case PERF_EVENT_IOC_SET_FILTER:
- return perf_event_set_filter(event, (void __user *)arg);
-
- default:
- return -ENOTTY;
- }
-
- if (flags & PERF_IOC_FLAG_GROUP)
- perf_event_for_each(event, func);
- else
- perf_event_for_each_child(event, func);
-
- return 0;
-}
-
-int perf_event_task_enable(void)
-{
- struct perf_event *event;
-
- mutex_lock(¤t->perf_event_mutex);
- list_for_each_entry(event, ¤t->perf_event_list, owner_entry)
- perf_event_for_each_child(event, perf_event_enable);
- mutex_unlock(¤t->perf_event_mutex);
-
- return 0;
-}
-
-int perf_event_task_disable(void)
-{
- struct perf_event *event;
-
- mutex_lock(¤t->perf_event_mutex);
- list_for_each_entry(event, ¤t->perf_event_list, owner_entry)
- perf_event_for_each_child(event, perf_event_disable);
- mutex_unlock(¤t->perf_event_mutex);
-
- return 0;
-}
-
-#ifndef PERF_EVENT_INDEX_OFFSET
-# define PERF_EVENT_INDEX_OFFSET 0
-#endif
-
-static int perf_event_index(struct perf_event *event)
-{
- if (event->state != PERF_EVENT_STATE_ACTIVE)
- return 0;
-
- return event->hw.idx + 1 - PERF_EVENT_INDEX_OFFSET;
-}
-
-/*
- * Callers need to ensure there can be no nesting of this function, otherwise
- * the seqlock logic goes bad. We can not serialize this because the arch
- * code calls this from NMI context.
- */
-void perf_event_update_userpage(struct perf_event *event)
-{
- struct perf_event_mmap_page *userpg;
- struct perf_buffer *buffer;
-
- rcu_read_lock();
- buffer = rcu_dereference(event->buffer);
- if (!buffer)
- goto unlock;
-
- userpg = buffer->user_page;
-
- /*
- * Disable preemption so as to not let the corresponding user-space
- * spin too long if we get preempted.
- */
- preempt_disable();
- ++userpg->lock;
- barrier();
- userpg->index = perf_event_index(event);
- userpg->offset = perf_event_count(event);
- if (event->state == PERF_EVENT_STATE_ACTIVE)
- userpg->offset -= local64_read(&event->hw.prev_count);
-
- userpg->time_enabled = event->total_time_enabled +
- atomic64_read(&event->child_total_time_enabled);
-
- userpg->time_running = event->total_time_running +
- atomic64_read(&event->child_total_time_running);
-
- barrier();
- ++userpg->lock;
- preempt_enable();
-unlock:
- rcu_read_unlock();
-}
-
-static unsigned long perf_data_size(struct perf_buffer *buffer);
-
-static void
-perf_buffer_init(struct perf_buffer *buffer, long watermark, int flags)
-{
- long max_size = perf_data_size(buffer);
-
- if (watermark)
- buffer->watermark = min(max_size, watermark);
-
- if (!buffer->watermark)
- buffer->watermark = max_size / 2;
-
- if (flags & PERF_BUFFER_WRITABLE)
- buffer->writable = 1;
-
- atomic_set(&buffer->refcount, 1);
-}
-
-#ifndef CONFIG_PERF_USE_VMALLOC
-
-/*
- * Back perf_mmap() with regular GFP_KERNEL-0 pages.
- */
-
-static struct page *
-perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff)
-{
- if (pgoff > buffer->nr_pages)
- return NULL;
-
- if (pgoff == 0)
- return virt_to_page(buffer->user_page);
-
- return virt_to_page(buffer->data_pages[pgoff - 1]);
-}
-
-static void *perf_mmap_alloc_page(int cpu)
-{
- struct page *page;
- int node;
-
- node = (cpu == -1) ? cpu : cpu_to_node(cpu);
- page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
- if (!page)
- return NULL;
-
- return page_address(page);
-}
-
-static struct perf_buffer *
-perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
-{
- struct perf_buffer *buffer;
- unsigned long size;
- int i;
-
- size = sizeof(struct perf_buffer);
- size += nr_pages * sizeof(void *);
-
- buffer = kzalloc(size, GFP_KERNEL);
- if (!buffer)
- goto fail;
-
- buffer->user_page = perf_mmap_alloc_page(cpu);
- if (!buffer->user_page)
- goto fail_user_page;
-
- for (i = 0; i < nr_pages; i++) {
- buffer->data_pages[i] = perf_mmap_alloc_page(cpu);
- if (!buffer->data_pages[i])
- goto fail_data_pages;
- }
-
- buffer->nr_pages = nr_pages;
-
- perf_buffer_init(buffer, watermark, flags);
-
- return buffer;
-
-fail_data_pages:
- for (i--; i >= 0; i--)
- free_page((unsigned long)buffer->data_pages[i]);
-
- free_page((unsigned long)buffer->user_page);
-
-fail_user_page:
- kfree(buffer);
-
-fail:
- return NULL;
-}
-
-static void perf_mmap_free_page(unsigned long addr)
-{
- struct page *page = virt_to_page((void *)addr);
-
- page->mapping = NULL;
- __free_page(page);
-}
-
-static void perf_buffer_free(struct perf_buffer *buffer)
-{
- int i;
-
- perf_mmap_free_page((unsigned long)buffer->user_page);
- for (i = 0; i < buffer->nr_pages; i++)
- perf_mmap_free_page((unsigned long)buffer->data_pages[i]);
- kfree(buffer);
-}
-
-static inline int page_order(struct perf_buffer *buffer)
-{
- return 0;
-}
-
-#else
-
-/*
- * Back perf_mmap() with vmalloc memory.
- *
- * Required for architectures that have d-cache aliasing issues.
- */
-
-static inline int page_order(struct perf_buffer *buffer)
-{
- return buffer->page_order;
-}
-
-static struct page *
-perf_mmap_to_page(struct perf_buffer *buffer, unsigned long pgoff)
-{
- if (pgoff > (1UL << page_order(buffer)))
- return NULL;
-
- return vmalloc_to_page((void *)buffer->user_page + pgoff * PAGE_SIZE);
-}
-
-static void perf_mmap_unmark_page(void *addr)
-{
- struct page *page = vmalloc_to_page(addr);
-
- page->mapping = NULL;
-}
-
-static void perf_buffer_free_work(struct work_struct *work)
-{
- struct perf_buffer *buffer;
- void *base;
- int i, nr;
-
- buffer = container_of(work, struct perf_buffer, work);
- nr = 1 << page_order(buffer);
-
- base = buffer->user_page;
- for (i = 0; i < nr + 1; i++)
- perf_mmap_unmark_page(base + (i * PAGE_SIZE));
-
- vfree(base);
- kfree(buffer);
-}
-
-static void perf_buffer_free(struct perf_buffer *buffer)
-{
- schedule_work(&buffer->work);
-}
-
-static struct perf_buffer *
-perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
-{
- struct perf_buffer *buffer;
- unsigned long size;
- void *all_buf;
-
- size = sizeof(struct perf_buffer);
- size += sizeof(void *);
-
- buffer = kzalloc(size, GFP_KERNEL);
- if (!buffer)
- goto fail;
-
- INIT_WORK(&buffer->work, perf_buffer_free_work);
-
- all_buf = vmalloc_user((nr_pages + 1) * PAGE_SIZE);
- if (!all_buf)
- goto fail_all_buf;
-
- buffer->user_page = all_buf;
- buffer->data_pages[0] = all_buf + PAGE_SIZE;
- buffer->page_order = ilog2(nr_pages);
- buffer->nr_pages = 1;
-
- perf_buffer_init(buffer, watermark, flags);
-
- return buffer;
-
-fail_all_buf:
- kfree(buffer);
-
-fail:
- return NULL;
-}
-
-#endif
-
-static unsigned long perf_data_size(struct perf_buffer *buffer)
-{
- return buffer->nr_pages << (PAGE_SHIFT + page_order(buffer));
-}
-
-static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
- struct perf_event *event = vma->vm_file->private_data;
- struct perf_buffer *buffer;
- int ret = VM_FAULT_SIGBUS;
-
- if (vmf->flags & FAULT_FLAG_MKWRITE) {
- if (vmf->pgoff == 0)
- ret = 0;
- return ret;
- }
-
- rcu_read_lock();
- buffer = rcu_dereference(event->buffer);
- if (!buffer)
- goto unlock;
-
- if (vmf->pgoff && (vmf->flags & FAULT_FLAG_WRITE))
- goto unlock;
-
- vmf->page = perf_mmap_to_page(buffer, vmf->pgoff);
- if (!vmf->page)
- goto unlock;
-
- get_page(vmf->page);
- vmf->page->mapping = vma->vm_file->f_mapping;
- vmf->page->index = vmf->pgoff;
-
- ret = 0;
-unlock:
- rcu_read_unlock();
-
- return ret;
-}
-
-static void perf_buffer_free_rcu(struct rcu_head *rcu_head)
-{
- struct perf_buffer *buffer;
-
- buffer = container_of(rcu_head, struct perf_buffer, rcu_head);
- perf_buffer_free(buffer);
-}
-
-static struct perf_buffer *perf_buffer_get(struct perf_event *event)
-{
- struct perf_buffer *buffer;
-
- rcu_read_lock();
- buffer = rcu_dereference(event->buffer);
- if (buffer) {
- if (!atomic_inc_not_zero(&buffer->refcount))
- buffer = NULL;
- }
- rcu_read_unlock();
-
- return buffer;
-}
-
-static void perf_buffer_put(struct perf_buffer *buffer)
-{
- if (!atomic_dec_and_test(&buffer->refcount))
- return;
-
- call_rcu(&buffer->rcu_head, perf_buffer_free_rcu);
-}
-
-static void perf_mmap_open(struct vm_area_struct *vma)
-{
- struct perf_event *event = vma->vm_file->private_data;
-
- atomic_inc(&event->mmap_count);
-}
-
-static void perf_mmap_close(struct vm_area_struct *vma)
-{
- struct perf_event *event = vma->vm_file->private_data;
-
- if (atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) {
- unsigned long size = perf_data_size(event->buffer);
- struct user_struct *user = event->mmap_user;
- struct perf_buffer *buffer = event->buffer;
-
- atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm);
- vma->vm_mm->locked_vm -= event->mmap_locked;
- rcu_assign_pointer(event->buffer, NULL);
- mutex_unlock(&event->mmap_mutex);
-
- perf_buffer_put(buffer);
- free_uid(user);
- }
-}
-
-static const struct vm_operations_struct perf_mmap_vmops = {
- .open = perf_mmap_open,
- .close = perf_mmap_close,
- .fault = perf_mmap_fault,
- .page_mkwrite = perf_mmap_fault,
-};
-
-static int perf_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct perf_event *event = file->private_data;
- unsigned long user_locked, user_lock_limit;
- struct user_struct *user = current_user();
- unsigned long locked, lock_limit;
- struct perf_buffer *buffer;
- unsigned long vma_size;
- unsigned long nr_pages;
- long user_extra, extra;
- int ret = 0, flags = 0;
-
- /*
- * Don't allow mmap() of inherited per-task counters. This would
- * create a performance issue due to all children writing to the
- * same buffer.
- */
- if (event->cpu == -1 && event->attr.inherit)
- return -EINVAL;
-
- if (!(vma->vm_flags & VM_SHARED))
- return -EINVAL;
-
- vma_size = vma->vm_end - vma->vm_start;
- nr_pages = (vma_size / PAGE_SIZE) - 1;
-
- /*
- * If we have buffer pages ensure they're a power-of-two number, so we
- * can do bitmasks instead of modulo.
- */
- if (nr_pages != 0 && !is_power_of_2(nr_pages))
- return -EINVAL;
-
- if (vma_size != PAGE_SIZE * (1 + nr_pages))
- return -EINVAL;
-
- if (vma->vm_pgoff != 0)
- return -EINVAL;
-
- WARN_ON_ONCE(event->ctx->parent_ctx);
- mutex_lock(&event->mmap_mutex);
- if (event->buffer) {
- if (event->buffer->nr_pages == nr_pages)
- atomic_inc(&event->buffer->refcount);
- else
- ret = -EINVAL;
- goto unlock;
- }
-
- user_extra = nr_pages + 1;
- user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10);
-
- /*
- * Increase the limit linearly with more CPUs:
- */
- user_lock_limit *= num_online_cpus();
-
- user_locked = atomic_long_read(&user->locked_vm) + user_extra;
-
- extra = 0;
- if (user_locked > user_lock_limit)
- extra = user_locked - user_lock_limit;
-
- lock_limit = rlimit(RLIMIT_MEMLOCK);
- lock_limit >>= PAGE_SHIFT;
- locked = vma->vm_mm->locked_vm + extra;
-
- if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() &&
- !capable(CAP_IPC_LOCK)) {
- ret = -EPERM;
- goto unlock;
- }
-
- WARN_ON(event->buffer);
-
- if (vma->vm_flags & VM_WRITE)
- flags |= PERF_BUFFER_WRITABLE;
-
- buffer = perf_buffer_alloc(nr_pages, event->attr.wakeup_watermark,
- event->cpu, flags);
- if (!buffer) {
- ret = -ENOMEM;
- goto unlock;
- }
- rcu_assign_pointer(event->buffer, buffer);
-
- atomic_long_add(user_extra, &user->locked_vm);
- event->mmap_locked = extra;
- event->mmap_user = get_current_user();
- vma->vm_mm->locked_vm += event->mmap_locked;
-
-unlock:
- if (!ret)
- atomic_inc(&event->mmap_count);
- mutex_unlock(&event->mmap_mutex);
-
- vma->vm_flags |= VM_RESERVED;
- vma->vm_ops = &perf_mmap_vmops;
-
- return ret;
-}
-
-static int perf_fasync(int fd, struct file *filp, int on)
-{
- struct inode *inode = filp->f_path.dentry->d_inode;
- struct perf_event *event = filp->private_data;
- int retval;
-
- mutex_lock(&inode->i_mutex);
- retval = fasync_helper(fd, filp, on, &event->fasync);
- mutex_unlock(&inode->i_mutex);
-
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static const struct file_operations perf_fops = {
- .llseek = no_llseek,
- .release = perf_release,
- .read = perf_read,
- .poll = perf_poll,
- .unlocked_ioctl = perf_ioctl,
- .compat_ioctl = perf_ioctl,
- .mmap = perf_mmap,
- .fasync = perf_fasync,
-};
-
-/*
- * Perf event wakeup
- *
- * If there's data, ensure we set the poll() state and publish everything
- * to user-space before waking everybody up.
- */
-
-void perf_event_wakeup(struct perf_event *event)
-{
- wake_up_all(&event->waitq);
-
- if (event->pending_kill) {
- kill_fasync(&event->fasync, SIGIO, event->pending_kill);
- event->pending_kill = 0;
- }
-}
-
-/*
- * Pending wakeups
- *
- * Handle the case where we need to wakeup up from NMI (or rq->lock) context.
- *
- * The NMI bit means we cannot possibly take locks. Therefore, maintain a
- * single linked list and use cmpxchg() to add entries lockless.
- */
-
-static void perf_pending_event(struct perf_pending_entry *entry)
-{
- struct perf_event *event = container_of(entry,
- struct perf_event, pending);
-
- if (event->pending_disable) {
- event->pending_disable = 0;
- __perf_event_disable(event);
- }
-
- if (event->pending_wakeup) {
- event->pending_wakeup = 0;
- perf_event_wakeup(event);
- }
-}
-
-#define PENDING_TAIL ((struct perf_pending_entry *)-1UL)
-
-static DEFINE_PER_CPU(struct perf_pending_entry *, perf_pending_head) = {
- PENDING_TAIL,
-};
-
-static void perf_pending_queue(struct perf_pending_entry *entry,
- void (*func)(struct perf_pending_entry *))
-{
- struct perf_pending_entry **head;
-
- if (cmpxchg(&entry->next, NULL, PENDING_TAIL) != NULL)
- return;
-
- entry->func = func;
-
- head = &get_cpu_var(perf_pending_head);
-
- do {
- entry->next = *head;
- } while (cmpxchg(head, entry->next, entry) != entry->next);
-
- set_perf_event_pending();
-
- put_cpu_var(perf_pending_head);
-}
-
-static int __perf_pending_run(void)
-{
- struct perf_pending_entry *list;
- int nr = 0;
-
- list = xchg(&__get_cpu_var(perf_pending_head), PENDING_TAIL);
- while (list != PENDING_TAIL) {
- void (*func)(struct perf_pending_entry *);
- struct perf_pending_entry *entry = list;
-
- list = list->next;
-
- func = entry->func;
- entry->next = NULL;
- /*
- * Ensure we observe the unqueue before we issue the wakeup,
- * so that we won't be waiting forever.
- * -- see perf_not_pending().
- */
- smp_wmb();
-
- func(entry);
- nr++;
- }
-
- return nr;
-}
-
-static inline int perf_not_pending(struct perf_event *event)
-{
- /*
- * If we flush on whatever cpu we run, there is a chance we don't
- * need to wait.
- */
- get_cpu();
- __perf_pending_run();
- put_cpu();
-
- /*
- * Ensure we see the proper queue state before going to sleep
- * so that we do not miss the wakeup. -- see perf_pending_handle()
- */
- smp_rmb();
- return event->pending.next == NULL;
-}
-
-static void perf_pending_sync(struct perf_event *event)
-{
- wait_event(event->waitq, perf_not_pending(event));
-}
-
-void perf_event_do_pending(void)
-{
- __perf_pending_run();
-}
-
-/*
- * Callchain support -- arch specific
- */
-
-__weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
-{
- return NULL;
-}
-
-
-/*
- * We assume there is only KVM supporting the callbacks.
- * Later on, we might change it to a list if there is
- * another virtualization implementation supporting the callbacks.
- */
-struct perf_guest_info_callbacks *perf_guest_cbs;
-
-int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
-{
- perf_guest_cbs = cbs;
- return 0;
-}
-EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks);
-
-int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
-{
- perf_guest_cbs = NULL;
- return 0;
-}
-EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
-
-/*
- * Output
- */
-static bool perf_output_space(struct perf_buffer *buffer, unsigned long tail,
- unsigned long offset, unsigned long head)
-{
- unsigned long mask;
-
- if (!buffer->writable)
- return true;
-
- mask = perf_data_size(buffer) - 1;
-
- offset = (offset - tail) & mask;
- head = (head - tail) & mask;
-
- if ((int)(head - offset) < 0)
- return false;
-
- return true;
-}
-
-static void perf_output_wakeup(struct perf_output_handle *handle)
-{
- atomic_set(&handle->buffer->poll, POLL_IN);
-
- if (handle->nmi) {
- handle->event->pending_wakeup = 1;
- perf_pending_queue(&handle->event->pending,
- perf_pending_event);
- } else
- perf_event_wakeup(handle->event);
-}
-
-/*
- * We need to ensure a later event_id doesn't publish a head when a former
- * event isn't done writing. However since we need to deal with NMIs we
- * cannot fully serialize things.
- *
- * We only publish the head (and generate a wakeup) when the outer-most
- * event completes.
- */
-static void perf_output_get_handle(struct perf_output_handle *handle)
-{
- struct perf_buffer *buffer = handle->buffer;
-
- preempt_disable();
- local_inc(&buffer->nest);
- handle->wakeup = local_read(&buffer->wakeup);
-}
-
-static void perf_output_put_handle(struct perf_output_handle *handle)
-{
- struct perf_buffer *buffer = handle->buffer;
- unsigned long head;
-
-again:
- head = local_read(&buffer->head);
-
- /*
- * IRQ/NMI can happen here, which means we can miss a head update.
- */
-
- if (!local_dec_and_test(&buffer->nest))
- goto out;
-
- /*
- * Publish the known good head. Rely on the full barrier implied
- * by atomic_dec_and_test() order the buffer->head read and this
- * write.
- */
- buffer->user_page->data_head = head;
-
- /*
- * Now check if we missed an update, rely on the (compiler)
- * barrier in atomic_dec_and_test() to re-read buffer->head.
- */
- if (unlikely(head != local_read(&buffer->head))) {
- local_inc(&buffer->nest);
- goto again;
- }
-
- if (handle->wakeup != local_read(&buffer->wakeup))
- perf_output_wakeup(handle);
-
- out:
- preempt_enable();
-}
-
-__always_inline void perf_output_copy(struct perf_output_handle *handle,
- const void *buf, unsigned int len)
-{
- do {
- unsigned long size = min_t(unsigned long, handle->size, len);
-
- memcpy(handle->addr, buf, size);
-
- len -= size;
- handle->addr += size;
- buf += size;
- handle->size -= size;
- if (!handle->size) {
- struct perf_buffer *buffer = handle->buffer;
-
- handle->page++;
- handle->page &= buffer->nr_pages - 1;
- handle->addr = buffer->data_pages[handle->page];
- handle->size = PAGE_SIZE << page_order(buffer);
- }
- } while (len);
-}
-
-int perf_output_begin(struct perf_output_handle *handle,
- struct perf_event *event, unsigned int size,
- int nmi, int sample)
-{
- struct perf_buffer *buffer;
- unsigned long tail, offset, head;
- int have_lost;
- struct {
- struct perf_event_header header;
- u64 id;
- u64 lost;
- } lost_event;
-
- rcu_read_lock();
- /*
- * For inherited events we send all the output towards the parent.
- */
- if (event->parent)
- event = event->parent;
-
- buffer = rcu_dereference(event->buffer);
- if (!buffer)
- goto out;
-
- handle->buffer = buffer;
- handle->event = event;
- handle->nmi = nmi;
- handle->sample = sample;
-
- if (!buffer->nr_pages)
- goto out;
-
- have_lost = local_read(&buffer->lost);
- if (have_lost)
- size += sizeof(lost_event);
-
- perf_output_get_handle(handle);
-
- do {
- /*
- * Userspace could choose to issue a mb() before updating the
- * tail pointer. So that all reads will be completed before the
- * write is issued.
- */
- tail = ACCESS_ONCE(buffer->user_page->data_tail);
- smp_rmb();
- offset = head = local_read(&buffer->head);
- head += size;
- if (unlikely(!perf_output_space(buffer, tail, offset, head)))
- goto fail;
- } while (local_cmpxchg(&buffer->head, offset, head) != offset);
-
- if (head - local_read(&buffer->wakeup) > buffer->watermark)
- local_add(buffer->watermark, &buffer->wakeup);
-
- handle->page = offset >> (PAGE_SHIFT + page_order(buffer));
- handle->page &= buffer->nr_pages - 1;
- handle->size = offset & ((PAGE_SIZE << page_order(buffer)) - 1);
- handle->addr = buffer->data_pages[handle->page];
- handle->addr += handle->size;
- handle->size = (PAGE_SIZE << page_order(buffer)) - handle->size;
-
- if (have_lost) {
- lost_event.header.type = PERF_RECORD_LOST;
- lost_event.header.misc = 0;
- lost_event.header.size = sizeof(lost_event);
- lost_event.id = event->id;
- lost_event.lost = local_xchg(&buffer->lost, 0);
-
- perf_output_put(handle, lost_event);
- }
-
- return 0;
-
-fail:
- local_inc(&buffer->lost);
- perf_output_put_handle(handle);
-out:
- rcu_read_unlock();
-
- return -ENOSPC;
-}
-
-void perf_output_end(struct perf_output_handle *handle)
-{
- struct perf_event *event = handle->event;
- struct perf_buffer *buffer = handle->buffer;
-
- int wakeup_events = event->attr.wakeup_events;
-
- if (handle->sample && wakeup_events) {
- int events = local_inc_return(&buffer->events);
- if (events >= wakeup_events) {
- local_sub(wakeup_events, &buffer->events);
- local_inc(&buffer->wakeup);
- }
- }
-
- perf_output_put_handle(handle);
- rcu_read_unlock();
-}
-
-static u32 perf_event_pid(struct perf_event *event, struct task_struct *p)
-{
- /*
- * only top level events have the pid namespace they were created in
- */
- if (event->parent)
- event = event->parent;
-
- return task_tgid_nr_ns(p, event->ns);
-}
-
-static u32 perf_event_tid(struct perf_event *event, struct task_struct *p)
-{
- /*
- * only top level events have the pid namespace they were created in
- */
- if (event->parent)
- event = event->parent;
-
- return task_pid_nr_ns(p, event->ns);
-}
-
-static void perf_output_read_one(struct perf_output_handle *handle,
- struct perf_event *event)
-{
- u64 read_format = event->attr.read_format;
- u64 values[4];
- int n = 0;
-
- values[n++] = perf_event_count(event);
- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
- values[n++] = event->total_time_enabled +
- atomic64_read(&event->child_total_time_enabled);
- }
- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
- values[n++] = event->total_time_running +
- atomic64_read(&event->child_total_time_running);
- }
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(event);
-
- perf_output_copy(handle, values, n * sizeof(u64));
-}
-
-/*
- * XXX PERF_FORMAT_GROUP vs inherited events seems difficult.
- */
-static void perf_output_read_group(struct perf_output_handle *handle,
- struct perf_event *event)
-{
- struct perf_event *leader = event->group_leader, *sub;
- u64 read_format = event->attr.read_format;
- u64 values[5];
- int n = 0;
-
- values[n++] = 1 + leader->nr_siblings;
-
- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- values[n++] = leader->total_time_enabled;
-
- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- values[n++] = leader->total_time_running;
-
- if (leader != event)
- leader->pmu->read(leader);
-
- values[n++] = perf_event_count(leader);
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(leader);
-
- perf_output_copy(handle, values, n * sizeof(u64));
-
- list_for_each_entry(sub, &leader->sibling_list, group_entry) {
- n = 0;
-
- if (sub != event)
- sub->pmu->read(sub);
-
- values[n++] = perf_event_count(sub);
- if (read_format & PERF_FORMAT_ID)
- values[n++] = primary_event_id(sub);
-
- perf_output_copy(handle, values, n * sizeof(u64));
- }
-}
-
-static void perf_output_read(struct perf_output_handle *handle,
- struct perf_event *event)
-{
- if (event->attr.read_format & PERF_FORMAT_GROUP)
- perf_output_read_group(handle, event);
- else
- perf_output_read_one(handle, event);
-}
-
-void perf_output_sample(struct perf_output_handle *handle,
- struct perf_event_header *header,
- struct perf_sample_data *data,
- struct perf_event *event)
-{
- u64 sample_type = data->type;
-
- perf_output_put(handle, *header);
-
- if (sample_type & PERF_SAMPLE_IP)
- perf_output_put(handle, data->ip);
-
- if (sample_type & PERF_SAMPLE_TID)
- perf_output_put(handle, data->tid_entry);
-
- if (sample_type & PERF_SAMPLE_TIME)
- perf_output_put(handle, data->time);
-
- if (sample_type & PERF_SAMPLE_ADDR)
- perf_output_put(handle, data->addr);
-
- if (sample_type & PERF_SAMPLE_ID)
- perf_output_put(handle, data->id);
-
- if (sample_type & PERF_SAMPLE_STREAM_ID)
- perf_output_put(handle, data->stream_id);
-
- if (sample_type & PERF_SAMPLE_CPU)
- perf_output_put(handle, data->cpu_entry);
-
- if (sample_type & PERF_SAMPLE_PERIOD)
- perf_output_put(handle, data->period);
-
- if (sample_type & PERF_SAMPLE_READ)
- perf_output_read(handle, event);
-
- if (sample_type & PERF_SAMPLE_CALLCHAIN) {
- if (data->callchain) {
- int size = 1;
-
- if (data->callchain)
- size += data->callchain->nr;
-
- size *= sizeof(u64);
-
- perf_output_copy(handle, data->callchain, size);
- } else {
- u64 nr = 0;
- perf_output_put(handle, nr);
- }
- }
-
- if (sample_type & PERF_SAMPLE_RAW) {
- if (data->raw) {
- perf_output_put(handle, data->raw->size);
- perf_output_copy(handle, data->raw->data,
- data->raw->size);
- } else {
- struct {
- u32 size;
- u32 data;
- } raw = {
- .size = sizeof(u32),
- .data = 0,
- };
- perf_output_put(handle, raw);
- }
- }
-}
-
-void perf_prepare_sample(struct perf_event_header *header,
- struct perf_sample_data *data,
- struct perf_event *event,
- struct pt_regs *regs)
-{
- u64 sample_type = event->attr.sample_type;
-
- data->type = sample_type;
-
- header->type = PERF_RECORD_SAMPLE;
- header->size = sizeof(*header);
-
- header->misc = 0;
- header->misc |= perf_misc_flags(regs);
-
- if (sample_type & PERF_SAMPLE_IP) {
- data->ip = perf_instruction_pointer(regs);
-
- header->size += sizeof(data->ip);
- }
-
- if (sample_type & PERF_SAMPLE_TID) {
- /* namespace issues */
- data->tid_entry.pid = perf_event_pid(event, current);
- data->tid_entry.tid = perf_event_tid(event, current);
-
- header->size += sizeof(data->tid_entry);
- }
-
- if (sample_type & PERF_SAMPLE_TIME) {
- data->time = perf_clock();
-
- header->size += sizeof(data->time);
- }
-
- if (sample_type & PERF_SAMPLE_ADDR)
- header->size += sizeof(data->addr);
-
- if (sample_type & PERF_SAMPLE_ID) {
- data->id = primary_event_id(event);
-
- header->size += sizeof(data->id);
- }
-
- if (sample_type & PERF_SAMPLE_STREAM_ID) {
- data->stream_id = event->id;
-
- header->size += sizeof(data->stream_id);
- }
-
- if (sample_type & PERF_SAMPLE_CPU) {
- data->cpu_entry.cpu = raw_smp_processor_id();
- data->cpu_entry.reserved = 0;
-
- header->size += sizeof(data->cpu_entry);
- }
-
- if (sample_type & PERF_SAMPLE_PERIOD)
- header->size += sizeof(data->period);
-
- if (sample_type & PERF_SAMPLE_READ)
- header->size += perf_event_read_size(event);
-
- if (sample_type & PERF_SAMPLE_CALLCHAIN) {
- int size = 1;
-
- data->callchain = perf_callchain(regs);
-
- if (data->callchain)
- size += data->callchain->nr;
-
- header->size += size * sizeof(u64);
- }
-
- if (sample_type & PERF_SAMPLE_RAW) {
- int size = sizeof(u32);
-
- if (data->raw)
- size += data->raw->size;
- else
- size += sizeof(u32);
-
- WARN_ON_ONCE(size & (sizeof(u64)-1));
- header->size += size;
- }
-}
-
-static void perf_event_output(struct perf_event *event, int nmi,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- struct perf_output_handle handle;
- struct perf_event_header header;
-
- perf_prepare_sample(&header, data, event, regs);
-
- if (perf_output_begin(&handle, event, header.size, nmi, 1))
- return;
-
- perf_output_sample(&handle, &header, data, event);
-
- perf_output_end(&handle);
-}
-
-/*
- * read event_id
- */
-
-struct perf_read_event {
- struct perf_event_header header;
-
- u32 pid;
- u32 tid;
-};
-
-static void
-perf_event_read_event(struct perf_event *event,
- struct task_struct *task)
-{
- struct perf_output_handle handle;
- struct perf_read_event read_event = {
- .header = {
- .type = PERF_RECORD_READ,
- .misc = 0,
- .size = sizeof(read_event) + perf_event_read_size(event),
- },
- .pid = perf_event_pid(event, task),
- .tid = perf_event_tid(event, task),
- };
- int ret;
-
- ret = perf_output_begin(&handle, event, read_event.header.size, 0, 0);
- if (ret)
- return;
-
- perf_output_put(&handle, read_event);
- perf_output_read(&handle, event);
-
- perf_output_end(&handle);
-}
-
-/*
- * task tracking -- fork/exit
- *
- * enabled by: attr.comm | attr.mmap | attr.mmap_data | attr.task
- */
-
-struct perf_task_event {
- struct task_struct *task;
- struct perf_event_context *task_ctx;
-
- struct {
- struct perf_event_header header;
-
- u32 pid;
- u32 ppid;
- u32 tid;
- u32 ptid;
- u64 time;
- } event_id;
-};
-
-static void perf_event_task_output(struct perf_event *event,
- struct perf_task_event *task_event)
-{
- struct perf_output_handle handle;
- struct task_struct *task = task_event->task;
- int size, ret;
-
- size = task_event->event_id.header.size;
- ret = perf_output_begin(&handle, event, size, 0, 0);
-
- if (ret)
- return;
-
- task_event->event_id.pid = perf_event_pid(event, task);
- task_event->event_id.ppid = perf_event_pid(event, current);
-
- task_event->event_id.tid = perf_event_tid(event, task);
- task_event->event_id.ptid = perf_event_tid(event, current);
-
- perf_output_put(&handle, task_event->event_id);
-
- perf_output_end(&handle);
-}
-
-static int perf_event_task_match(struct perf_event *event)
-{
- if (event->state < PERF_EVENT_STATE_INACTIVE)
- return 0;
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- return 0;
-
- if (event->attr.comm || event->attr.mmap ||
- event->attr.mmap_data || event->attr.task)
- return 1;
-
- return 0;
-}
-
-static void perf_event_task_ctx(struct perf_event_context *ctx,
- struct perf_task_event *task_event)
-{
- struct perf_event *event;
-
- list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
- if (perf_event_task_match(event))
- perf_event_task_output(event, task_event);
- }
-}
-
-static void perf_event_task_event(struct perf_task_event *task_event)
-{
- struct perf_cpu_context *cpuctx;
- struct perf_event_context *ctx = task_event->task_ctx;
-
- rcu_read_lock();
- cpuctx = &get_cpu_var(perf_cpu_context);
- perf_event_task_ctx(&cpuctx->ctx, task_event);
- if (!ctx)
- ctx = rcu_dereference(current->perf_event_ctxp);
- if (ctx)
- perf_event_task_ctx(ctx, task_event);
- put_cpu_var(perf_cpu_context);
- rcu_read_unlock();
-}
-
-static void perf_event_task(struct task_struct *task,
- struct perf_event_context *task_ctx,
- int new)
-{
- struct perf_task_event task_event;
-
- if (!atomic_read(&nr_comm_events) &&
- !atomic_read(&nr_mmap_events) &&
- !atomic_read(&nr_task_events))
- return;
-
- task_event = (struct perf_task_event){
- .task = task,
- .task_ctx = task_ctx,
- .event_id = {
- .header = {
- .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT,
- .misc = 0,
- .size = sizeof(task_event.event_id),
- },
- /* .pid */
- /* .ppid */
- /* .tid */
- /* .ptid */
- .time = perf_clock(),
- },
- };
-
- perf_event_task_event(&task_event);
-}
-
-void perf_event_fork(struct task_struct *task)
-{
- perf_event_task(task, NULL, 1);
-}
-
-/*
- * comm tracking
- */
-
-struct perf_comm_event {
- struct task_struct *task;
- char *comm;
- int comm_size;
-
- struct {
- struct perf_event_header header;
-
- u32 pid;
- u32 tid;
- } event_id;
-};
-
-static void perf_event_comm_output(struct perf_event *event,
- struct perf_comm_event *comm_event)
-{
- struct perf_output_handle handle;
- int size = comm_event->event_id.header.size;
- int ret = perf_output_begin(&handle, event, size, 0, 0);
-
- if (ret)
- return;
-
- comm_event->event_id.pid = perf_event_pid(event, comm_event->task);
- comm_event->event_id.tid = perf_event_tid(event, comm_event->task);
-
- perf_output_put(&handle, comm_event->event_id);
- perf_output_copy(&handle, comm_event->comm,
- comm_event->comm_size);
- perf_output_end(&handle);
-}
-
-static int perf_event_comm_match(struct perf_event *event)
-{
- if (event->state < PERF_EVENT_STATE_INACTIVE)
- return 0;
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- return 0;
-
- if (event->attr.comm)
- return 1;
-
- return 0;
-}
-
-static void perf_event_comm_ctx(struct perf_event_context *ctx,
- struct perf_comm_event *comm_event)
-{
- struct perf_event *event;
-
- list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
- if (perf_event_comm_match(event))
- perf_event_comm_output(event, comm_event);
- }
-}
-
-static void perf_event_comm_event(struct perf_comm_event *comm_event)
-{
- struct perf_cpu_context *cpuctx;
- struct perf_event_context *ctx;
- unsigned int size;
- char comm[TASK_COMM_LEN];
-
- memset(comm, 0, sizeof(comm));
- strlcpy(comm, comm_event->task->comm, sizeof(comm));
- size = ALIGN(strlen(comm)+1, sizeof(u64));
-
- comm_event->comm = comm;
- comm_event->comm_size = size;
-
- comm_event->event_id.header.size = sizeof(comm_event->event_id) + size;
-
- rcu_read_lock();
- cpuctx = &get_cpu_var(perf_cpu_context);
- perf_event_comm_ctx(&cpuctx->ctx, comm_event);
- ctx = rcu_dereference(current->perf_event_ctxp);
- if (ctx)
- perf_event_comm_ctx(ctx, comm_event);
- put_cpu_var(perf_cpu_context);
- rcu_read_unlock();
-}
-
-void perf_event_comm(struct task_struct *task)
-{
- struct perf_comm_event comm_event;
-
- if (task->perf_event_ctxp)
- perf_event_enable_on_exec(task);
-
- if (!atomic_read(&nr_comm_events))
- return;
-
- comm_event = (struct perf_comm_event){
- .task = task,
- /* .comm */
- /* .comm_size */
- .event_id = {
- .header = {
- .type = PERF_RECORD_COMM,
- .misc = 0,
- /* .size */
- },
- /* .pid */
- /* .tid */
- },
- };
-
- perf_event_comm_event(&comm_event);
-}
-
-/*
- * mmap tracking
- */
-
-struct perf_mmap_event {
- struct vm_area_struct *vma;
-
- const char *file_name;
- int file_size;
-
- struct {
- struct perf_event_header header;
-
- u32 pid;
- u32 tid;
- u64 start;
- u64 len;
- u64 pgoff;
- } event_id;
-};
-
-static void perf_event_mmap_output(struct perf_event *event,
- struct perf_mmap_event *mmap_event)
-{
- struct perf_output_handle handle;
- int size = mmap_event->event_id.header.size;
- int ret = perf_output_begin(&handle, event, size, 0, 0);
-
- if (ret)
- return;
-
- mmap_event->event_id.pid = perf_event_pid(event, current);
- mmap_event->event_id.tid = perf_event_tid(event, current);
-
- perf_output_put(&handle, mmap_event->event_id);
- perf_output_copy(&handle, mmap_event->file_name,
- mmap_event->file_size);
- perf_output_end(&handle);
-}
-
-static int perf_event_mmap_match(struct perf_event *event,
- struct perf_mmap_event *mmap_event,
- int executable)
-{
- if (event->state < PERF_EVENT_STATE_INACTIVE)
- return 0;
-
- if (event->cpu != -1 && event->cpu != smp_processor_id())
- return 0;
-
- if ((!executable && event->attr.mmap_data) ||
- (executable && event->attr.mmap))
- return 1;
-
- return 0;
-}
-
-static void perf_event_mmap_ctx(struct perf_event_context *ctx,
- struct perf_mmap_event *mmap_event,
- int executable)
-{
- struct perf_event *event;
-
- list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
- if (perf_event_mmap_match(event, mmap_event, executable))
- perf_event_mmap_output(event, mmap_event);
- }
-}
-
-static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
-{
- struct perf_cpu_context *cpuctx;
- struct perf_event_context *ctx;
- struct vm_area_struct *vma = mmap_event->vma;
- struct file *file = vma->vm_file;
- unsigned int size;
- char tmp[16];
- char *buf = NULL;
- const char *name;
-
- memset(tmp, 0, sizeof(tmp));
-
- if (file) {
- /*
- * d_path works from the end of the buffer backwards, so we
- * need to add enough zero bytes after the string to handle
- * the 64bit alignment we do later.
- */
- buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL);
- if (!buf) {
- name = strncpy(tmp, "//enomem", sizeof(tmp));
- goto got_name;
- }
- name = d_path(&file->f_path, buf, PATH_MAX);
- if (IS_ERR(name)) {
- name = strncpy(tmp, "//toolong", sizeof(tmp));
- goto got_name;
- }
- } else {
- if (arch_vma_name(mmap_event->vma)) {
- name = strncpy(tmp, arch_vma_name(mmap_event->vma),
- sizeof(tmp));
- goto got_name;
- }
-
- if (!vma->vm_mm) {
- name = strncpy(tmp, "[vdso]", sizeof(tmp));
- goto got_name;
- } else if (vma->vm_start <= vma->vm_mm->start_brk &&
- vma->vm_end >= vma->vm_mm->brk) {
- name = strncpy(tmp, "[heap]", sizeof(tmp));
- goto got_name;
- } else if (vma->vm_start <= vma->vm_mm->start_stack &&
- vma->vm_end >= vma->vm_mm->start_stack) {
- name = strncpy(tmp, "[stack]", sizeof(tmp));
- goto got_name;
- }
-
- name = strncpy(tmp, "//anon", sizeof(tmp));
- goto got_name;
- }
-
-got_name:
- size = ALIGN(strlen(name)+1, sizeof(u64));
-
- mmap_event->file_name = name;
- mmap_event->file_size = size;
-
- mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size;
-
- rcu_read_lock();
- cpuctx = &get_cpu_var(perf_cpu_context);
- perf_event_mmap_ctx(&cpuctx->ctx, mmap_event, vma->vm_flags & VM_EXEC);
- ctx = rcu_dereference(current->perf_event_ctxp);
- if (ctx)
- perf_event_mmap_ctx(ctx, mmap_event, vma->vm_flags & VM_EXEC);
- put_cpu_var(perf_cpu_context);
- rcu_read_unlock();
-
- kfree(buf);
-}
-
-void perf_event_mmap(struct vm_area_struct *vma)
-{
- struct perf_mmap_event mmap_event;
-
- if (!atomic_read(&nr_mmap_events))
- return;
-
- mmap_event = (struct perf_mmap_event){
- .vma = vma,
- /* .file_name */
- /* .file_size */
- .event_id = {
- .header = {
- .type = PERF_RECORD_MMAP,
- .misc = PERF_RECORD_MISC_USER,
- /* .size */
- },
- /* .pid */
- /* .tid */
- .start = vma->vm_start,
- .len = vma->vm_end - vma->vm_start,
- .pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT,
- },
- };
-
- perf_event_mmap_event(&mmap_event);
-}
-
-/*
- * IRQ throttle logging
- */
-
-static void perf_log_throttle(struct perf_event *event, int enable)
-{
- struct perf_output_handle handle;
- int ret;
-
- struct {
- struct perf_event_header header;
- u64 time;
- u64 id;
- u64 stream_id;
- } throttle_event = {
- .header = {
- .type = PERF_RECORD_THROTTLE,
- .misc = 0,
- .size = sizeof(throttle_event),
- },
- .time = perf_clock(),
- .id = primary_event_id(event),
- .stream_id = event->id,
- };
-
- if (enable)
- throttle_event.header.type = PERF_RECORD_UNTHROTTLE;
-
- ret = perf_output_begin(&handle, event, sizeof(throttle_event), 1, 0);
- if (ret)
- return;
-
- perf_output_put(&handle, throttle_event);
- perf_output_end(&handle);
-}
-
-/*
- * Generic event overflow handling, sampling.
- */
-
-static int __perf_event_overflow(struct perf_event *event, int nmi,
- int throttle, struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- int events = atomic_read(&event->event_limit);
- struct hw_perf_event *hwc = &event->hw;
- int ret = 0;
-
- throttle = (throttle && event->pmu->unthrottle != NULL);
-
- if (!throttle) {
- hwc->interrupts++;
- } else {
- if (hwc->interrupts != MAX_INTERRUPTS) {
- hwc->interrupts++;
- if (HZ * hwc->interrupts >
- (u64)sysctl_perf_event_sample_rate) {
- hwc->interrupts = MAX_INTERRUPTS;
- perf_log_throttle(event, 0);
- ret = 1;
- }
- } else {
- /*
- * Keep re-disabling events even though on the previous
- * pass we disabled it - just in case we raced with a
- * sched-in and the event got enabled again:
- */
- ret = 1;
- }
- }
-
- if (event->attr.freq) {
- u64 now = perf_clock();
- s64 delta = now - hwc->freq_time_stamp;
-
- hwc->freq_time_stamp = now;
-
- if (delta > 0 && delta < 2*TICK_NSEC)
- perf_adjust_period(event, delta, hwc->last_period);
- }
-
- /*
- * XXX event_limit might not quite work as expected on inherited
- * events
- */
-
- event->pending_kill = POLL_IN;
- if (events && atomic_dec_and_test(&event->event_limit)) {
- ret = 1;
- event->pending_kill = POLL_HUP;
- if (nmi) {
- event->pending_disable = 1;
- perf_pending_queue(&event->pending,
- perf_pending_event);
- } else
- perf_event_disable(event);
- }
-
- if (event->overflow_handler)
- event->overflow_handler(event, nmi, data, regs);
- else
- perf_event_output(event, nmi, data, regs);
-
- return ret;
-}
-
-int perf_event_overflow(struct perf_event *event, int nmi,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- return __perf_event_overflow(event, nmi, 1, data, regs);
-}
-
-/*
- * Generic software event infrastructure
- */
-
-/*
- * We directly increment event->count and keep a second value in
- * event->hw.period_left to count intervals. This period event
- * is kept in the range [-sample_period, 0] so that we can use the
- * sign as trigger.
- */
-
-static u64 perf_swevent_set_period(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- u64 period = hwc->last_period;
- u64 nr, offset;
- s64 old, val;
-
- hwc->last_period = hwc->sample_period;
-
-again:
- old = val = local64_read(&hwc->period_left);
- if (val < 0)
- return 0;
-
- nr = div64_u64(period + val, period);
- offset = nr * period;
- val -= offset;
- if (local64_cmpxchg(&hwc->period_left, old, val) != old)
- goto again;
-
- return nr;
-}
-
-static void perf_swevent_overflow(struct perf_event *event, u64 overflow,
- int nmi, struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- struct hw_perf_event *hwc = &event->hw;
- int throttle = 0;
-
- data->period = event->hw.last_period;
- if (!overflow)
- overflow = perf_swevent_set_period(event);
-
- if (hwc->interrupts == MAX_INTERRUPTS)
- return;
-
- for (; overflow; overflow--) {
- if (__perf_event_overflow(event, nmi, throttle,
- data, regs)) {
- /*
- * We inhibit the overflow from happening when
- * hwc->interrupts == MAX_INTERRUPTS.
- */
- break;
- }
- throttle = 1;
- }
-}
-
-static void perf_swevent_add(struct perf_event *event, u64 nr,
- int nmi, struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- local64_add(nr, &event->count);
-
- if (!regs)
- return;
-
- if (!hwc->sample_period)
- return;
-
- if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
- return perf_swevent_overflow(event, 1, nmi, data, regs);
-
- if (local64_add_negative(nr, &hwc->period_left))
- return;
-
- perf_swevent_overflow(event, 0, nmi, data, regs);
-}
-
-static int perf_exclude_event(struct perf_event *event,
- struct pt_regs *regs)
-{
- if (regs) {
- if (event->attr.exclude_user && user_mode(regs))
- return 1;
-
- if (event->attr.exclude_kernel && !user_mode(regs))
- return 1;
- }
-
- return 0;
-}
-
-static int perf_swevent_match(struct perf_event *event,
- enum perf_type_id type,
- u32 event_id,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- if (event->attr.type != type)
- return 0;
-
- if (event->attr.config != event_id)
- return 0;
-
- if (perf_exclude_event(event, regs))
- return 0;
-
- return 1;
-}
-
-static inline u64 swevent_hash(u64 type, u32 event_id)
-{
- u64 val = event_id | (type << 32);
-
- return hash_64(val, SWEVENT_HLIST_BITS);
-}
-
-static inline struct hlist_head *
-__find_swevent_head(struct swevent_hlist *hlist, u64 type, u32 event_id)
-{
- u64 hash = swevent_hash(type, event_id);
-
- return &hlist->heads[hash];
-}
-
-/* For the read side: events when they trigger */
-static inline struct hlist_head *
-find_swevent_head_rcu(struct perf_cpu_context *ctx, u64 type, u32 event_id)
-{
- struct swevent_hlist *hlist;
-
- hlist = rcu_dereference(ctx->swevent_hlist);
- if (!hlist)
- return NULL;
-
- return __find_swevent_head(hlist, type, event_id);
-}
-
-/* For the event head insertion and removal in the hlist */
-static inline struct hlist_head *
-find_swevent_head(struct perf_cpu_context *ctx, struct perf_event *event)
-{
- struct swevent_hlist *hlist;
- u32 event_id = event->attr.config;
- u64 type = event->attr.type;
-
- /*
- * Event scheduling is always serialized against hlist allocation
- * and release. Which makes the protected version suitable here.
- * The context lock guarantees that.
- */
- hlist = rcu_dereference_protected(ctx->swevent_hlist,
- lockdep_is_held(&event->ctx->lock));
- if (!hlist)
- return NULL;
-
- return __find_swevent_head(hlist, type, event_id);
-}
-
-static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
- u64 nr, int nmi,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- struct perf_cpu_context *cpuctx;
- struct perf_event *event;
- struct hlist_node *node;
- struct hlist_head *head;
-
- cpuctx = &__get_cpu_var(perf_cpu_context);
-
- rcu_read_lock();
-
- head = find_swevent_head_rcu(cpuctx, type, event_id);
-
- if (!head)
- goto end;
-
- hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
- if (perf_swevent_match(event, type, event_id, data, regs))
- perf_swevent_add(event, nr, nmi, data, regs);
- }
-end:
- rcu_read_unlock();
-}
-
-int perf_swevent_get_recursion_context(void)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- int rctx;
-
- if (in_nmi())
- rctx = 3;
- else if (in_irq())
- rctx = 2;
- else if (in_softirq())
- rctx = 1;
- else
- rctx = 0;
-
- if (cpuctx->recursion[rctx])
- return -1;
-
- cpuctx->recursion[rctx]++;
- barrier();
-
- return rctx;
-}
-EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context);
-
-void inline perf_swevent_put_recursion_context(int rctx)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- barrier();
- cpuctx->recursion[rctx]--;
-}
-
-void __perf_sw_event(u32 event_id, u64 nr, int nmi,
- struct pt_regs *regs, u64 addr)
-{
- struct perf_sample_data data;
- int rctx;
-
- preempt_disable_notrace();
- rctx = perf_swevent_get_recursion_context();
- if (rctx < 0)
- return;
-
- perf_sample_data_init(&data, addr);
-
- do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, nmi, &data, regs);
-
- perf_swevent_put_recursion_context(rctx);
- preempt_enable_notrace();
-}
-
-static void perf_swevent_read(struct perf_event *event)
-{
-}
-
-static int perf_swevent_enable(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- struct perf_cpu_context *cpuctx;
- struct hlist_head *head;
-
- cpuctx = &__get_cpu_var(perf_cpu_context);
-
- if (hwc->sample_period) {
- hwc->last_period = hwc->sample_period;
- perf_swevent_set_period(event);
- }
-
- head = find_swevent_head(cpuctx, event);
- if (WARN_ON_ONCE(!head))
- return -EINVAL;
-
- hlist_add_head_rcu(&event->hlist_entry, head);
-
- return 0;
-}
-
-static void perf_swevent_disable(struct perf_event *event)
-{
- hlist_del_rcu(&event->hlist_entry);
-}
-
-static void perf_swevent_void(struct perf_event *event)
-{
-}
-
-static int perf_swevent_int(struct perf_event *event)
-{
- return 0;
-}
-
-static const struct pmu perf_ops_generic = {
- .enable = perf_swevent_enable,
- .disable = perf_swevent_disable,
- .start = perf_swevent_int,
- .stop = perf_swevent_void,
- .read = perf_swevent_read,
- .unthrottle = perf_swevent_void, /* hwc->interrupts already reset */
-};
-
-/*
- * hrtimer based swevent callback
- */
-
-static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
-{
- enum hrtimer_restart ret = HRTIMER_RESTART;
- struct perf_sample_data data;
- struct pt_regs *regs;
- struct perf_event *event;
- u64 period;
-
- event = container_of(hrtimer, struct perf_event, hw.hrtimer);
- event->pmu->read(event);
-
- perf_sample_data_init(&data, 0);
- data.period = event->hw.last_period;
- regs = get_irq_regs();
-
- if (regs && !perf_exclude_event(event, regs)) {
- if (!(event->attr.exclude_idle && current->pid == 0))
- if (perf_event_overflow(event, 0, &data, regs))
- ret = HRTIMER_NORESTART;
- }
-
- period = max_t(u64, 10000, event->hw.sample_period);
- hrtimer_forward_now(hrtimer, ns_to_ktime(period));
-
- return ret;
-}
-
-static void perf_swevent_start_hrtimer(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hwc->hrtimer.function = perf_swevent_hrtimer;
- if (hwc->sample_period) {
- u64 period;
-
- if (hwc->remaining) {
- if (hwc->remaining < 0)
- period = 10000;
- else
- period = hwc->remaining;
- hwc->remaining = 0;
- } else {
- period = max_t(u64, 10000, hwc->sample_period);
- }
- __hrtimer_start_range_ns(&hwc->hrtimer,
- ns_to_ktime(period), 0,
- HRTIMER_MODE_REL, 0);
- }
-}
-
-static void perf_swevent_cancel_hrtimer(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- if (hwc->sample_period) {
- ktime_t remaining = hrtimer_get_remaining(&hwc->hrtimer);
- hwc->remaining = ktime_to_ns(remaining);
-
- hrtimer_cancel(&hwc->hrtimer);
- }
-}
-
-/*
- * Software event: cpu wall time clock
- */
-
-static void cpu_clock_perf_event_update(struct perf_event *event)
-{
- int cpu = raw_smp_processor_id();
- s64 prev;
- u64 now;
-
- now = cpu_clock(cpu);
- prev = local64_xchg(&event->hw.prev_count, now);
- local64_add(now - prev, &event->count);
-}
-
-static int cpu_clock_perf_event_enable(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- int cpu = raw_smp_processor_id();
-
- local64_set(&hwc->prev_count, cpu_clock(cpu));
- perf_swevent_start_hrtimer(event);
-
- return 0;
-}
-
-static void cpu_clock_perf_event_disable(struct perf_event *event)
-{
- perf_swevent_cancel_hrtimer(event);
- cpu_clock_perf_event_update(event);
-}
-
-static void cpu_clock_perf_event_read(struct perf_event *event)
-{
- cpu_clock_perf_event_update(event);
-}
-
-static const struct pmu perf_ops_cpu_clock = {
- .enable = cpu_clock_perf_event_enable,
- .disable = cpu_clock_perf_event_disable,
- .read = cpu_clock_perf_event_read,
-};
-
-/*
- * Software event: task time clock
- */
-
-static void task_clock_perf_event_update(struct perf_event *event, u64 now)
-{
- u64 prev;
- s64 delta;
-
- prev = local64_xchg(&event->hw.prev_count, now);
- delta = now - prev;
- local64_add(delta, &event->count);
-}
-
-static int task_clock_perf_event_enable(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- u64 now;
-
- now = event->ctx->time;
-
- local64_set(&hwc->prev_count, now);
-
- perf_swevent_start_hrtimer(event);
-
- return 0;
-}
-
-static void task_clock_perf_event_disable(struct perf_event *event)
-{
- perf_swevent_cancel_hrtimer(event);
- task_clock_perf_event_update(event, event->ctx->time);
-
-}
-
-static void task_clock_perf_event_read(struct perf_event *event)
-{
- u64 time;
-
- if (!in_nmi()) {
- update_context_time(event->ctx);
- time = event->ctx->time;
- } else {
- u64 now = perf_clock();
- u64 delta = now - event->ctx->timestamp;
- time = event->ctx->time + delta;
- }
-
- task_clock_perf_event_update(event, time);
-}
-
-static const struct pmu perf_ops_task_clock = {
- .enable = task_clock_perf_event_enable,
- .disable = task_clock_perf_event_disable,
- .read = task_clock_perf_event_read,
-};
-
-/* Deref the hlist from the update side */
-static inline struct swevent_hlist *
-swevent_hlist_deref(struct perf_cpu_context *cpuctx)
-{
- return rcu_dereference_protected(cpuctx->swevent_hlist,
- lockdep_is_held(&cpuctx->hlist_mutex));
-}
-
-static void swevent_hlist_release_rcu(struct rcu_head *rcu_head)
-{
- struct swevent_hlist *hlist;
-
- hlist = container_of(rcu_head, struct swevent_hlist, rcu_head);
- kfree(hlist);
-}
-
-static void swevent_hlist_release(struct perf_cpu_context *cpuctx)
-{
- struct swevent_hlist *hlist = swevent_hlist_deref(cpuctx);
-
- if (!hlist)
- return;
-
- rcu_assign_pointer(cpuctx->swevent_hlist, NULL);
- call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu);
-}
-
-static void swevent_hlist_put_cpu(struct perf_event *event, int cpu)
-{
- struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
-
- mutex_lock(&cpuctx->hlist_mutex);
-
- if (!--cpuctx->hlist_refcount)
- swevent_hlist_release(cpuctx);
-
- mutex_unlock(&cpuctx->hlist_mutex);
-}
-
-static void swevent_hlist_put(struct perf_event *event)
-{
- int cpu;
-
- if (event->cpu != -1) {
- swevent_hlist_put_cpu(event, event->cpu);
- return;
- }
-
- for_each_possible_cpu(cpu)
- swevent_hlist_put_cpu(event, cpu);
-}
-
-static int swevent_hlist_get_cpu(struct perf_event *event, int cpu)
-{
- struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
- int err = 0;
-
- mutex_lock(&cpuctx->hlist_mutex);
-
- if (!swevent_hlist_deref(cpuctx) && cpu_online(cpu)) {
- struct swevent_hlist *hlist;
-
- hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
- if (!hlist) {
- err = -ENOMEM;
- goto exit;
- }
- rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
- }
- cpuctx->hlist_refcount++;
- exit:
- mutex_unlock(&cpuctx->hlist_mutex);
-
- return err;
-}
-
-static int swevent_hlist_get(struct perf_event *event)
-{
- int err;
- int cpu, failed_cpu;
-
- if (event->cpu != -1)
- return swevent_hlist_get_cpu(event, event->cpu);
-
- get_online_cpus();
- for_each_possible_cpu(cpu) {
- err = swevent_hlist_get_cpu(event, cpu);
- if (err) {
- failed_cpu = cpu;
- goto fail;
- }
- }
- put_online_cpus();
-
- return 0;
- fail:
- for_each_possible_cpu(cpu) {
- if (cpu == failed_cpu)
- break;
- swevent_hlist_put_cpu(event, cpu);
- }
-
- put_online_cpus();
- return err;
-}
-
-#ifdef CONFIG_EVENT_TRACING
-
-static const struct pmu perf_ops_tracepoint = {
- .enable = perf_trace_enable,
- .disable = perf_trace_disable,
- .start = perf_swevent_int,
- .stop = perf_swevent_void,
- .read = perf_swevent_read,
- .unthrottle = perf_swevent_void,
-};
-
-static int perf_tp_filter_match(struct perf_event *event,
- struct perf_sample_data *data)
-{
- void *record = data->raw->data;
-
- if (likely(!event->filter) || filter_match_preds(event->filter, record))
- return 1;
- return 0;
-}
-
-static int perf_tp_event_match(struct perf_event *event,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- /*
- * All tracepoints are from kernel-space.
- */
- if (event->attr.exclude_kernel)
- return 0;
-
- if (!perf_tp_filter_match(event, data))
- return 0;
-
- return 1;
-}
-
-void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
- struct pt_regs *regs, struct hlist_head *head, int rctx)
-{
- struct perf_sample_data data;
- struct perf_event *event;
- struct hlist_node *node;
-
- struct perf_raw_record raw = {
- .size = entry_size,
- .data = record,
- };
-
- perf_sample_data_init(&data, addr);
- data.raw = &raw;
-
- hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
- if (perf_tp_event_match(event, &data, regs))
- perf_swevent_add(event, count, 1, &data, regs);
- }
-
- perf_swevent_put_recursion_context(rctx);
-}
-EXPORT_SYMBOL_GPL(perf_tp_event);
-
-static void tp_perf_event_destroy(struct perf_event *event)
-{
- perf_trace_destroy(event);
-}
-
-static const struct pmu *tp_perf_event_init(struct perf_event *event)
-{
- int err;
-
- /*
- * Raw tracepoint data is a severe data leak, only allow root to
- * have these.
- */
- if ((event->attr.sample_type & PERF_SAMPLE_RAW) &&
- perf_paranoid_tracepoint_raw() &&
- !capable(CAP_SYS_ADMIN))
- return ERR_PTR(-EPERM);
-
- err = perf_trace_init(event);
- if (err)
- return NULL;
-
- event->destroy = tp_perf_event_destroy;
-
- return &perf_ops_tracepoint;
-}
-
-static int perf_event_set_filter(struct perf_event *event, void __user *arg)
-{
- char *filter_str;
- int ret;
-
- if (event->attr.type != PERF_TYPE_TRACEPOINT)
- return -EINVAL;
-
- filter_str = strndup_user(arg, PAGE_SIZE);
- if (IS_ERR(filter_str))
- return PTR_ERR(filter_str);
-
- ret = ftrace_profile_set_filter(event, event->attr.config, filter_str);
-
- kfree(filter_str);
- return ret;
-}
-
-static void perf_event_free_filter(struct perf_event *event)
-{
- ftrace_profile_free_filter(event);
-}
-
-#else
-
-static const struct pmu *tp_perf_event_init(struct perf_event *event)
-{
- return NULL;
-}
-
-static int perf_event_set_filter(struct perf_event *event, void __user *arg)
-{
- return -ENOENT;
-}
-
-static void perf_event_free_filter(struct perf_event *event)
-{
-}
-
-#endif /* CONFIG_EVENT_TRACING */
-
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
-static void bp_perf_event_destroy(struct perf_event *event)
-{
- release_bp_slot(event);
-}
-
-static const struct pmu *bp_perf_event_init(struct perf_event *bp)
-{
- int err;
-
- err = register_perf_hw_breakpoint(bp);
- if (err)
- return ERR_PTR(err);
-
- bp->destroy = bp_perf_event_destroy;
-
- return &perf_ops_bp;
-}
-
-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);
-
- if (!perf_exclude_event(bp, regs))
- perf_swevent_add(bp, 1, 1, &sample, regs);
-}
-#else
-static const struct pmu *bp_perf_event_init(struct perf_event *bp)
-{
- return NULL;
-}
-
-void perf_bp_event(struct perf_event *bp, void *regs)
-{
-}
-#endif
-
-atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX];
-
-static void sw_perf_event_destroy(struct perf_event *event)
-{
- u64 event_id = event->attr.config;
-
- WARN_ON(event->parent);
-
- atomic_dec(&perf_swevent_enabled[event_id]);
- swevent_hlist_put(event);
-}
-
-static const struct pmu *sw_perf_event_init(struct perf_event *event)
-{
- const struct pmu *pmu = NULL;
- u64 event_id = event->attr.config;
-
- /*
- * Software events (currently) can't in general distinguish
- * between user, kernel and hypervisor events.
- * However, context switches and cpu migrations are considered
- * to be kernel events, and page faults are never hypervisor
- * events.
- */
- switch (event_id) {
- case PERF_COUNT_SW_CPU_CLOCK:
- pmu = &perf_ops_cpu_clock;
-
- break;
- case PERF_COUNT_SW_TASK_CLOCK:
- /*
- * If the user instantiates this as a per-cpu event,
- * use the cpu_clock event instead.
- */
- if (event->ctx->task)
- pmu = &perf_ops_task_clock;
- else
- pmu = &perf_ops_cpu_clock;
-
- break;
- case PERF_COUNT_SW_PAGE_FAULTS:
- case PERF_COUNT_SW_PAGE_FAULTS_MIN:
- case PERF_COUNT_SW_PAGE_FAULTS_MAJ:
- case PERF_COUNT_SW_CONTEXT_SWITCHES:
- case PERF_COUNT_SW_CPU_MIGRATIONS:
- case PERF_COUNT_SW_ALIGNMENT_FAULTS:
- case PERF_COUNT_SW_EMULATION_FAULTS:
- if (!event->parent) {
- int err;
-
- err = swevent_hlist_get(event);
- if (err)
- return ERR_PTR(err);
-
- atomic_inc(&perf_swevent_enabled[event_id]);
- event->destroy = sw_perf_event_destroy;
- }
- pmu = &perf_ops_generic;
- break;
- }
-
- return pmu;
-}
-
-/*
- * Allocate and initialize a event structure
- */
-static struct perf_event *
-perf_event_alloc(struct perf_event_attr *attr,
- int cpu,
- struct perf_event_context *ctx,
- struct perf_event *group_leader,
- struct perf_event *parent_event,
- perf_overflow_handler_t overflow_handler,
- gfp_t gfpflags)
-{
- const struct pmu *pmu;
- struct perf_event *event;
- struct hw_perf_event *hwc;
- long err;
-
- event = kzalloc(sizeof(*event), gfpflags);
- if (!event)
- return ERR_PTR(-ENOMEM);
-
- /*
- * Single events are their own group leaders, with an
- * empty sibling list:
- */
- if (!group_leader)
- group_leader = event;
-
- mutex_init(&event->child_mutex);
- INIT_LIST_HEAD(&event->child_list);
-
- INIT_LIST_HEAD(&event->group_entry);
- INIT_LIST_HEAD(&event->event_entry);
- INIT_LIST_HEAD(&event->sibling_list);
- init_waitqueue_head(&event->waitq);
-
- mutex_init(&event->mmap_mutex);
-
- event->cpu = cpu;
- event->attr = *attr;
- event->group_leader = group_leader;
- event->pmu = NULL;
- event->ctx = ctx;
- event->oncpu = -1;
-
- event->parent = parent_event;
-
- event->ns = get_pid_ns(current->nsproxy->pid_ns);
- event->id = atomic64_inc_return(&perf_event_id);
-
- event->state = PERF_EVENT_STATE_INACTIVE;
-
- if (!overflow_handler && parent_event)
- overflow_handler = parent_event->overflow_handler;
-
- event->overflow_handler = overflow_handler;
-
- if (attr->disabled)
- event->state = PERF_EVENT_STATE_OFF;
-
- pmu = NULL;
-
- hwc = &event->hw;
- hwc->sample_period = attr->sample_period;
- if (attr->freq && attr->sample_freq)
- hwc->sample_period = 1;
- hwc->last_period = hwc->sample_period;
-
- local64_set(&hwc->period_left, hwc->sample_period);
-
- /*
- * we currently do not support PERF_FORMAT_GROUP on inherited events
- */
- if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
- goto done;
-
- switch (attr->type) {
- case PERF_TYPE_RAW:
- case PERF_TYPE_HARDWARE:
- case PERF_TYPE_HW_CACHE:
- pmu = hw_perf_event_init(event);
- break;
-
- case PERF_TYPE_SOFTWARE:
- pmu = sw_perf_event_init(event);
- break;
-
- case PERF_TYPE_TRACEPOINT:
- pmu = tp_perf_event_init(event);
- break;
-
- case PERF_TYPE_BREAKPOINT:
- pmu = bp_perf_event_init(event);
- break;
-
-
- default:
- break;
- }
-done:
- err = 0;
- if (!pmu)
- err = -EINVAL;
- else if (IS_ERR(pmu))
- err = PTR_ERR(pmu);
-
- if (err) {
- if (event->ns)
- put_pid_ns(event->ns);
- kfree(event);
- return ERR_PTR(err);
- }
-
- event->pmu = pmu;
-
- if (!event->parent) {
- atomic_inc(&nr_events);
- if (event->attr.mmap || event->attr.mmap_data)
- atomic_inc(&nr_mmap_events);
- if (event->attr.comm)
- atomic_inc(&nr_comm_events);
- if (event->attr.task)
- atomic_inc(&nr_task_events);
- }
-
- return event;
-}
-
-static int perf_copy_attr(struct perf_event_attr __user *uattr,
- struct perf_event_attr *attr)
-{
- u32 size;
- int ret;
-
- if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0))
- return -EFAULT;
-
- /*
- * zero the full structure, so that a short copy will be nice.
- */
- memset(attr, 0, sizeof(*attr));
-
- ret = get_user(size, &uattr->size);
- if (ret)
- return ret;
-
- if (size > PAGE_SIZE) /* silly large */
- goto err_size;
-
- if (!size) /* abi compat */
- size = PERF_ATTR_SIZE_VER0;
-
- if (size < PERF_ATTR_SIZE_VER0)
- goto err_size;
-
- /*
- * If we're handed a bigger struct than we know of,
- * ensure all the unknown bits are 0 - i.e. new
- * user-space does not rely on any kernel feature
- * extensions we dont know about yet.
- */
- if (size > sizeof(*attr)) {
- unsigned char __user *addr;
- unsigned char __user *end;
- unsigned char val;
-
- addr = (void __user *)uattr + sizeof(*attr);
- end = (void __user *)uattr + size;
-
- for (; addr < end; addr++) {
- ret = get_user(val, addr);
- if (ret)
- return ret;
- if (val)
- goto err_size;
- }
- size = sizeof(*attr);
- }
-
- ret = copy_from_user(attr, uattr, size);
- if (ret)
- return -EFAULT;
-
- /*
- * If the type exists, the corresponding creation will verify
- * the attr->config.
- */
- if (attr->type >= PERF_TYPE_MAX)
- return -EINVAL;
-
- if (attr->__reserved_1)
- return -EINVAL;
-
- if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
- return -EINVAL;
-
- if (attr->read_format & ~(PERF_FORMAT_MAX-1))
- return -EINVAL;
-
-out:
- return ret;
-
-err_size:
- put_user(sizeof(*attr), &uattr->size);
- ret = -E2BIG;
- goto out;
-}
-
-static int
-perf_event_set_output(struct perf_event *event, struct perf_event *output_event)
-{
- struct perf_buffer *buffer = NULL, *old_buffer = NULL;
- int ret = -EINVAL;
-
- if (!output_event)
- goto set;
-
- /* don't allow circular references */
- if (event == output_event)
- goto out;
-
- /*
- * Don't allow cross-cpu buffers
- */
- if (output_event->cpu != event->cpu)
- goto out;
-
- /*
- * If its not a per-cpu buffer, it must be the same task.
- */
- if (output_event->cpu == -1 && output_event->ctx != event->ctx)
- goto out;
-
-set:
- mutex_lock(&event->mmap_mutex);
- /* Can't redirect output if we've got an active mmap() */
- if (atomic_read(&event->mmap_count))
- goto unlock;
-
- if (output_event) {
- /* get the buffer we want to redirect to */
- buffer = perf_buffer_get(output_event);
- if (!buffer)
- goto unlock;
- }
-
- old_buffer = event->buffer;
- rcu_assign_pointer(event->buffer, buffer);
- ret = 0;
-unlock:
- mutex_unlock(&event->mmap_mutex);
-
- if (old_buffer)
- perf_buffer_put(old_buffer);
-out:
- return ret;
-}
-
-/**
- * sys_perf_event_open - open a performance event, associate it to a task/cpu
- *
- * @attr_uptr: event_id type attributes for monitoring/sampling
- * @pid: target pid
- * @cpu: target cpu
- * @group_fd: group leader event fd
- */
-SYSCALL_DEFINE5(perf_event_open,
- struct perf_event_attr __user *, attr_uptr,
- pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
-{
- struct perf_event *event, *group_leader = NULL, *output_event = NULL;
- struct perf_event_attr attr;
- struct perf_event_context *ctx;
- struct file *event_file = NULL;
- struct file *group_file = NULL;
- int event_fd;
- int fput_needed = 0;
- int err;
-
- /* for future expandability... */
- if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
- return -EINVAL;
-
- err = perf_copy_attr(attr_uptr, &attr);
- if (err)
- return err;
-
- if (!attr.exclude_kernel) {
- if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
- return -EACCES;
- }
-
- if (attr.freq) {
- if (attr.sample_freq > sysctl_perf_event_sample_rate)
- return -EINVAL;
- }
-
- event_fd = get_unused_fd_flags(O_RDWR);
- if (event_fd < 0)
- return event_fd;
-
- /*
- * Get the target context (task or percpu):
- */
- ctx = find_get_context(pid, cpu);
- if (IS_ERR(ctx)) {
- err = PTR_ERR(ctx);
- goto err_fd;
- }
-
- if (group_fd != -1) {
- group_leader = perf_fget_light(group_fd, &fput_needed);
- if (IS_ERR(group_leader)) {
- err = PTR_ERR(group_leader);
- goto err_put_context;
- }
- group_file = group_leader->filp;
- if (flags & PERF_FLAG_FD_OUTPUT)
- output_event = group_leader;
- if (flags & PERF_FLAG_FD_NO_GROUP)
- group_leader = NULL;
- }
-
- /*
- * Look up the group leader (we will attach this event to it):
- */
- if (group_leader) {
- err = -EINVAL;
-
- /*
- * Do not allow a recursive hierarchy (this new sibling
- * becoming part of another group-sibling):
- */
- if (group_leader->group_leader != group_leader)
- goto err_put_context;
- /*
- * Do not allow to attach to a group in a different
- * task or CPU context:
- */
- if (group_leader->ctx != ctx)
- goto err_put_context;
- /*
- * Only a group leader can be exclusive or pinned
- */
- if (attr.exclusive || attr.pinned)
- goto err_put_context;
- }
-
- event = perf_event_alloc(&attr, cpu, ctx, group_leader,
- NULL, NULL, GFP_KERNEL);
- if (IS_ERR(event)) {
- err = PTR_ERR(event);
- goto err_put_context;
- }
-
- if (output_event) {
- err = perf_event_set_output(event, output_event);
- if (err)
- goto err_free_put_context;
- }
-
- event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR);
- if (IS_ERR(event_file)) {
- err = PTR_ERR(event_file);
- goto err_free_put_context;
- }
-
- event->filp = event_file;
- WARN_ON_ONCE(ctx->parent_ctx);
- mutex_lock(&ctx->mutex);
- perf_install_in_context(ctx, event, cpu);
- ++ctx->generation;
- mutex_unlock(&ctx->mutex);
-
- event->owner = current;
- get_task_struct(current);
- mutex_lock(¤t->perf_event_mutex);
- list_add_tail(&event->owner_entry, ¤t->perf_event_list);
- mutex_unlock(¤t->perf_event_mutex);
-
- /*
- * Drop the reference on the group_event after placing the
- * new event on the sibling_list. This ensures destruction
- * of the group leader will find the pointer to itself in
- * perf_group_detach().
- */
- fput_light(group_file, fput_needed);
- fd_install(event_fd, event_file);
- return event_fd;
-
-err_free_put_context:
- free_event(event);
-err_put_context:
- fput_light(group_file, fput_needed);
- put_ctx(ctx);
-err_fd:
- put_unused_fd(event_fd);
- return err;
-}
-
-/**
- * perf_event_create_kernel_counter
- *
- * @attr: attributes of the counter to create
- * @cpu: cpu in which the counter is bound
- * @pid: task to profile
- */
-struct perf_event *
-perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
- pid_t pid,
- perf_overflow_handler_t overflow_handler)
-{
- struct perf_event *event;
- struct perf_event_context *ctx;
- int err;
-
- /*
- * Get the target context (task or percpu):
- */
-
- ctx = find_get_context(pid, cpu);
- if (IS_ERR(ctx)) {
- err = PTR_ERR(ctx);
- goto err_exit;
- }
-
- event = perf_event_alloc(attr, cpu, ctx, NULL,
- NULL, overflow_handler, GFP_KERNEL);
- if (IS_ERR(event)) {
- err = PTR_ERR(event);
- goto err_put_context;
- }
-
- event->filp = NULL;
- WARN_ON_ONCE(ctx->parent_ctx);
- mutex_lock(&ctx->mutex);
- perf_install_in_context(ctx, event, cpu);
- ++ctx->generation;
- mutex_unlock(&ctx->mutex);
-
- event->owner = current;
- get_task_struct(current);
- mutex_lock(¤t->perf_event_mutex);
- list_add_tail(&event->owner_entry, ¤t->perf_event_list);
- mutex_unlock(¤t->perf_event_mutex);
-
- return event;
-
- err_put_context:
- put_ctx(ctx);
- err_exit:
- return ERR_PTR(err);
-}
-EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);
-
-/*
- * inherit a event from parent task to child task:
- */
-static struct perf_event *
-inherit_event(struct perf_event *parent_event,
- struct task_struct *parent,
- struct perf_event_context *parent_ctx,
- struct task_struct *child,
- struct perf_event *group_leader,
- struct perf_event_context *child_ctx)
-{
- struct perf_event *child_event;
-
- /*
- * Instead of creating recursive hierarchies of events,
- * we link inherited events back to the original parent,
- * which has a filp for sure, which we use as the reference
- * count:
- */
- if (parent_event->parent)
- parent_event = parent_event->parent;
-
- child_event = perf_event_alloc(&parent_event->attr,
- parent_event->cpu, child_ctx,
- group_leader, parent_event,
- NULL, GFP_KERNEL);
- if (IS_ERR(child_event))
- return child_event;
- get_ctx(child_ctx);
-
- /*
- * Make the child state follow the state of the parent event,
- * not its attr.disabled bit. We hold the parent's mutex,
- * so we won't race with perf_event_{en, dis}able_family.
- */
- if (parent_event->state >= PERF_EVENT_STATE_INACTIVE)
- child_event->state = PERF_EVENT_STATE_INACTIVE;
- else
- child_event->state = PERF_EVENT_STATE_OFF;
-
- if (parent_event->attr.freq) {
- u64 sample_period = parent_event->hw.sample_period;
- struct hw_perf_event *hwc = &child_event->hw;
-
- hwc->sample_period = sample_period;
- hwc->last_period = sample_period;
-
- local64_set(&hwc->period_left, sample_period);
- }
-
- child_event->overflow_handler = parent_event->overflow_handler;
-
- /*
- * Link it up in the child's context:
- */
- add_event_to_ctx(child_event, child_ctx);
-
- /*
- * Get a reference to the parent filp - we will fput it
- * when the child event exits. This is safe to do because
- * we are in the parent and we know that the filp still
- * exists and has a nonzero count:
- */
- atomic_long_inc(&parent_event->filp->f_count);
-
- /*
- * Link this into the parent event's child list
- */
- WARN_ON_ONCE(parent_event->ctx->parent_ctx);
- mutex_lock(&parent_event->child_mutex);
- list_add_tail(&child_event->child_list, &parent_event->child_list);
- mutex_unlock(&parent_event->child_mutex);
-
- return child_event;
-}
-
-static int inherit_group(struct perf_event *parent_event,
- struct task_struct *parent,
- struct perf_event_context *parent_ctx,
- struct task_struct *child,
- struct perf_event_context *child_ctx)
-{
- struct perf_event *leader;
- struct perf_event *sub;
- struct perf_event *child_ctr;
-
- leader = inherit_event(parent_event, parent, parent_ctx,
- child, NULL, child_ctx);
- if (IS_ERR(leader))
- return PTR_ERR(leader);
- list_for_each_entry(sub, &parent_event->sibling_list, group_entry) {
- child_ctr = inherit_event(sub, parent, parent_ctx,
- child, leader, child_ctx);
- if (IS_ERR(child_ctr))
- return PTR_ERR(child_ctr);
- }
- return 0;
-}
-
-static void sync_child_event(struct perf_event *child_event,
- struct task_struct *child)
-{
- struct perf_event *parent_event = child_event->parent;
- u64 child_val;
-
- if (child_event->attr.inherit_stat)
- perf_event_read_event(child_event, child);
-
- child_val = perf_event_count(child_event);
-
- /*
- * Add back the child's count to the parent's count:
- */
- atomic64_add(child_val, &parent_event->child_count);
- atomic64_add(child_event->total_time_enabled,
- &parent_event->child_total_time_enabled);
- atomic64_add(child_event->total_time_running,
- &parent_event->child_total_time_running);
-
- /*
- * Remove this event from the parent's list
- */
- WARN_ON_ONCE(parent_event->ctx->parent_ctx);
- mutex_lock(&parent_event->child_mutex);
- list_del_init(&child_event->child_list);
- mutex_unlock(&parent_event->child_mutex);
-
- /*
- * Release the parent event, if this was the last
- * reference to it.
- */
- fput(parent_event->filp);
-}
-
-static void
-__perf_event_exit_task(struct perf_event *child_event,
- struct perf_event_context *child_ctx,
- struct task_struct *child)
-{
- struct perf_event *parent_event;
-
- perf_event_remove_from_context(child_event);
-
- parent_event = child_event->parent;
- /*
- * It can happen that parent exits first, and has events
- * that are still around due to the child reference. These
- * events need to be zapped - but otherwise linger.
- */
- if (parent_event) {
- sync_child_event(child_event, child);
- free_event(child_event);
- }
-}
-
-/*
- * When a child task exits, feed back event values to parent events.
- */
-void perf_event_exit_task(struct task_struct *child)
-{
- struct perf_event *child_event, *tmp;
- struct perf_event_context *child_ctx;
- unsigned long flags;
-
- if (likely(!child->perf_event_ctxp)) {
- perf_event_task(child, NULL, 0);
- return;
- }
-
- local_irq_save(flags);
- /*
- * We can't reschedule here because interrupts are disabled,
- * and either child is current or it is a task that can't be
- * scheduled, so we are now safe from rescheduling changing
- * our context.
- */
- child_ctx = child->perf_event_ctxp;
- __perf_event_task_sched_out(child_ctx);
-
- /*
- * Take the context lock here so that if find_get_context is
- * reading child->perf_event_ctxp, we wait until it has
- * incremented the context's refcount before we do put_ctx below.
- */
- raw_spin_lock(&child_ctx->lock);
- child->perf_event_ctxp = NULL;
- /*
- * If this context is a clone; unclone it so it can't get
- * swapped to another process while we're removing all
- * the events from it.
- */
- unclone_ctx(child_ctx);
- update_context_time(child_ctx);
- raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
-
- /*
- * Report the task dead after unscheduling the events so that we
- * won't get any samples after PERF_RECORD_EXIT. We can however still
- * get a few PERF_RECORD_READ events.
- */
- perf_event_task(child, child_ctx, 0);
-
- /*
- * We can recurse on the same lock type through:
- *
- * __perf_event_exit_task()
- * sync_child_event()
- * fput(parent_event->filp)
- * perf_release()
- * mutex_lock(&ctx->mutex)
- *
- * But since its the parent context it won't be the same instance.
- */
- mutex_lock(&child_ctx->mutex);
-
-again:
- list_for_each_entry_safe(child_event, tmp, &child_ctx->pinned_groups,
- group_entry)
- __perf_event_exit_task(child_event, child_ctx, child);
-
- list_for_each_entry_safe(child_event, tmp, &child_ctx->flexible_groups,
- group_entry)
- __perf_event_exit_task(child_event, child_ctx, child);
-
- /*
- * If the last event was a group event, it will have appended all
- * its siblings to the list, but we obtained 'tmp' before that which
- * will still point to the list head terminating the iteration.
- */
- if (!list_empty(&child_ctx->pinned_groups) ||
- !list_empty(&child_ctx->flexible_groups))
- goto again;
-
- mutex_unlock(&child_ctx->mutex);
-
- put_ctx(child_ctx);
-}
-
-static void perf_free_event(struct perf_event *event,
- struct perf_event_context *ctx)
-{
- struct perf_event *parent = event->parent;
-
- if (WARN_ON_ONCE(!parent))
- return;
-
- mutex_lock(&parent->child_mutex);
- list_del_init(&event->child_list);
- mutex_unlock(&parent->child_mutex);
-
- fput(parent->filp);
-
- perf_group_detach(event);
- list_del_event(event, ctx);
- free_event(event);
-}
-
-/*
- * free an unexposed, unused context as created by inheritance by
- * init_task below, used by fork() in case of fail.
- */
-void perf_event_free_task(struct task_struct *task)
-{
- struct perf_event_context *ctx = task->perf_event_ctxp;
- struct perf_event *event, *tmp;
-
- if (!ctx)
- return;
-
- mutex_lock(&ctx->mutex);
-again:
- list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry)
- perf_free_event(event, ctx);
-
- list_for_each_entry_safe(event, tmp, &ctx->flexible_groups,
- group_entry)
- perf_free_event(event, ctx);
-
- if (!list_empty(&ctx->pinned_groups) ||
- !list_empty(&ctx->flexible_groups))
- goto again;
-
- mutex_unlock(&ctx->mutex);
-
- put_ctx(ctx);
-}
-
-static int
-inherit_task_group(struct perf_event *event, struct task_struct *parent,
- struct perf_event_context *parent_ctx,
- struct task_struct *child,
- int *inherited_all)
-{
- int ret;
- struct perf_event_context *child_ctx = child->perf_event_ctxp;
-
- if (!event->attr.inherit) {
- *inherited_all = 0;
- return 0;
- }
-
- if (!child_ctx) {
- /*
- * This is executed from the parent task context, so
- * inherit events that have been marked for cloning.
- * First allocate and initialize a context for the
- * child.
- */
-
- child_ctx = kzalloc(sizeof(struct perf_event_context),
- GFP_KERNEL);
- if (!child_ctx)
- return -ENOMEM;
-
- __perf_event_init_context(child_ctx, child);
- child->perf_event_ctxp = child_ctx;
- get_task_struct(child);
- }
-
- ret = inherit_group(event, parent, parent_ctx,
- child, child_ctx);
-
- if (ret)
- *inherited_all = 0;
-
- return ret;
-}
-
-
-/*
- * Initialize the perf_event context in task_struct
- */
-int perf_event_init_task(struct task_struct *child)
-{
- struct perf_event_context *child_ctx, *parent_ctx;
- struct perf_event_context *cloned_ctx;
- struct perf_event *event;
- struct task_struct *parent = current;
- int inherited_all = 1;
- int ret = 0;
-
- child->perf_event_ctxp = NULL;
-
- mutex_init(&child->perf_event_mutex);
- INIT_LIST_HEAD(&child->perf_event_list);
-
- if (likely(!parent->perf_event_ctxp))
- return 0;
-
- /*
- * If the parent's context is a clone, pin it so it won't get
- * swapped under us.
- */
- parent_ctx = perf_pin_task_context(parent);
-
- /*
- * No need to check if parent_ctx != NULL here; since we saw
- * it non-NULL earlier, the only reason for it to become NULL
- * is if we exit, and since we're currently in the middle of
- * a fork we can't be exiting at the same time.
- */
-
- /*
- * Lock the parent list. No need to lock the child - not PID
- * hashed yet and not running, so nobody can access it.
- */
- mutex_lock(&parent_ctx->mutex);
-
- /*
- * We dont have to disable NMIs - we are only looking at
- * the list, not manipulating it:
- */
- list_for_each_entry(event, &parent_ctx->pinned_groups, group_entry) {
- ret = inherit_task_group(event, parent, parent_ctx, child,
- &inherited_all);
- if (ret)
- break;
- }
-
- list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) {
- ret = inherit_task_group(event, parent, parent_ctx, child,
- &inherited_all);
- if (ret)
- break;
- }
-
- child_ctx = child->perf_event_ctxp;
-
- if (child_ctx && inherited_all) {
- /*
- * Mark the child context as a clone of the parent
- * context, or of whatever the parent is a clone of.
- * Note that if the parent is a clone, it could get
- * uncloned at any point, but that doesn't matter
- * because the list of events and the generation
- * count can't have changed since we took the mutex.
- */
- cloned_ctx = rcu_dereference(parent_ctx->parent_ctx);
- if (cloned_ctx) {
- child_ctx->parent_ctx = cloned_ctx;
- child_ctx->parent_gen = parent_ctx->parent_gen;
- } else {
- child_ctx->parent_ctx = parent_ctx;
- child_ctx->parent_gen = parent_ctx->generation;
- }
- get_ctx(child_ctx->parent_ctx);
- }
-
- mutex_unlock(&parent_ctx->mutex);
-
- perf_unpin_context(parent_ctx);
-
- return ret;
-}
-
-static void __init perf_event_init_all_cpus(void)
-{
- int cpu;
- struct perf_cpu_context *cpuctx;
-
- for_each_possible_cpu(cpu) {
- cpuctx = &per_cpu(perf_cpu_context, cpu);
- mutex_init(&cpuctx->hlist_mutex);
- __perf_event_init_context(&cpuctx->ctx, NULL);
- }
-}
-
-static void __cpuinit perf_event_init_cpu(int cpu)
-{
- struct perf_cpu_context *cpuctx;
-
- cpuctx = &per_cpu(perf_cpu_context, cpu);
-
- spin_lock(&perf_resource_lock);
- cpuctx->max_pertask = perf_max_events - perf_reserved_percpu;
- spin_unlock(&perf_resource_lock);
-
- mutex_lock(&cpuctx->hlist_mutex);
- if (cpuctx->hlist_refcount > 0) {
- struct swevent_hlist *hlist;
-
- hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
- WARN_ON_ONCE(!hlist);
- rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
- }
- mutex_unlock(&cpuctx->hlist_mutex);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-static void __perf_event_exit_cpu(void *info)
-{
- struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
- struct perf_event_context *ctx = &cpuctx->ctx;
- struct perf_event *event, *tmp;
-
- list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry)
- __perf_event_remove_from_context(event);
- list_for_each_entry_safe(event, tmp, &ctx->flexible_groups, group_entry)
- __perf_event_remove_from_context(event);
-}
-static void perf_event_exit_cpu(int cpu)
-{
- struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
- struct perf_event_context *ctx = &cpuctx->ctx;
-
- mutex_lock(&cpuctx->hlist_mutex);
- swevent_hlist_release(cpuctx);
- mutex_unlock(&cpuctx->hlist_mutex);
-
- mutex_lock(&ctx->mutex);
- smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1);
- mutex_unlock(&ctx->mutex);
-}
-#else
-static inline void perf_event_exit_cpu(int cpu) { }
-#endif
-
-static int __cpuinit
-perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
-{
- unsigned int cpu = (long)hcpu;
-
- switch (action & ~CPU_TASKS_FROZEN) {
-
- case CPU_UP_PREPARE:
- case CPU_DOWN_FAILED:
- perf_event_init_cpu(cpu);
- break;
-
- case CPU_UP_CANCELED:
- case CPU_DOWN_PREPARE:
- perf_event_exit_cpu(cpu);
- break;
-
- default:
- break;
- }
-
- return NOTIFY_OK;
-}
-
-/*
- * This has to have a higher priority than migration_notifier in sched.c.
- */
-static struct notifier_block __cpuinitdata perf_cpu_nb = {
- .notifier_call = perf_cpu_notify,
- .priority = 20,
-};
-
-void __init perf_event_init(void)
-{
- perf_event_init_all_cpus();
- perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_UP_PREPARE,
- (void *)(long)smp_processor_id());
- perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_ONLINE,
- (void *)(long)smp_processor_id());
- register_cpu_notifier(&perf_cpu_nb);
-}
-
-static ssize_t perf_show_reserve_percpu(struct sysdev_class *class,
- struct sysdev_class_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", perf_reserved_percpu);
-}
-
-static ssize_t
-perf_set_reserve_percpu(struct sysdev_class *class,
- struct sysdev_class_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct perf_cpu_context *cpuctx;
- unsigned long val;
- int err, cpu, mpt;
-
- err = strict_strtoul(buf, 10, &val);
- if (err)
- return err;
- if (val > perf_max_events)
- return -EINVAL;
-
- spin_lock(&perf_resource_lock);
- perf_reserved_percpu = val;
- for_each_online_cpu(cpu) {
- cpuctx = &per_cpu(perf_cpu_context, cpu);
- raw_spin_lock_irq(&cpuctx->ctx.lock);
- mpt = min(perf_max_events - cpuctx->ctx.nr_events,
- perf_max_events - perf_reserved_percpu);
- cpuctx->max_pertask = mpt;
- raw_spin_unlock_irq(&cpuctx->ctx.lock);
- }
- spin_unlock(&perf_resource_lock);
-
- return count;
-}
-
-static ssize_t perf_show_overcommit(struct sysdev_class *class,
- struct sysdev_class_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", perf_overcommit);
-}
-
-static ssize_t
-perf_set_overcommit(struct sysdev_class *class,
- struct sysdev_class_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned long val;
- int err;
-
- err = strict_strtoul(buf, 10, &val);
- if (err)
- return err;
- if (val > 1)
- return -EINVAL;
-
- spin_lock(&perf_resource_lock);
- perf_overcommit = val;
- spin_unlock(&perf_resource_lock);
-
- return count;
-}
-
-static SYSDEV_CLASS_ATTR(
- reserve_percpu,
- 0644,
- perf_show_reserve_percpu,
- perf_set_reserve_percpu
- );
-
-static SYSDEV_CLASS_ATTR(
- overcommit,
- 0644,
- perf_show_overcommit,
- perf_set_overcommit
- );
-
-static struct attribute *perfclass_attrs[] = {
- &attr_reserve_percpu.attr,
- &attr_overcommit.attr,
- NULL
-};
-
-static struct attribute_group perfclass_attr_group = {
- .attrs = perfclass_attrs,
- .name = "perf_events",
-};
-
-static int __init perf_event_sysfs_init(void)
-{
- return sysfs_create_group(&cpu_sysdev_class.kset.kobj,
- &perfclass_attr_group);
-}
-device_initcall(perf_event_sysfs_init);
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 02/20] perf: Add persistent event facilities
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
2010-11-04 15:36 ` [PATCH 01/20] perf: Start the massive restructuring Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 03/20] x86, mce: Add persistent MCE event Borislav Petkov
` (18 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Add barebones implementation for registering persistent events with
perf.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
include/linux/perf_event.h | 20 +++++++++++++++
kernel/events/Makefile | 2 +-
kernel/events/core.c | 19 +++++++++++---
kernel/events/persistent.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 95 insertions(+), 5 deletions(-)
create mode 100644 kernel/events/persistent.c
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 716f99b..e3e16d5 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1019,6 +1019,15 @@ extern int perf_swevent_get_recursion_context(void);
extern void perf_swevent_put_recursion_context(int rctx);
extern void perf_event_enable(struct perf_event *event);
extern void perf_event_disable(struct perf_event *event);
+extern struct perf_buffer *
+perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags);
+extern void perf_buffer_put(struct perf_buffer *buffer);
+extern int perf_enable_persistent_event(struct perf_event **event,
+ struct perf_event_attr *attr,
+ int cpu, unsigned bufsz);
+extern void perf_disable_persistent_event(struct perf_event *event, int cpu);
+extern int perf_persistent_open(struct inode *inode, struct file *file);
+extern const struct file_operations perf_pers_fops;
#else
static inline void
perf_event_task_sched_in(struct task_struct *task) { }
@@ -1056,6 +1065,17 @@ static inline int perf_swevent_get_recursion_context(void) { return -1; }
static inline void perf_swevent_put_recursion_context(int rctx) { }
static inline void perf_event_enable(struct perf_event *event) { }
static inline void perf_event_disable(struct perf_event *event) { }
+static inline struct perf_buffer *
+perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags) { return NULL; }
+static inline void perf_buffer_put(struct perf_buffer *buffer) {}
+static inline int
+perf_enable_persistent_event(struct perf_event **event,
+ struct perf_event_attr *attr, int cpu,
+ unsigned bufsz) { return -EINVAL; }
+static inline void
+perf_disable_persistent_event(struct perf_event *event, int cpu) {}
+static inline int
+perf_persistent_open(struct inode *inode, struct file *file) { return -1; }
#endif
#define perf_output_put(handle, x) \
diff --git a/kernel/events/Makefile b/kernel/events/Makefile
index 5445cbf..6ef034a 100644
--- a/kernel/events/Makefile
+++ b/kernel/events/Makefile
@@ -2,4 +2,4 @@ ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_perf_event.o = -pg
endif
-obj-y += core.o
+obj-y += core.o persistent.o
diff --git a/kernel/events/core.c b/kernel/events/core.c
index b98bed3..8d2cfd3 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1899,7 +1899,7 @@ static void free_event_rcu(struct rcu_head *head)
}
static void perf_pending_sync(struct perf_event *event);
-static void perf_buffer_put(struct perf_buffer *buffer);
+void perf_buffer_put(struct perf_buffer *buffer);
static void free_event(struct perf_event *event)
{
@@ -2440,7 +2440,7 @@ static void *perf_mmap_alloc_page(int cpu)
return page_address(page);
}
-static struct perf_buffer *
+struct perf_buffer *
perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
{
struct perf_buffer *buffer;
@@ -2557,7 +2557,7 @@ static void perf_buffer_free(struct perf_buffer *buffer)
schedule_work(&buffer->work);
}
-static struct perf_buffer *
+struct perf_buffer *
perf_buffer_alloc(int nr_pages, long watermark, int cpu, int flags)
{
struct perf_buffer *buffer;
@@ -2658,7 +2658,7 @@ static struct perf_buffer *perf_buffer_get(struct perf_event *event)
return buffer;
}
-static void perf_buffer_put(struct perf_buffer *buffer)
+void perf_buffer_put(struct perf_buffer *buffer)
{
if (!atomic_dec_and_test(&buffer->refcount))
return;
@@ -2828,6 +2828,17 @@ static const struct file_operations perf_fops = {
.fasync = perf_fasync,
};
+const struct file_operations perf_pers_fops = {
+ .llseek = no_llseek,
+ .open = perf_persistent_open,
+ .poll = perf_poll,
+ .unlocked_ioctl = perf_ioctl,
+ .compat_ioctl = perf_ioctl,
+ .mmap = perf_mmap,
+ .fasync = perf_fasync,
+ .release = perf_release,
+};
+
/*
* Perf event wakeup
*
diff --git a/kernel/events/persistent.c b/kernel/events/persistent.c
new file mode 100644
index 0000000..f7d27a2
--- /dev/null
+++ b/kernel/events/persistent.c
@@ -0,0 +1,59 @@
+#include <linux/perf_event.h>
+
+/*
+ * Pass in the @event pointer which receives the allocated event from
+ * perf on success. Check return code before touching @event further.
+ *
+ * @attr: perf attr template
+ * @cpu: on which cpu
+ * @nr_pages: perf buffer size in pages
+ *
+ */
+int perf_enable_persistent_event(struct perf_event **event,
+ struct perf_event_attr *attr, int cpu,
+ unsigned nr_pages)
+{
+ struct perf_buffer *buffer;
+ struct perf_event *ev;
+
+ ev = perf_event_create_kernel_counter(attr, cpu, -1, NULL);
+ if (IS_ERR(ev))
+ return -EINVAL;
+
+ buffer = perf_buffer_alloc(nr_pages, 0, cpu, 0);
+ if (IS_ERR(buffer))
+ goto err;
+
+ rcu_assign_pointer(ev->buffer, buffer);
+ perf_event_enable(ev);
+ *event = ev;
+
+ return 0;
+
+err:
+ perf_event_release_kernel(ev);
+ return -EINVAL;
+}
+
+void perf_disable_persistent_event(struct perf_event *event, int cpu)
+{
+ if (!event)
+ return;
+
+ perf_event_disable(event);
+
+ if (event->buffer) {
+ perf_buffer_put(event->buffer);
+ rcu_assign_pointer(event->buffer, NULL);
+ }
+
+ perf_event_release_kernel(event);
+}
+
+int perf_persistent_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+;
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
2010-11-04 15:36 ` [PATCH 01/20] perf: Start the massive restructuring Borislav Petkov
2010-11-04 15:36 ` [PATCH 02/20] perf: Add persistent event facilities Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-10 21:15 ` Ben Gamari
2010-11-04 15:36 ` [PATCH 04/20] perf: Move trace-event-parse out of perf/util directory Borislav Petkov
` (17 subsequent siblings)
20 siblings, 1 reply; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Add the necessary glue to enable the mce_record tracepoint on boot
turning it into a persistent event. This exports the MCE buffer
read-only to a userspace daemon which will hook into it through debugfs
when booting is finished.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
arch/x86/include/asm/mce.h | 8 ++++
arch/x86/kernel/cpu/mcheck/mce.c | 84 ++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 0 deletions(-)
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index c62c13c..c248038 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -91,6 +91,14 @@ struct mce_log {
struct mce entry[MCE_LOG_LEN];
};
+/*
+ * a per-cpu descriptor of the persistent MCE tracepoint
+ */
+struct mce_tp_desc {
+ struct perf_event *event;
+ struct dentry *debugfs_entry;
+};
+
#define MCE_OVERFLOW 0 /* bit 0 in flags means overflow */
#define MCE_LOG_SIGNATURE "MACHINECHECK"
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index ed41562..5ce3e72 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -95,8 +95,11 @@ static char *mce_helper_argv[2] = { mce_helper, NULL };
static DECLARE_WAIT_QUEUE_HEAD(mce_wait);
static DEFINE_PER_CPU(struct mce, mces_seen);
+static DEFINE_PER_CPU(struct mce_tp_desc, mce_event);
static int cpu_missing;
+
+
/*
* CPU/chipset specific EDAC code can register a notifier call here to print
* MCE errors in a human-readable form.
@@ -2052,6 +2055,86 @@ static void __cpuinit mce_reenable_cpu(void *h)
}
}
+struct perf_event_attr pattr = {
+ .type = PERF_TYPE_TRACEPOINT,
+ .size = sizeof(pattr),
+ .sample_type = PERF_SAMPLE_RAW,
+};
+
+static struct dentry *mce_add_event_debugfs(struct perf_event *event, int cpu)
+{
+ char buf[14];
+
+ sprintf(buf, "mce_record%d", cpu);
+
+ return debugfs_create_file(buf, S_IRUGO, mce_get_debugfs_dir(),
+ event, &perf_pers_fops);
+}
+
+static int mce_enable_perf_event_on_cpu(int cpu)
+{
+ struct mce_tp_desc *d = &per_cpu(mce_event, cpu);
+ int err = 0;
+
+ err = perf_enable_persistent_event(&d->event, &pattr, cpu, 128);
+ if (err) {
+ printk(KERN_ERR "MCE: Error enabling event on cpu %d\n", cpu);
+ goto ret;
+ }
+
+ d->debugfs_entry = mce_add_event_debugfs(d->event, cpu);
+ if (!d->debugfs_entry) {
+ printk(KERN_ERR "MCE: Error adding event debugfs entry on cpu %d\n", cpu);
+ goto disable;
+ }
+
+ return 0;
+
+disable:
+ perf_disable_persistent_event(d->event, cpu);
+
+ret:
+ return err;
+}
+
+static void mce_disable_perf_event_on_cpu(int cpu)
+{
+ struct mce_tp_desc *d = &per_cpu(mce_event, cpu);
+ perf_disable_persistent_event(d->event, cpu);
+ debugfs_remove(d->debugfs_entry);
+}
+
+static __init int mcheck_init_persistent_event(void)
+{
+ int cpu, err = 0;
+
+ get_online_cpus();
+
+ pattr.config = event_mce_record.event.type;
+ pattr.sample_period = ULLONG_MAX;
+
+ for_each_online_cpu(cpu)
+ if (mce_enable_perf_event_on_cpu(cpu))
+ goto unwind;
+
+ goto unlock;
+
+unwind:
+ for (--cpu; cpu >= 0; cpu--)
+ mce_disable_perf_event_on_cpu(cpu);
+
+unlock:
+ put_online_cpus();
+
+ return err;
+}
+
+/*
+ * This has to run after event_trace_init()
+ */
+device_initcall(mcheck_init_persistent_event);
+
+
/* Get notified when a cpu comes on/off. Be hotplug friendly. */
static int __cpuinit
mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
@@ -2065,6 +2148,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
mce_create_device(cpu);
if (threshold_cpu_callback)
threshold_cpu_callback(action, cpu);
+ mce_enable_perf_event_on_cpu(cpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-04 15:36 ` [PATCH 03/20] x86, mce: Add persistent MCE event Borislav Petkov
@ 2010-11-10 21:15 ` Ben Gamari
2010-11-10 22:21 ` Ingo Molnar
0 siblings, 1 reply; 31+ messages in thread
From: Ben Gamari @ 2010-11-10 21:15 UTC (permalink / raw)
To: Borislav Petkov, acme, fweisbec, mingo, peterz, rostedt
Cc: linux-kernel, Borislav Petkov
On Thu, 4 Nov 2010 16:36:39 +0100, Borislav Petkov <bp@amd64.org> wrote:
> From: Borislav Petkov <borislav.petkov@amd.com>
>
> Add the necessary glue to enable the mce_record tracepoint on boot
> turning it into a persistent event. This exports the MCE buffer
> read-only to a userspace daemon which will hook into it through debugfs
> when booting is finished.
>
While MCE technically is a diagnostics service, do we want to start
adding dependencies on debugfs in long-running, common-place daemons? I
was under the impression we were to avoid using debugfs for anything
other than values for one-off debugging.
Cheers,
- Ben
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-10 21:15 ` Ben Gamari
@ 2010-11-10 22:21 ` Ingo Molnar
2010-11-11 6:17 ` Borislav Petkov
0 siblings, 1 reply; 31+ messages in thread
From: Ingo Molnar @ 2010-11-10 22:21 UTC (permalink / raw)
To: Ben Gamari
Cc: Borislav Petkov, acme, fweisbec, peterz, rostedt, linux-kernel,
Borislav Petkov
* Ben Gamari <bgamari@gmail.com> wrote:
> On Thu, 4 Nov 2010 16:36:39 +0100, Borislav Petkov <bp@amd64.org> wrote:
> > From: Borislav Petkov <borislav.petkov@amd.com>
> >
> > Add the necessary glue to enable the mce_record tracepoint on boot
> > turning it into a persistent event. This exports the MCE buffer
> > read-only to a userspace daemon which will hook into it through debugfs
> > when booting is finished.
>
> While MCE technically is a diagnostics service, do we want to start adding
> dependencies on debugfs in long-running, common-place daemons? I was under the
> impression we were to avoid using debugfs for anything other than values for
> one-off debugging.
See this current discussion thread on lkml:
[RFC][PATCH] perf: sysfs type id
Events are being added to sysfs as we want to avoid the debugfs dependency.
Thanks,
Ingo
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-10 22:21 ` Ingo Molnar
@ 2010-11-11 6:17 ` Borislav Petkov
2010-11-11 8:58 ` Ingo Molnar
0 siblings, 1 reply; 31+ messages in thread
From: Borislav Petkov @ 2010-11-11 6:17 UTC (permalink / raw)
To: Ingo Molnar
Cc: Ben Gamari, acme@infradead.org, fweisbec@gmail.com,
peterz@infradead.org, rostedt@goodmis.org,
linux-kernel@vger.kernel.org
On Wed, Nov 10, 2010 at 05:21:57PM -0500, Ingo Molnar wrote:
>
> * Ben Gamari <bgamari@gmail.com> wrote:
>
> > On Thu, 4 Nov 2010 16:36:39 +0100, Borislav Petkov <bp@amd64.org> wrote:
> > > From: Borislav Petkov <borislav.petkov@amd.com>
> > >
> > > Add the necessary glue to enable the mce_record tracepoint on boot
> > > turning it into a persistent event. This exports the MCE buffer
> > > read-only to a userspace daemon which will hook into it through debugfs
> > > when booting is finished.
> >
> > While MCE technically is a diagnostics service, do we want to start adding
> > dependencies on debugfs in long-running, common-place daemons? I was under the
> > impression we were to avoid using debugfs for anything other than values for
> > one-off debugging.
>
> See this current discussion thread on lkml:
>
> [RFC][PATCH] perf: sysfs type id
>
> Events are being added to sysfs as we want to avoid the debugfs dependency.
Yep, event enumeration will land in /sysfs eventually but in this case
debugfs is used to export the buffers with the events recorded so far to
the daemon which mmaps them. And I remember also being mentioned that
debugfs will thus be always compiled in because of that.
In any case, we need some kind of a facility with which to export those
buffers for userspace to mmap. sysfs is clearly not that and debugfs is
what we currently use. I'm open for better suggestions though.
Thanks.
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-11 6:17 ` Borislav Petkov
@ 2010-11-11 8:58 ` Ingo Molnar
2010-11-11 13:34 ` Borislav Petkov
0 siblings, 1 reply; 31+ messages in thread
From: Ingo Molnar @ 2010-11-11 8:58 UTC (permalink / raw)
To: Borislav Petkov
Cc: Ben Gamari, acme@infradead.org, fweisbec@gmail.com,
peterz@infradead.org, rostedt@goodmis.org,
linux-kernel@vger.kernel.org, Peter Zijlstra
* Borislav Petkov <bp@amd64.org> wrote:
> On Wed, Nov 10, 2010 at 05:21:57PM -0500, Ingo Molnar wrote:
> >
> > * Ben Gamari <bgamari@gmail.com> wrote:
> >
> > > On Thu, 4 Nov 2010 16:36:39 +0100, Borislav Petkov <bp@amd64.org> wrote:
> > > > From: Borislav Petkov <borislav.petkov@amd.com>
> > > >
> > > > Add the necessary glue to enable the mce_record tracepoint on boot
> > > > turning it into a persistent event. This exports the MCE buffer
> > > > read-only to a userspace daemon which will hook into it through debugfs
> > > > when booting is finished.
> > >
> > > While MCE technically is a diagnostics service, do we want to start adding
> > > dependencies on debugfs in long-running, common-place daemons? I was under the
> > > impression we were to avoid using debugfs for anything other than values for
> > > one-off debugging.
> >
> > See this current discussion thread on lkml:
> >
> > [RFC][PATCH] perf: sysfs type id
> >
> > Events are being added to sysfs as we want to avoid the debugfs dependency.
>
> Yep, event enumeration will land in /sysfs eventually but in this case debugfs is
> used to export the buffers with the events recorded so far to the daemon which
> mmaps them. And I remember also being mentioned that debugfs will thus be always
> compiled in because of that.
The event ring-buffer can be mmap()-ed off the fd that sys_perf_event_open() gives.
This is what all of tools/perf/ does - it uses debugfs only for even enumeration
(which will move to sysfs).
Thanks,
Ingo
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-11 8:58 ` Ingo Molnar
@ 2010-11-11 13:34 ` Borislav Petkov
2010-11-11 15:38 ` Peter Zijlstra
0 siblings, 1 reply; 31+ messages in thread
From: Borislav Petkov @ 2010-11-11 13:34 UTC (permalink / raw)
To: Ingo Molnar
Cc: Ben Gamari, acme@infradead.org, fweisbec@gmail.com,
peterz@infradead.org, rostedt@goodmis.org,
linux-kernel@vger.kernel.org, Peter Zijlstra
On Thu, Nov 11, 2010 at 03:58:25AM -0500, Ingo Molnar wrote:
> > Yep, event enumeration will land in /sysfs eventually but in this case debugfs is
> > used to export the buffers with the events recorded so far to the daemon which
> > mmaps them. And I remember also being mentioned that debugfs will thus be always
> > compiled in because of that.
>
> The event ring-buffer can be mmap()-ed off the fd that sys_perf_event_open() gives.
This is what I did in the first version of the patchset...
> This is what all of tools/perf/ does - it uses debugfs only for even enumeration
> (which will move to sysfs).
but then PeterZ suggested I should use debugfs to export the buffers
and not teach sys_perf_event_open of persistent events. Which is also
the right thing to do (maybe not through debugfs) since we want to have
a single per-cpu buffer with all MCEs in there which even multiple
userspace tools can access. So the question is, how do I mmap() those
properly?
So we either
1) use the perf syscall and teach it about persistent events and it always
returns the same fd whenever a persistent event is requested over its attr
argument;
2) use debugfs (current approach);
3) enable the persistent event and stash its fd in some /sysfs member
which the RAS daemon reads out and mmaps. This last one actually sounds
pretty neat.
4) another idea which I'm missing.
So what do you guys think?
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-11 13:34 ` Borislav Petkov
@ 2010-11-11 15:38 ` Peter Zijlstra
2010-11-11 15:55 ` Borislav Petkov
0 siblings, 1 reply; 31+ messages in thread
From: Peter Zijlstra @ 2010-11-11 15:38 UTC (permalink / raw)
To: Borislav Petkov
Cc: Ingo Molnar, Ben Gamari, acme@infradead.org, fweisbec@gmail.com,
rostedt@goodmis.org, linux-kernel@vger.kernel.org
On Thu, 2010-11-11 at 14:34 +0100, Borislav Petkov wrote:
> 3) enable the persistent event and stash its fd in some /sysfs member
> which the RAS daemon reads out and mmaps. This last one actually sounds
> pretty neat.
Right, stashing the thing in sysfs, which you can open()+mmap() seems
the best way. Not sure you want to make it a RAS specific place, maybe a
generic persistent buffer place.
You'll get multiple files anyway, one per cpu, so maybe something like:
/sysfs/persistent_buffer/ras[0..n]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-11 15:38 ` Peter Zijlstra
@ 2010-11-11 15:55 ` Borislav Petkov
2010-11-11 17:30 ` Ingo Molnar
0 siblings, 1 reply; 31+ messages in thread
From: Borislav Petkov @ 2010-11-11 15:55 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Ben Gamari, acme@infradead.org, fweisbec@gmail.com,
rostedt@goodmis.org, linux-kernel@vger.kernel.org
On Thu, Nov 11, 2010 at 10:38:50AM -0500, Peter Zijlstra wrote:
> On Thu, 2010-11-11 at 14:34 +0100, Borislav Petkov wrote:
> > 3) enable the persistent event and stash its fd in some /sysfs member
> > which the RAS daemon reads out and mmaps. This last one actually sounds
> > pretty neat.
>
> Right, stashing the thing in sysfs, which you can open()+mmap() seems
> the best way. Not sure you want to make it a RAS specific place, maybe a
> generic persistent buffer place.
>
> You'll get multiple files anyway, one per cpu, so maybe something like:
>
> /sysfs/persistent_buffer/ras[0..n]
Yeah, or even integrate it even further into the events hierarchy like
this:
/sys/.../cpu/events/mce -> this is the trace_mce_record tracepoint
/sys/.../cpu/events/mce_pers -> persistent buffer fd
or an additional level further down
/sys/.../cpu/events/mce/persistent/...
or whatever.
Generally I don't care too much where we put it at the end.
Thanks.
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 03/20] x86, mce: Add persistent MCE event
2010-11-11 15:55 ` Borislav Petkov
@ 2010-11-11 17:30 ` Ingo Molnar
0 siblings, 0 replies; 31+ messages in thread
From: Ingo Molnar @ 2010-11-11 17:30 UTC (permalink / raw)
To: Borislav Petkov
Cc: Peter Zijlstra, Ben Gamari, acme@infradead.org,
fweisbec@gmail.com, rostedt@goodmis.org,
linux-kernel@vger.kernel.org
* Borislav Petkov <bp@amd64.org> wrote:
> On Thu, Nov 11, 2010 at 10:38:50AM -0500, Peter Zijlstra wrote:
> > On Thu, 2010-11-11 at 14:34 +0100, Borislav Petkov wrote:
> > > 3) enable the persistent event and stash its fd in some /sysfs member
> > > which the RAS daemon reads out and mmaps. This last one actually sounds
> > > pretty neat.
> >
> > Right, stashing the thing in sysfs, which you can open()+mmap() seems
> > the best way. Not sure you want to make it a RAS specific place, maybe a
> > generic persistent buffer place.
> >
> > You'll get multiple files anyway, one per cpu, so maybe something like:
> >
> > /sysfs/persistent_buffer/ras[0..n]
>
> Yeah, or even integrate it even further into the events hierarchy like
> this:
>
> /sys/.../cpu/events/mce -> this is the trace_mce_record tracepoint
> /sys/.../cpu/events/mce_pers -> persistent buffer fd
>
> or an additional level further down
>
> /sys/.../cpu/events/mce/persistent/...
>
> or whatever.
>
> Generally I don't care too much where we put it at the end.
It should be in a nice logical place though :-)
Thanks,
Ingo
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 04/20] perf: Move trace-event-parse out of perf/util directory
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (2 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 03/20] x86, mce: Add persistent MCE event Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 05/20] perf: Update the lib parse-events to the latest code Borislav Petkov
` (16 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt
Cc: linux-kernel, Steven Rostedt, Steven Rostedt
From: Steven Rostedt <srostedt@redhat.com>
Move the parse-events.c code that originally came from trace-cmd into
tools/lib/ and use it to make a libparsevent.a library.
This patch moves the file with minimal changes to the code.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
tools/lib/Makefile | 43 +
tools/lib/parse-events.c | 3231 ++++++++++++++++++++++++++++++++++
tools/lib/parse-events.h | 295 ++++
tools/perf/Makefile | 11 +-
tools/perf/util/trace-event-parse.c | 3233 -----------------------------------
tools/perf/util/trace-event.h | 297 +----
6 files changed, 3582 insertions(+), 3528 deletions(-)
create mode 100644 tools/lib/Makefile
create mode 100644 tools/lib/parse-events.c
create mode 100644 tools/lib/parse-events.h
delete mode 100644 tools/perf/util/trace-event-parse.c
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
new file mode 100644
index 0000000..4a8ad38
--- /dev/null
+++ b/tools/lib/Makefile
@@ -0,0 +1,43 @@
+
+# Make the path relative to DESTDIR, not to prefix
+ifndef DESTDIR
+prefix = $(HOME)
+endif
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+sharedir = $(prefix)/share
+ifeq ($(prefix),/usr)
+sysconfdir = /etc
+else
+sysconfdir = $(prefix)/etc
+endif
+
+export prefix bindir sharedir sysconfdir
+
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+RM = rm -f
+TAR = tar
+FIND = find
+INSTALL = install
+RPMBUILD = rpmbuild
+PTHREAD_LIBS = -lpthread
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+all: libparsevent.a
+
+PEVENT_LIB_OBJS = parse-events.o
+
+$(OUTPUT)%.o: %.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
+libparsevent.a: $(PEVENT_LIB_OBJS)
+ $(RM) $@; $(AR) rcs $@ $^
diff --git a/tools/lib/parse-events.c b/tools/lib/parse-events.c
new file mode 100644
index 0000000..3730389
--- /dev/null
+++ b/tools/lib/parse-events.c
@@ -0,0 +1,3231 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * 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.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#undef _GNU_SOURCE
+#include "parse-events.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_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;
+
+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);
+
+static void init_input_buf(char *buf, unsigned long long size)
+{
+ 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;
+
+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;
+}
+
+void parse_cmdlines(char *file, int size __unused)
+{
+ 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++;
+ }
+
+ cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
+
+ i = 0;
+ while (list) {
+ cmdlines[i].pid = list->pid;
+ cmdlines[i].comm = list->comm;
+ i++;
+ item = list;
+ list = list->next;
+ free(item);
+ }
+
+ qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+}
+
+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)
+{
+ 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;
+}
+
+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;
+ 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;
+}
+
+/*
+ * 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 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");
+ }
+}
+
+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)
+{
+ 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 struct printk_map *find_printk(unsigned long long addr)
+{
+ struct printk_map *printk;
+ struct printk_map key;
+
+ key.addr = addr;
+
+ printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
+ printk_cmp);
+
+ return printk;
+}
+
+void parse_ftrace_printk(char *file, unsigned int size __unused)
+{
+ 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);
+
+ i = 0;
+ while (list) {
+ printk_list[i].printk = list->printk;
+ printk_list[i].addr = list->addr;
+ i++;
+ item = list;
+ list = list->next;
+ free(item);
+ }
+
+ qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
+}
+
+void print_printk(void)
+{
+ int i;
+
+ for (i = 0; i < (int)printk_count; i++) {
+ printf("%016llx %s\n",
+ printk_list[i].addr,
+ printk_list[i].printk);
+ }
+}
+
+static struct event *alloc_event(void)
+{
+ struct event *event;
+
+ event = malloc_or_die(sizeof(*event));
+ memset(event, 0, sizeof(*event));
+
+ return event;
+}
+
+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)
+{
+ event->next = event_list;
+ event_list = event;
+}
+
+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_arg(struct print_arg *arg)
+{
+ if (!arg)
+ return;
+
+ 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);
+}
+
+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];
+}
+
+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;
+ }
+
+ 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;
+
+ 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 */
+ return EVENT_NONE;
+}
+
+/* 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);
+ }
+
+ /* not reached */
+ return EVENT_NONE;
+}
+
+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;
+ }
+ return 0;
+}
+
+static int __test_type_token(enum event_type type, char *token,
+ enum event_type expect, const char *expect_tok,
+ bool warn)
+{
+ if (type != expect) {
+ if (warn)
+ warning("Error: expected type %d but read %d",
+ expect, type);
+ return -1;
+ }
+
+ if (strcmp(token, expect_tok) != 0) {
+ if (warn)
+ warning("Error: expected '%s' but read '%s'",
+ expect_tok, token);
+ return -1;
+ }
+ 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)
+{
+ return __read_expected(expect, str, 1, true);
+}
+
+static int read_expected_item(enum event_type expect, const char *str)
+{
+ return __read_expected(expect, str, 0, true);
+}
+
+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))
+ 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;
+ 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 (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);
+ 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);
+}
+
+/* taken from Linux, written by Frederic Weisbecker */
+static void print_graph_cpu(int cpu)
+{
+ int i;
+ int log10_this = log10_cpu(cpu);
+ int log10_all = log10_cpu(cpus);
+
+
+ /*
+ * Start with a space character - to make it stand out
+ * to the right a bit when trace output is pasted into
+ * email:
+ */
+ printf(" ");
+
+ /*
+ * Tricky - we space the CPU field according to the max
+ * number of online CPUs. On a 2-cpu system it would take
+ * a maximum of 1 digit - on a 128 cpu system it would
+ * take up to 3 digits:
+ */
+ for (i = 0; i < log10_all - log10_this; i++)
+ printf(" ");
+
+ printf("%d) ", cpu);
+}
+
+#define TRACE_GRAPH_PROCINFO_LENGTH 14
+#define TRACE_GRAPH_INDENT 2
+
+static void print_graph_proc(int pid, const char *comm)
+{
+ /* sign + log10(MAX_INT) + '\0' */
+ char pid_str[11];
+ int spaces = 0;
+ int len;
+ int i;
+
+ sprintf(pid_str, "%d", pid);
+
+ /* 1 stands for the "-" character */
+ len = strlen(comm) + strlen(pid_str) + 1;
+
+ if (len < TRACE_GRAPH_PROCINFO_LENGTH)
+ spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
+
+ /* First spaces to align center */
+ for (i = 0; i < spaces / 2; i++)
+ printf(" ");
+
+ printf("%s-%s", comm, pid_str);
+
+ /* Last spaces to align center */
+ for (i = 0; i < spaces - (spaces / 2); i++)
+ printf(" ");
+}
+
+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, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ struct format_field *field;
+ struct record *rec;
+ void *copy_data;
+ unsigned long val;
+
+ printf("%5lu.%06lu | ", secs, usecs);
+
+ print_graph_cpu(cpu);
+ print_graph_proc(pid, comm);
+
+ printf(" | ");
+
+ 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,
+ int cpu, int pid, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ unsigned long long rettime, calltime;
+ unsigned long long duration, depth;
+ struct format_field *field;
+ int i;
+
+ printf("%5lu.%06lu | ", secs, usecs);
+
+ print_graph_cpu(cpu);
+ print_graph_proc(pid, comm);
+
+ printf(" | ");
+
+ 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, const char *comm,
+ unsigned long secs, unsigned long usecs)
+{
+ if (event->flags & EVENT_FL_ISFUNCENT)
+ pretty_print_func_ent(data, size, event,
+ cpu, pid, comm, secs, usecs);
+ else if (event->flags & EVENT_FL_ISFUNCRET)
+ pretty_print_func_ret(data, size, event,
+ cpu, pid, comm, secs, usecs);
+ printf("\n");
+}
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm)
+{
+ struct event *event;
+ unsigned long secs;
+ unsigned long usecs;
+ int type;
+ int pid;
+
+ secs = nsecs / NSECS_PER_SEC;
+ nsecs -= secs * NSECS_PER_SEC;
+ usecs = nsecs / NSECS_PER_USEC;
+
+ 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, comm, secs, usecs);
+
+ if (latency_format) {
+ printf("%8.8s-%-5d %3d",
+ comm, pid, cpu);
+ print_lat_fmt(data, size);
+ } else
+ printf("%16s-%-5d [%03d]", comm, pid, cpu);
+
+ printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+
+ if (event->flags & EVENT_FL_FAILED) {
+ printf("EVENT '%s' FAILED TO PARSE\n",
+ event->name);
+ return;
+ }
+
+ pretty_print(data, size, event);
+ printf("\n");
+}
+
+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);
+}
diff --git a/tools/lib/parse-events.h b/tools/lib/parse-events.h
new file mode 100644
index 0000000..b60b4df
--- /dev/null
+++ b/tools/lib/parse-events.h
@@ -0,0 +1,295 @@
+#ifndef __PERF_TRACE_EVENTS_H
+#define __PERF_TRACE_EVENTS_H
+
+#include <stdbool.h>
+
+#define __unused __attribute__((unused))
+
+
+#ifndef PAGE_MASK
+#define PAGE_MASK (page_size - 1)
+#endif
+
+enum {
+ RINGBUF_TYPE_PADDING = 29,
+ RINGBUF_TYPE_TIME_EXTEND = 30,
+ RINGBUF_TYPE_TIME_STAMP = 31,
+};
+
+#ifndef TS_SHIFT
+#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);
+
+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);
+
+void print_funcs(void);
+void print_printk(void);
+
+int parse_ftrace_file(char *buf, unsigned long size);
+int parse_event_file(char *buf, unsigned long size, char *sys);
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm);
+
+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);
+
+ 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;
+}
+
+#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); \
+})
+
+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;
+
+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);
+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);
+
+/* 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);
+ int (*stop_script) (void);
+ void (*process_event) (int cpu, void *data, int size,
+ unsigned long long nsecs, char *comm);
+ int (*generate_script) (const char *outfile);
+};
+
+int script_spec_register(const char *spec, struct scripting_ops *ops);
+
+void setup_perl_scripting(void);
+void setup_python_scripting(void);
+
+struct scripting_context {
+ void *event_data;
+};
+
+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 */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 1950e19..a251fb7 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -1,5 +1,8 @@
ifeq ("$(origin O)", "command line")
OUTPUT := $(O)/
+ LIB_OUTPUT := $(OUTPUT)generic-lib/
+else
+ LIB_OUTPUT := ../lib/
endif
# The default target of this Makefile is...
@@ -355,6 +358,8 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a
+EXTRA_LIB_FILE=$(LIB_OUTPUT)libparsevent.a
+
LIB_H += ../../include/linux/perf_event.h
LIB_H += ../../include/linux/rbtree.h
LIB_H += ../../include/linux/list.h
@@ -451,7 +456,6 @@ LIB_OBJS += $(OUTPUT)util/map.o
LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
-LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@ -490,7 +494,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) $(EXTRA_LIB_FILE)
#
# Platform specific tweaks
@@ -855,6 +859,9 @@ ifdef ASCIIDOC8
export ASCIIDOC8
endif
+$(LIB_OUTPUT)libparsevent.a:
+ $(MAKE) -C ../lib libparsevent.a
+
# Shell quote (do not use $(call) to accommodate ancient setups);
SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
deleted file mode 100644
index 73a0222..0000000
--- a/tools/perf/util/trace-event-parse.c
+++ /dev/null
@@ -1,3233 +0,0 @@
-/*
- * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * 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.
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#undef _GNU_SOURCE
-#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_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;
-
-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);
-
-static void init_input_buf(char *buf, unsigned long long size)
-{
- 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;
-
-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;
-}
-
-void parse_cmdlines(char *file, int size __unused)
-{
- 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++;
- }
-
- cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
-
- i = 0;
- while (list) {
- cmdlines[i].pid = list->pid;
- cmdlines[i].comm = list->comm;
- i++;
- item = list;
- list = list->next;
- free(item);
- }
-
- qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
-}
-
-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)
-{
- 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;
-}
-
-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;
- 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;
-}
-
-/*
- * 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 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");
- }
-}
-
-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)
-{
- 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 struct printk_map *find_printk(unsigned long long addr)
-{
- struct printk_map *printk;
- struct printk_map key;
-
- key.addr = addr;
-
- printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
- printk_cmp);
-
- return printk;
-}
-
-void parse_ftrace_printk(char *file, unsigned int size __unused)
-{
- 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);
-
- i = 0;
- while (list) {
- printk_list[i].printk = list->printk;
- printk_list[i].addr = list->addr;
- i++;
- item = list;
- list = list->next;
- free(item);
- }
-
- qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
-}
-
-void print_printk(void)
-{
- int i;
-
- for (i = 0; i < (int)printk_count; i++) {
- printf("%016llx %s\n",
- printk_list[i].addr,
- printk_list[i].printk);
- }
-}
-
-static struct event *alloc_event(void)
-{
- struct event *event;
-
- event = malloc_or_die(sizeof(*event));
- memset(event, 0, sizeof(*event));
-
- return event;
-}
-
-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)
-{
- event->next = event_list;
- event_list = event;
-}
-
-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_arg(struct print_arg *arg)
-{
- if (!arg)
- return;
-
- 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);
-}
-
-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];
-}
-
-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;
- }
-
- 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;
-
- 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 */
- return EVENT_NONE;
-}
-
-/* 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);
- }
-
- /* not reached */
- return EVENT_NONE;
-}
-
-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;
- }
- return 0;
-}
-
-static int __test_type_token(enum event_type type, char *token,
- enum event_type expect, const char *expect_tok,
- bool warn)
-{
- if (type != expect) {
- if (warn)
- warning("Error: expected type %d but read %d",
- expect, type);
- return -1;
- }
-
- if (strcmp(token, expect_tok) != 0) {
- if (warn)
- warning("Error: expected '%s' but read '%s'",
- expect_tok, token);
- return -1;
- }
- 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)
-{
- return __read_expected(expect, str, 1, true);
-}
-
-static int read_expected_item(enum event_type expect, const char *str)
-{
- return __read_expected(expect, str, 0, true);
-}
-
-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))
- 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;
- 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 (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);
- 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);
-}
-
-/* taken from Linux, written by Frederic Weisbecker */
-static void print_graph_cpu(int cpu)
-{
- int i;
- int log10_this = log10_cpu(cpu);
- int log10_all = log10_cpu(cpus);
-
-
- /*
- * Start with a space character - to make it stand out
- * to the right a bit when trace output is pasted into
- * email:
- */
- printf(" ");
-
- /*
- * Tricky - we space the CPU field according to the max
- * number of online CPUs. On a 2-cpu system it would take
- * a maximum of 1 digit - on a 128 cpu system it would
- * take up to 3 digits:
- */
- for (i = 0; i < log10_all - log10_this; i++)
- printf(" ");
-
- printf("%d) ", cpu);
-}
-
-#define TRACE_GRAPH_PROCINFO_LENGTH 14
-#define TRACE_GRAPH_INDENT 2
-
-static void print_graph_proc(int pid, const char *comm)
-{
- /* sign + log10(MAX_INT) + '\0' */
- char pid_str[11];
- int spaces = 0;
- int len;
- int i;
-
- sprintf(pid_str, "%d", pid);
-
- /* 1 stands for the "-" character */
- len = strlen(comm) + strlen(pid_str) + 1;
-
- if (len < TRACE_GRAPH_PROCINFO_LENGTH)
- spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
-
- /* First spaces to align center */
- for (i = 0; i < spaces / 2; i++)
- printf(" ");
-
- printf("%s-%s", comm, pid_str);
-
- /* Last spaces to align center */
- for (i = 0; i < spaces - (spaces / 2); i++)
- printf(" ");
-}
-
-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, const char *comm,
- unsigned long secs, unsigned long usecs)
-{
- struct format_field *field;
- struct record *rec;
- void *copy_data;
- unsigned long val;
-
- printf("%5lu.%06lu | ", secs, usecs);
-
- print_graph_cpu(cpu);
- print_graph_proc(pid, comm);
-
- printf(" | ");
-
- 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,
- int cpu, int pid, const char *comm,
- unsigned long secs, unsigned long usecs)
-{
- unsigned long long rettime, calltime;
- unsigned long long duration, depth;
- struct format_field *field;
- int i;
-
- printf("%5lu.%06lu | ", secs, usecs);
-
- print_graph_cpu(cpu);
- print_graph_proc(pid, comm);
-
- printf(" | ");
-
- 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, const char *comm,
- unsigned long secs, unsigned long usecs)
-{
- if (event->flags & EVENT_FL_ISFUNCENT)
- pretty_print_func_ent(data, size, event,
- cpu, pid, comm, secs, usecs);
- else if (event->flags & EVENT_FL_ISFUNCRET)
- pretty_print_func_ret(data, size, event,
- cpu, pid, comm, secs, usecs);
- printf("\n");
-}
-
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm)
-{
- struct event *event;
- unsigned long secs;
- unsigned long usecs;
- int type;
- int pid;
-
- secs = nsecs / NSECS_PER_SEC;
- nsecs -= secs * NSECS_PER_SEC;
- usecs = nsecs / NSECS_PER_USEC;
-
- 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, comm, secs, usecs);
-
- if (latency_format) {
- printf("%8.8s-%-5d %3d",
- comm, pid, cpu);
- print_lat_fmt(data, size);
- } else
- printf("%16s-%-5d [%03d]", comm, pid, cpu);
-
- printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
-
- if (event->flags & EVENT_FL_FAILED) {
- printf("EVENT '%s' FAILED TO PARSE\n",
- event->name);
- return;
- }
-
- pretty_print(data, size, event);
- printf("\n");
-}
-
-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);
-}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index b3e86b1..2adc26f 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -1,300 +1,11 @@
-#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 "../../lib/parse-events.h"
#include "parse-events.h"
-#define __unused __attribute__((unused))
-
-
-#ifndef PAGE_MASK
-#define PAGE_MASK (page_size - 1)
-#endif
-
-enum {
- RINGBUF_TYPE_PADDING = 29,
- RINGBUF_TYPE_TIME_EXTEND = 30,
- RINGBUF_TYPE_TIME_STAMP = 31,
-};
-
-#ifndef TS_SHIFT
-#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);
-
-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);
-
-void print_funcs(void);
-void print_printk(void);
-
-int parse_ftrace_file(char *buf, unsigned long size);
-int parse_event_file(char *buf, unsigned long size, char *sys);
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm);
-
-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);
-
- 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;
-}
-
-#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); \
-})
-
-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;
-
-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);
-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);
-
int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
int nb_events);
-/* 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);
- int (*stop_script) (void);
- void (*process_event) (int cpu, void *data, int size,
- unsigned long long nsecs, char *comm);
- int (*generate_script) (const char *outfile);
-};
-
-int script_spec_register(const char *spec, struct scripting_ops *ops);
-
-void setup_perl_scripting(void);
-void setup_python_scripting(void);
-
-struct scripting_context {
- void *event_data;
-};
-
-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 */
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 05/20] perf: Update the lib parse-events to the latest code
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (3 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 04/20] perf: Move trace-event-parse out of perf/util directory Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 06/20] perf: Move trace stuff into tools/lib/trace Borislav Petkov
` (15 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt
Cc: linux-kernel, Steven Rostedt, Steven Rostedt
From: Steven Rostedt <srostedt@redhat.com>
TBD
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
tools/lib/Makefile | 10 +-
tools/lib/parse-events.c | 3248 ++++++++++++++------
tools/lib/parse-events.h | 604 ++++-
tools/lib/parse-filter.c | 2085 +++++++++++++
tools/lib/parse-utils.c | 110 +
tools/lib/trace-seq.c | 153 +
tools/lib/util.h | 64 +
tools/perf/Makefile | 8 +-
tools/perf/builtin-kmem.c | 6 +-
tools/perf/builtin-lock.c | 26 +-
tools/perf/builtin-sched.c | 42 +-
.../util/scripting-engines/trace-event-python.c | 12 +-
tools/perf/util/trace-event-info.c | 2 +
tools/perf/util/trace-event-parse.c | 234 ++
tools/perf/util/trace-event-read.c | 24 +-
tools/perf/util/trace-event.h | 79 +
16 files changed, 5648 insertions(+), 1059 deletions(-)
create mode 100644 tools/lib/parse-filter.c
create mode 100644 tools/lib/parse-utils.c
create mode 100644 tools/lib/trace-seq.c
create mode 100644 tools/lib/util.h
create mode 100644 tools/perf/util/trace-event-parse.c
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 4a8ad38..206078b 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -34,10 +34,16 @@ endif
all: libparsevent.a
-PEVENT_LIB_OBJS = parse-events.o
+PEVENT_LIB_OBJS += parse-events.o
+PEVENT_LIB_OBJS += parse-filter.o
+PEVENT_LIB_OBJS += parse-utils.o
+PEVENT_LIB_OBJS += trace-seq.o
$(OUTPUT)%.o: %.c
- $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+ $(QUIET_CC)$(CC) -g -o $@ -c $(ALL_CFLAGS) $<
libparsevent.a: $(PEVENT_LIB_OBJS)
$(RM) $@; $(AR) rcs $@ $^
+
+clean:
+ $(RM) *.a *.o *~ *.so
\ No newline at end of file
diff --git a/tools/lib/parse-events.c b/tools/lib/parse-events.c
index 3730389..5503a18 100644
--- a/tools/lib/parse-events.c
+++ b/tools/lib/parse-events.c
@@ -1,74 +1,123 @@
/*
- * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ * 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 General Public License as published by
- * the Free Software Foundation; version 2 of the License (not later!)
+ * 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 General Public License for more details.
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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., 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.
+ * 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>
-#undef _GNU_SOURCE
#include "parse-events.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_data_offset;
-int header_page_data_size;
-
-bool latency_format;
-
-static char *input_buf;
+static const char *input_buf;
static unsigned long long input_buf_ptr;
static unsigned long long input_buf_siz;
-static int cpus;
-static int long_size;
-static int is_flag_field;
-static int is_symbolic_field;
+static int show_warning = 1;
-static struct format_field *
-find_any_field(struct event *event, const char *name);
+#define do_warning(fmt, ...) \
+ do { \
+ if (show_warning) \
+ warning(fmt, ##__VA_ARGS__); \
+ } while (0)
-static void init_input_buf(char *buf, unsigned long long size)
+static void init_input_buf(const char *buf, unsigned long long size)
{
input_buf = buf;
input_buf_siz = size;
input_buf_ptr = 0;
}
+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 struct cmdline *cmdlines;
-static int cmdline_count;
-
static int cmdline_cmp(const void *a, const void *b)
{
const struct cmdline *ca = a;
@@ -82,122 +131,180 @@ static int cmdline_cmp(const void *a, const void *b)
return 0;
}
-void parse_cmdlines(char *file, int size __unused)
+struct cmdline_list {
+ struct cmdline_list *next;
+ char *comm;
+ int pid;
+};
+
+static int cmdline_init(struct pevent *pevent)
{
- struct cmdline_list {
- struct cmdline_list *next;
- char *comm;
- int pid;
- } *list = NULL, *item;
- char *line;
- char *next = NULL;
+ struct cmdline_list *cmdlist = pevent->cmdlist;
+ struct cmdline_list *item;
+ struct cmdline *cmdlines;
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++;
- }
-
- cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
+ cmdlines = malloc_or_die(sizeof(*cmdlines) * pevent->cmdline_count);
i = 0;
- while (list) {
- cmdlines[i].pid = list->pid;
- cmdlines[i].comm = list->comm;
+ while (cmdlist) {
+ cmdlines[i].pid = cmdlist->pid;
+ cmdlines[i].comm = cmdlist->comm;
i++;
- item = list;
- list = list->next;
+ item = cmdlist;
+ cmdlist = cmdlist->next;
free(item);
}
- qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+ qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+
+ pevent->cmdlines = cmdlines;
+ pevent->cmdlist = NULL;
+
+ return 0;
}
-static struct func_map {
- unsigned long long addr;
- char *func;
- char *mod;
-} *func_list;
-static unsigned int func_count;
+static char *find_cmdline(struct pevent *pevent, int pid)
+{
+ const struct cmdline *comm;
+ struct cmdline key;
-static int func_cmp(const void *a, const void *b)
+ 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 func_map *fa = a;
- const struct func_map *fb = b;
+ const struct cmdline *comm;
+ struct cmdline key;
- if (fa->addr < fb->addr)
- return -1;
- if (fa->addr > fb->addr)
+ 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;
}
-void parse_proc_kallsyms(char *file, unsigned int size __unused)
+/*
+ * 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, char *comm, int pid)
{
- 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;
- int i;
+ struct cmdline *cmdlines = pevent->cmdlines;
+ const struct cmdline *cmdline;
+ struct cmdline key;
- 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);
+ if (!pid)
+ return 0;
- /* truncate the extra ']' */
- if (item->mod)
- item->mod[strlen(item->mod) - 1] = 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;
+ }
- item->next = list;
- list = item;
- line = strtok_r(NULL, "\n", &next);
- func_count++;
+ cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1));
+ if (!cmdlines) {
+ errno = ENOMEM;
+ return -1;
}
- func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
+ cmdlines[pevent->cmdline_count].pid = pid;
+ cmdlines[pevent->cmdline_count].comm = comm;
+ pevent->cmdline_count++;
- 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(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+ pevent->cmdlines = cmdlines;
- qsort(func_list, func_count, sizeof(*func_list), func_cmp);
+ return 0;
+}
- /*
- * 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;
+/**
+ * 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, 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);
+ 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;
}
/*
@@ -221,39 +328,166 @@ static int func_bcmp(const void *a, const void *b)
return 1;
}
-static struct func_map *find_func(unsigned long long addr)
+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, func_list, func_count, sizeof(*func_list),
- func_bcmp);
+ func = bsearch(&key, pevent->func_map, pevent->func_count,
+ sizeof(*pevent->func_map), func_bcmp);
return func;
}
-void print_funcs(void)
+/**
+ * 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;
- for (i = 0; i < (int)func_count; i++) {
+ if (!pevent->func_map)
+ func_map_init(pevent);
+
+ for (i = 0; i < (int)pevent->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);
+ 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");
}
}
-static struct printk_map {
+struct printk_map {
unsigned long long addr;
char *printk;
-} *printk_list;
-static unsigned int printk_count;
+};
+
+struct printk_list {
+ struct printk_list *next;
+ unsigned long long addr;
+ char *printk;
+};
static int printk_cmp(const void *a, const void *b)
{
@@ -268,77 +502,99 @@ static int printk_cmp(const void *a, const void *b)
return 0;
}
-static struct printk_map *find_printk(unsigned long long addr)
+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, printk_list, printk_count, sizeof(*printk_list),
- printk_cmp);
+ printk = bsearch(&key, pevent->printk_map, pevent->printk_count,
+ sizeof(*pevent->printk_map), printk_cmp);
return printk;
}
-void parse_ftrace_printk(char *file, unsigned int size __unused)
+/**
+ * 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 {
- struct printk_list *next;
- unsigned long long addr;
- char *printk;
- } *list = NULL, *item;
- char *line;
- char *next = NULL;
- char *addr_str;
- int i;
+ struct printk_list *item;
- 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++;
- }
+ item = malloc_or_die(sizeof(*item));
- printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1);
+ item->next = pevent->printklist;
+ pevent->printklist = item;
+ item->printk = strdup(fmt);
+ item->addr = addr;
- i = 0;
- while (list) {
- printk_list[i].printk = list->printk;
- printk_list[i].addr = list->addr;
- i++;
- item = list;
- list = list->next;
- free(item);
- }
+ pevent->printk_count++;
- qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
+ return 0;
}
-void print_printk(void)
+/**
+ * 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;
- for (i = 0; i < (int)printk_count; i++) {
+ if (!pevent->printk_map)
+ printk_map_init(pevent);
+
+ for (i = 0; i < (int)pevent->printk_count; i++) {
printf("%016llx %s\n",
- printk_list[i].addr,
- printk_list[i].printk);
+ pevent->printk_map[i].addr,
+ pevent->printk_map[i].printk);
}
}
-static struct event *alloc_event(void)
+static struct event_format *alloc_event(void)
{
- struct event *event;
+ struct event_format *event;
event = malloc_or_die(sizeof(*event));
memset(event, 0, sizeof(*event));
@@ -346,24 +602,32 @@ static struct event *alloc_event(void)
return event;
}
-enum event_type {
- EVENT_ERROR,
- EVENT_NONE,
- EVENT_SPACE,
- EVENT_NEWLINE,
- EVENT_OP,
- EVENT_DELIM,
- EVENT_ITEM,
- EVENT_DQUOTE,
- EVENT_SQUOTE,
-};
+static void add_event(struct pevent *pevent, struct event_format *event)
+{
+ int i;
-static struct event *event_list;
+ 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));
-static void add_event(struct event *event)
-{
- event->next = event_list;
- event_list = event;
+ pevent->events[i] = event;
+ pevent->nr_events++;
+
+ event->pevent = pevent;
}
static int event_item_type(enum event_type type)
@@ -377,20 +641,67 @@ static int event_item_type(enum event_type type)
}
}
+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:
- if (arg->atom.atom)
- free(arg->atom.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:
+ 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:
- case PRINT_FIELD ... PRINT_OP:
default:
- /* todo */
break;
}
@@ -433,6 +744,18 @@ static int __peek_char(void)
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];
@@ -503,7 +826,7 @@ static enum event_type __read_token(char **tok)
ch = __peek_char();
if (ch == '=')
buf[i++] = __read_char();
- break;
+ goto out;
case EVENT_DQUOTE:
case EVENT_SQUOTE:
@@ -576,6 +899,54 @@ static enum event_type __read_token(char **tok)
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;
}
@@ -598,9 +969,33 @@ static enum event_type read_token(char **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)
{
@@ -610,51 +1005,43 @@ static enum event_type read_token_item(char **tok)
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) {
- warning("Error: expected type %d but read %d",
+ do_warning("Error: expected type %d but read %d",
expect, type);
return -1;
}
return 0;
}
-static int __test_type_token(enum event_type type, char *token,
- enum event_type expect, const char *expect_tok,
- bool warn)
+static int test_type_token(enum event_type type, const char *token,
+ enum event_type expect, const char *expect_tok)
{
if (type != expect) {
- if (warn)
- warning("Error: expected type %d but read %d",
- expect, type);
+ do_warning("Error: expected type %d but read %d",
+ expect, type);
return -1;
}
if (strcmp(token, expect_tok) != 0) {
- if (warn)
- warning("Error: expected '%s' but read '%s'",
- expect_tok, token);
+ do_warning("Error: expected '%s' but read '%s'",
+ expect_tok, token);
return -1;
}
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)
+static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
{
enum event_type type;
@@ -671,7 +1058,7 @@ static int read_expect_type(enum event_type expect, char **tok)
}
static int __read_expected(enum event_type expect, const char *str,
- int newline_ok, bool warn)
+ int newline_ok)
{
enum event_type type;
char *token;
@@ -682,7 +1069,7 @@ static int __read_expected(enum event_type expect, const char *str,
else
type = read_token_item(&token);
- ret = __test_type_token(type, token, expect, str, warn);
+ ret = test_type_token(type, token, expect, str);
free_token(token);
@@ -691,12 +1078,12 @@ static int __read_expected(enum event_type expect, const char *str,
static int read_expected(enum event_type expect, const char *str)
{
- return __read_expected(expect, str, 1, true);
+ return __read_expected(expect, str, 1);
}
static int read_expected_item(enum event_type expect, const char *str)
{
- return __read_expected(expect, str, 0, true);
+ return __read_expected(expect, str, 0);
}
static char *event_read_name(void)
@@ -745,8 +1132,8 @@ static int event_read_id(void)
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")))
+ (strstr(field->type, "char") || strstr(field->type, "u8") ||
+ strstr(field->type, "s8")))
return 1;
return 0;
@@ -754,13 +1141,22 @@ static int field_is_string(struct format_field *field)
static int field_is_dynamic(struct format_field *field)
{
- if (!strncmp(field->type, "__data_loc", 10))
+ 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 *event, struct format_field **fields)
+static int event_read_fields(struct event_format *event, struct format_field **fields)
{
struct format_field *field = NULL;
enum event_type type;
@@ -793,8 +1189,9 @@ static int event_read_fields(struct event *event, struct format_field **fields)
}
if (test_type_token(type, token, EVENT_OP, ":") < 0)
- return -1;
+ goto fail;
+ free_token(token);
if (read_expect_type(EVENT_ITEM, &token) < 0)
goto fail;
@@ -802,6 +1199,7 @@ static int event_read_fields(struct event *event, struct format_field **fields)
field = malloc_or_die(sizeof(*field));
memset(field, 0, sizeof(*field));
+ field->event = event;
/* read the rest of the type */
for (;;) {
@@ -824,6 +1222,7 @@ static int event_read_fields(struct event *event, struct format_field **fields)
strlen(last_token) + 2);
strcat(field->type, " ");
strcat(field->type, last_token);
+ free(last_token);
} else
field->type = last_token;
last_token = token;
@@ -850,6 +1249,12 @@ static int event_read_fields(struct event *event, struct format_field **fields)
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)
@@ -864,6 +1269,8 @@ static int event_read_fields(struct event *event, struct format_field **fields)
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) {
@@ -904,11 +1311,12 @@ static int event_read_fields(struct event *event, struct format_field **fields)
free(brackets);
}
- if (field_is_string(field)) {
+ if (field_is_string(field))
field->flags |= FIELD_IS_STRING;
- if (field_is_dynamic(field))
- field->flags |= FIELD_IS_DYNAMIC;
- }
+ 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;
@@ -956,8 +1364,7 @@ static int event_read_fields(struct event *event, struct format_field **fields)
if (read_expect_type(EVENT_ITEM, &token))
goto fail;
- if (strtoul(token, NULL, 0))
- field->flags |= FIELD_IS_SIGNED;
+ /* add signed type */
free_token(token);
if (read_expected(EVENT_OP, ";") < 0)
@@ -969,6 +1376,16 @@ static int event_read_fields(struct event *event, struct format_field **fields)
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;
@@ -984,7 +1401,7 @@ fail_expect:
return -1;
}
-static int event_read_format(struct event *event)
+static int event_read_format(struct event_format *event)
{
char *token;
int ret;
@@ -1016,12 +1433,12 @@ static int event_read_format(struct event *event)
return -1;
}
-enum event_type
-process_arg_token(struct event *event, struct print_arg *arg,
+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 *event, struct print_arg *arg, char **tok)
+process_arg(struct event_format *event, struct print_arg *arg, char **tok)
{
enum event_type type;
char *token;
@@ -1033,18 +1450,18 @@ process_arg(struct event *event, struct print_arg *arg, char **tok)
}
static enum event_type
-process_cond(struct event *event, struct print_arg *top, char **tok)
+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 = malloc_or_die(sizeof(*arg));
- memset(arg, 0, sizeof(*arg));
-
- left = malloc_or_die(sizeof(*left));
-
- right = malloc_or_die(sizeof(*right));
+ arg = alloc_arg();
+ left = alloc_arg();
+ right = alloc_arg();
arg->type = PRINT_OP;
arg->op.left = left;
@@ -1052,6 +1469,14 @@ process_cond(struct event *event, struct print_arg *top, char **tok)
*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;
@@ -1065,22 +1490,21 @@ process_cond(struct event *event, struct print_arg *top, char **tok)
return type;
out_free:
- free_token(*tok);
- free(right);
- free(left);
+ /* 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 *event, struct print_arg *top, char **tok)
+process_array(struct event_format *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));
+ arg = alloc_arg();
*tok = NULL;
type = process_arg(event, arg, &token);
@@ -1097,6 +1521,7 @@ process_array(struct event *event, struct print_arg *top, char **tok)
out_free:
free_token(*tok);
+ *tok = NULL;
free_arg(arg);
return EVENT_ERROR;
}
@@ -1165,8 +1590,9 @@ static void set_op_prio(struct print_arg *arg)
arg->op.prio = get_op_prio(arg->op.op);
}
+/* Note, *tok does not get freed, but will most likely be saved */
static enum event_type
-process_op(struct event *event, struct print_arg *arg, char **tok)
+process_op(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *left, *right = NULL;
enum event_type type;
@@ -1179,7 +1605,7 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
/* handle single op */
if (token[1]) {
die("bad op token %s", token);
- return EVENT_ERROR;
+ goto out_free;
}
switch (token[0]) {
case '!':
@@ -1187,23 +1613,26 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
case '-':
break;
default:
- die("bad op token %s", token);
- return EVENT_ERROR;
+ do_warning("bad op token %s", token);
+ goto out_free;
+
}
/* make an empty left */
- left = malloc_or_die(sizeof(*left));
+ left = alloc_arg();
left->type = PRINT_NULL;
arg->op.left = left;
- right = malloc_or_die(sizeof(*right));
+ 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 = malloc_or_die(sizeof(*left));
+ left = alloc_arg();
/* copy the top arg to the left */
*left = *arg;
@@ -1230,7 +1659,7 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
strcmp(token, "==") == 0 ||
strcmp(token, "!=") == 0) {
- left = malloc_or_die(sizeof(*left));
+ left = alloc_arg();
/* copy the top arg to the left */
*left = *arg;
@@ -1241,8 +1670,6 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
set_op_prio(arg);
- right = malloc_or_die(sizeof(*right));
-
type = read_token_item(&token);
*tok = token;
@@ -1252,21 +1679,22 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
if (left->type != PRINT_ATOM)
die("bad pointer type");
left->atom.atom = realloc(left->atom.atom,
- sizeof(left->atom.atom) + 3);
+ strlen(left->atom.atom) + 3);
strcat(left->atom.atom, " *");
+ free(arg->op.op);
*arg = *left;
- free(arg);
+ 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 = malloc_or_die(sizeof(*left));
+ left = alloc_arg();
*left = *arg;
arg->type = PRINT_OP;
@@ -1274,16 +1702,17 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
arg->op.left = left;
arg->op.prio = 0;
+
type = process_array(event, arg, tok);
} else {
- warning("unknown op '%s'", token);
+ do_warning("unknown op '%s'", token);
event->flags |= EVENT_FL_FAILED;
/* the arg is now the left side */
- return EVENT_NONE;
+ goto out_free;
}
- if (type == EVENT_OP) {
+ if (type == EVENT_OP && strcmp(*tok, ":") != 0) {
int prio;
/* higher prios need to be closer to the root */
@@ -1296,10 +1725,15 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
}
return type;
+
+ out_free:
+ free_token(token);
+ *tok = NULL;
+ return EVENT_ERROR;
}
static enum event_type
-process_entry(struct event *event __unused, struct print_arg *arg,
+process_entry(struct event_format *event __unused, struct print_arg *arg,
char **tok)
{
enum event_type type;
@@ -1307,37 +1741,128 @@ process_entry(struct event *event __unused, struct print_arg *arg,
char *token;
if (read_expected(EVENT_OP, "->") < 0)
- return EVENT_ERROR;
+ goto out_err;
if (read_expect_type(EVENT_ITEM, &token) < 0)
- goto fail;
+ goto out_free;
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:
+ 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 long long arg_num_eval(struct print_arg *arg)
{
long long left, right;
@@ -1349,6 +1874,7 @@ static long long arg_num_eval(struct print_arg *arg)
break;
case PRINT_TYPE:
val = arg_num_eval(arg->typecast.item);
+ val = eval_type(val, arg, 0);
break;
case PRINT_OP:
switch (arg->op.op[0]) {
@@ -1423,6 +1949,15 @@ static long long arg_num_eval(struct print_arg *arg)
die("unknown op '%s'", arg->op.op);
}
break;
+ case '-':
+ /* check for negative */
+ if (arg->op.left->type == PRINT_NULL)
+ left = 0;
+ else
+ 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);
}
@@ -1465,12 +2000,12 @@ static char *arg_eval (struct print_arg *arg)
}
static enum event_type
-process_fields(struct event *event, struct print_flag_sym **list, char **tok)
+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 = NULL;
+ char *token = *tok;
char *value;
do {
@@ -1479,7 +2014,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
if (test_type_token(type, token, EVENT_OP, "{"))
break;
- arg = malloc_or_die(sizeof(*arg));
+ arg = alloc_arg();
free_token(token);
type = process_arg(event, arg, &token);
@@ -1487,11 +2022,14 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
goto out_free;
field = malloc_or_die(sizeof(*field));
- memset(field, 0, sizeof(*field));
+ memset(field, 0, sizeof(field));
value = arg_eval(arg);
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, "}"))
@@ -1515,12 +2053,13 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
out_free:
free_arg(arg);
free_token(token);
+ *tok = NULL;
return EVENT_ERROR;
}
static enum event_type
-process_flags(struct event *event, struct print_arg *arg, char **tok)
+process_flags(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
@@ -1529,14 +2068,12 @@ process_flags(struct event *event, struct print_arg *arg, char **tok)
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));
+ field = alloc_arg();
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
+ free_token(token);
arg->flags.field = field;
@@ -1557,13 +2094,14 @@ process_flags(struct event *event, struct print_arg *arg, char **tok)
type = read_token_item(tok);
return type;
-out_free:
+ out_free:
free_token(token);
+ *tok = NULL;
return EVENT_ERROR;
}
static enum event_type
-process_symbols(struct event *event, struct print_arg *arg, char **tok)
+process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
@@ -1572,10 +2110,7 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok)
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));
+ field = alloc_arg();
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
@@ -1591,13 +2126,70 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok)
type = read_token_item(tok);
return type;
-out_free:
+ 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;
+
+ 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 *event, struct print_arg *arg, char **tok)
+process_paren(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *item_arg;
enum event_type type;
@@ -1606,18 +2198,16 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
type = process_arg(event, arg, &token);
if (type == EVENT_ERROR)
- return EVENT_ERROR;
+ goto out_free;
if (type == EVENT_OP)
type = process_op(event, arg, &token);
if (type == EVENT_ERROR)
- return EVENT_ERROR;
+ goto out_free;
- if (test_type_token(type, token, EVENT_DELIM, ")")) {
- free_token(token);
- return EVENT_ERROR;
- }
+ if (test_type_token(type, token, EVENT_DELIM, ")"))
+ goto out_free;
free_token(token);
type = read_token_item(&token);
@@ -1635,7 +2225,7 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
if (arg->type != PRINT_ATOM)
die("previous needed to be PRINT_ATOM");
- item_arg = malloc_or_die(sizeof(*item_arg));
+ item_arg = alloc_arg();
arg->type = PRINT_TYPE;
arg->typecast.type = arg->atom.atom;
@@ -1646,39 +2236,148 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
*tok = token;
return type;
+
+ out_free:
+ free_token(token);
+ *tok = NULL;
+ return EVENT_ERROR;
}
static enum event_type
-process_str(struct event *event __unused, struct print_arg *arg, char **tok)
+process_str(struct event_format *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;
+ goto out_free;
arg->type = PRINT_STRING;
arg->string.string = token;
arg->string.offset = -1;
if (read_expected(EVENT_DELIM, ")") < 0)
- return EVENT_ERROR;
+ goto out_err;
type = read_token(&token);
*tok = token;
return type;
-fail:
+
+ 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);
+ }
+
+ 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);
+ return process_flags(event, arg, tok);
+ }
+ if (strcmp(token, "__print_symbolic") == 0) {
+ free_token(token);
+ 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;
}
-enum event_type
-process_arg_token(struct event *event, struct print_arg *arg,
+static enum event_type
+process_arg_token(struct event_format *event, struct print_arg *arg,
char **tok, enum event_type type)
{
char *token;
@@ -1691,37 +2390,36 @@ process_arg_token(struct event *event, struct print_arg *arg,
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) {
+ 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);
- is_symbolic_field = 1;
- type = process_symbols(event, arg, &token);
- } else if (strcmp(token, "__get_str") == 0) {
+ 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 = 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;
}
+
+ arg->type = PRINT_ATOM;
+ arg->atom.atom = atom;
break;
+
case EVENT_DQUOTE:
case EVENT_SQUOTE:
arg->type = PRINT_ATOM;
@@ -1741,6 +2439,11 @@ process_arg_token(struct event *event, struct print_arg *arg,
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:
@@ -1752,7 +2455,7 @@ process_arg_token(struct event *event, struct print_arg *arg,
return type;
}
-static int event_read_print_args(struct event *event, struct print_arg **list)
+static int event_read_print_args(struct event_format *event, struct print_arg **list)
{
enum event_type type = EVENT_ERROR;
struct print_arg *arg;
@@ -1761,17 +2464,16 @@ static int event_read_print_args(struct event *event, struct print_arg **list)
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));
+ arg = alloc_arg();
type = process_arg(event, arg, &token);
if (type == EVENT_ERROR) {
+ free_token(token);
free_arg(arg);
return -1;
}
@@ -1781,6 +2483,12 @@ static int event_read_print_args(struct event *event, struct print_arg **list)
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;
}
@@ -1794,13 +2502,13 @@ static int event_read_print_args(struct event *event, struct print_arg **list)
break;
} while (type != EVENT_NONE);
- if (type != EVENT_NONE)
+ if (type != EVENT_NONE && type != EVENT_ERROR)
free_token(token);
return args;
}
-static int event_read_print(struct event *event)
+static int event_read_print(struct event_format *event)
{
enum event_type type;
char *token;
@@ -1828,7 +2536,7 @@ static int event_read_print(struct event *event)
if (type == EVENT_NONE)
return 0;
- /* Handle concatination of print lines */
+ /* Handle concatenation of print lines */
if (type == EVENT_DQUOTE) {
char *cat;
@@ -1842,7 +2550,7 @@ static int event_read_print(struct event *event)
token = cat;
goto concat;
}
-
+
if (test_type_token(type, token, EVENT_DELIM, ","))
goto fail;
@@ -1859,8 +2567,16 @@ static int event_read_print(struct event *event)
return -1;
}
-static struct format_field *
-find_common_field(struct event *event, const char *name)
+/**
+ * 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;
@@ -1873,9 +2589,17 @@ find_common_field(struct event *event, const char *name)
return format;
}
-static struct format_field *
-find_field(struct event *event, const char *name)
-{
+/**
+ * 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;
@@ -1887,80 +2611,95 @@ find_field(struct event *event, const char *name)
return format;
}
-static struct format_field *
-find_any_field(struct event *event, const char *name)
+/**
+ * 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 = find_common_field(event, name);
+ format = pevent_find_common_field(event, name);
if (format)
return format;
- return find_field(event, name);
+ return pevent_find_field(event, name);
}
-unsigned long long read_size(void *ptr, int size)
+/**
+ * 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(ptr);
+ return data2host2(pevent, ptr);
case 4:
- return data2host4(ptr);
+ return data2host4(pevent, ptr);
case 8:
- return data2host8(ptr);
+ return data2host8(pevent, 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)
+/**
+ * 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)
{
- 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;
+ 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;
}
-
- return data + field->offset;
}
-static int get_common_info(const char *type, int *offset, int *size)
+static int get_common_info(struct pevent *pevent,
+ const char *type, int *offset, int *size)
{
- struct event *event;
+ 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 (!event_list)
+ if (!pevent->events)
die("no event_list!");
- event = event_list;
- field = find_common_field(event, type);
+ event = pevent->events[0];
+ field = pevent_find_common_field(event, type);
if (!field)
die("field '%s' not found", type);
@@ -1970,62 +2709,53 @@ static int get_common_info(const char *type, int *offset, int *size)
return 0;
}
-static int __parse_common(void *data, int *size, int *offset,
- const char *name)
+static int __parse_common(struct pevent *pevent, void *data,
+ int *size, int *offset, const char *name)
{
int ret;
if (!*size) {
- ret = get_common_info(name, offset, size);
+ ret = get_common_info(pevent, name, offset, size);
if (ret < 0)
return ret;
}
- return read_size(data + *offset, *size);
+ return pevent_read_number(pevent, data + *offset, *size);
}
-int trace_parse_common_type(void *data)
+static int trace_parse_common_type(struct pevent *pevent, void *data)
{
- static int type_offset;
- static int type_size;
-
- return __parse_common(data, &type_size, &type_offset,
+ return __parse_common(pevent, data,
+ &pevent->type_size, &pevent->type_offset,
"common_type");
}
-int trace_parse_common_pid(void *data)
+static int parse_common_pid(struct pevent *pevent, void *data)
{
- static int pid_offset;
- static int pid_size;
-
- return __parse_common(data, &pid_size, &pid_offset,
+ return __parse_common(pevent, data,
+ &pevent->pid_size, &pevent->pid_offset,
"common_pid");
}
-int parse_common_pc(void *data)
+static int parse_common_pc(struct pevent *pevent, void *data)
{
- static int pc_offset;
- static int pc_size;
-
- return __parse_common(data, &pc_size, &pc_offset,
+ return __parse_common(pevent, data,
+ &pevent->pc_size, &pevent->pc_offset,
"common_preempt_count");
}
-int parse_common_flags(void *data)
+static int parse_common_flags(struct pevent *pevent, void *data)
{
- static int flags_offset;
- static int flags_size;
-
- return __parse_common(data, &flags_size, &flags_offset,
+ return __parse_common(pevent, data,
+ &pevent->flags_size, &pevent->flags_offset,
"common_flags");
}
-int parse_common_lock_depth(void *data)
+static int parse_common_lock_depth(struct pevent *pevent, void *data)
{
- static int ld_offset;
- static int ld_size;
int ret;
- ret = __parse_common(data, &ld_size, &ld_offset,
+ ret = __parse_common(pevent, data,
+ &pevent->ld_size, &pevent->ld_offset,
"common_lock_depth");
if (ret < 0)
return -1;
@@ -2033,31 +2763,85 @@ int parse_common_lock_depth(void *data)
return ret;
}
-struct event *trace_find_event(int id)
+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 *event;
+ struct event_format **eventptr;
+ struct event_format key;
+ struct event_format *pkey = &key;
- for (event = event_list; event; event = event->next) {
- if (event->id == id)
- break;
+ /* 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 event;
+
+ return NULL;
}
-struct event *trace_find_next_event(struct event *event)
+/**
+ * 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)
{
- if (!event)
- return event_list;
+ 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;
- return event->next;
+ pevent->last_event = event;
+ return event;
}
-static unsigned long long eval_num_arg(void *data, int size,
- struct event *event, struct print_arg *arg)
+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:
@@ -2067,40 +2851,87 @@ static unsigned long long eval_num_arg(void *data, int size,
return strtoull(arg->atom.atom, NULL, 0);
case PRINT_FIELD:
if (!arg->field.field) {
- arg->field.field = find_any_field(event, arg->field.name);
+ 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 = read_size(data + arg->field.field->offset,
+ 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:
- return eval_num_arg(data, size, event, arg->typecast.item);
+ val = eval_num_arg(data, size, event, arg->typecast.item);
+ return eval_type(val, arg, 0);
case PRINT_STRING:
return 0;
- break;
+ case PRINT_FUNC: {
+ struct trace_seq s;
+ trace_seq_init(&s);
+ return process_defined_func(&s, data, size, event, arg);
+ }
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 */
+ right = eval_num_arg(data, size, event, arg->op.right);
+
+ /* handle typecasts */
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);
+ while (larg->type == PRINT_TYPE) {
+ if (!typearg)
+ typearg = larg;
+ larg = larg->typecast.item;
}
- right = eval_num_arg(data, size, event, arg->op.right);
- val = read_size(data + larg->field.field->offset +
- right * long_size, long_size);
+
+ /* 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:
@@ -2191,7 +3022,7 @@ static const struct flag flags[] = {
{ "HRTIMER_RESTART", 1 },
};
-unsigned long long eval_flag(const char *flag)
+static unsigned long long eval_flag(const char *flag)
{
int i;
@@ -2210,32 +3041,48 @@ unsigned long long eval_flag(const char *flag)
return 0;
}
-static void print_str_arg(void *data, int size,
- struct event *event, struct print_arg *arg)
+static void print_str_arg(struct trace_seq *s, void *data, int size,
+ struct event_format *event, 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:
- printf("%s", arg->atom.atom);
+ trace_seq_puts(s, arg->atom.atom);
return;
case PRINT_FIELD:
if (!arg->field.field) {
- arg->field.field = find_any_field(event, arg->field.name);
+ arg->field.field = pevent_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);
+ /* Zero sized fields, mean the rest of the data */
+ len = arg->field.field->size ? : size;
+
+ /*
+ * 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) &&
+ len == 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;
+ trace_seq_puts(s, str);
free(str);
break;
case PRINT_FLAGS:
@@ -2244,13 +3091,13 @@ static void print_str_arg(void *data, int size,
for (flag = arg->flags.flags; flag; flag = flag->next) {
fval = eval_flag(flag->value);
if (!val && !fval) {
- printf("%s", flag->str);
+ trace_seq_puts(s, flag->str);
break;
}
if (fval && (val & fval) == fval) {
if (print && arg->flags.delim)
- printf("%s", arg->flags.delim);
- printf("%s", flag->str);
+ trace_seq_puts(s, arg->flags.delim);
+ trace_seq_puts(s, flag->str);
print = 1;
val &= ~fval;
}
@@ -2261,7 +3108,7 @@ static void print_str_arg(void *data, int size,
for (flag = arg->symbol.symbols; flag; flag = flag->next) {
fval = eval_flag(flag->value);
if (val == fval) {
- printf("%s", flag->str);
+ trace_seq_puts(s, flag->str);
break;
}
}
@@ -2275,12 +3122,12 @@ static void print_str_arg(void *data, int size,
if (arg->string.offset == -1) {
struct format_field *f;
- f = find_any_field(event, arg->string.string);
+ f = pevent_find_any_field(event, arg->string.string);
arg->string.offset = f->offset;
}
- str_offset = *(int *)(data + arg->string.offset);
+ str_offset = data2host4(pevent, data + arg->string.offset);
str_offset &= 0xffff;
- printf("%s", ((char *)data) + str_offset);
+ trace_seq_puts(s, ((char *)data) + str_offset);
break;
}
case PRINT_OP:
@@ -2291,9 +3138,12 @@ static void print_str_arg(void *data, int size,
return;
val = eval_num_arg(data, size, event, arg->op.left);
if (val)
- print_str_arg(data, size, event, arg->op.right->op.left);
+ print_str_arg(s, data, size, event, arg->op.right->op.left);
else
- print_str_arg(data, size, event, arg->op.right->op.right);
+ print_str_arg(s, data, size, event, arg->op.right->op.right);
+ break;
+ case PRINT_FUNC:
+ process_defined_func(s, data, size, event, arg);
break;
default:
/* well... */
@@ -2301,29 +3151,101 @@ static void print_str_arg(void *data, int size,
}
}
-static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event)
+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, farg);
+ trace_seq_terminate(&str);
+ string = malloc_or_die(sizeof(*string));
+ string->next = strings;
+ string->str = strdup(str.buffer);
+ strings = string;
+ 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)
{
- static struct format_field *field, *ip_field;
+ 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 = find_field(event, "buf");
+ field = pevent_find_field(event, "buf");
if (!field)
die("can't find buffer field for binary printk");
- ip_field = find_field(event, "ip");
+ 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 = read_size(data + ip_field->offset, ip_field->size);
+ ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size);
/*
* The first arg is the IP pointer.
*/
- args = malloc_or_die(sizeof(*args));
+ args = alloc_arg();
arg = args;
arg->next = NULL;
next = &arg->next;
@@ -2364,16 +3286,16 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
switch (ls) {
case 0:
case 1:
- ls = long_size;
+ ls = pevent->long_size;
break;
case 2:
ls = 8;
default:
break;
}
- val = read_size(bptr, ls);
+ val = pevent_read_number(pevent, bptr, ls);
bptr += ls;
- arg = malloc_or_die(sizeof(*arg));
+ arg = alloc_arg();
arg->next = NULL;
arg->type = PRINT_ATOM;
arg->atom.atom = malloc_or_die(32);
@@ -2382,7 +3304,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
next = &arg->next;
break;
case 's':
- arg = malloc_or_die(sizeof(*arg));
+ arg = alloc_arg();
arg->next = NULL;
arg->type = PRINT_STRING;
arg->string.string = strdup(bptr);
@@ -2405,33 +3327,34 @@ static void free_args(struct print_arg *args)
while (args) {
next = args->next;
- if (args->type == PRINT_ATOM)
- free(args->atom.atom);
- else
- free(args->string.string);
- free(args);
+ free_arg(args);
args = next;
}
}
-static char *get_bprint_format(void *data, int size __unused, struct event *event)
+static char *
+get_bprint_format(void *data, int size __unused, struct event_format *event)
{
+ struct pevent *pevent = event->pevent;
unsigned long long addr;
- static struct format_field *field;
+ struct format_field *field;
struct printk_map *printk;
char *format;
char *p;
+ field = pevent->bprint_fmt_field;
+
if (!field) {
- field = find_field(event, "fmt");
+ field = pevent_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);
+ pevent->bprint_fmt_field = field;
}
- addr = read_size(data + field->offset, field->size);
+ addr = pevent_read_number(pevent, data + field->offset, field->size);
- printk = find_printk(addr);
+ printk = find_printk(pevent, addr);
if (!printk) {
format = malloc_or_die(45);
sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
@@ -2457,8 +3380,110 @@ static char *get_bprint_format(void *data, int size __unused, struct event *even
return format;
}
-static void pretty_print(void *data, int size, struct event *event)
+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;
@@ -2472,8 +3497,11 @@ static void pretty_print(void *data, int size, struct event *event)
int len;
int ls;
- if (event->flags & EVENT_FL_ISFUNC)
- ptr = " %pF <-- %pF";
+ 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);
@@ -2488,19 +3516,19 @@ static void pretty_print(void *data, int size, struct event *event)
ptr++;
switch (*ptr) {
case 'n':
- printf("\n");
+ trace_seq_putc(s, '\n');
break;
case 't':
- printf("\t");
+ trace_seq_putc(s, '\t');
break;
case 'r':
- printf("\r");
+ trace_seq_putc(s, '\r');
break;
case '\\':
- printf("\\");
+ trace_seq_putc(s, '\\');
break;
default:
- printf("%c", *ptr);
+ trace_seq_putc(s, *ptr);
break;
}
@@ -2511,20 +3539,24 @@ static void pretty_print(void *data, int size, struct event *event)
ptr++;
switch (*ptr) {
case '%':
- printf("%%");
+ trace_seq_putc(s, '%');
break;
+ case '#':
+ /* FIXME: need to handle properly */
+ goto cont_process;
case 'l':
ls++;
goto cont_process;
case 'L':
ls = 2;
goto cont_process;
+ case '.':
case 'z':
case 'Z':
case '0' ... '9':
goto cont_process;
case 'p':
- if (long_size == 4)
+ if (pevent->long_size == 4)
ls = 1;
else
ls = 2;
@@ -2533,6 +3565,10 @@ static void pretty_print(void *data, int size, struct event *event)
*(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 */
@@ -2558,24 +3594,36 @@ static void pretty_print(void *data, int size, struct event *event)
arg = arg->next;
if (show_func) {
- func = find_func(val);
+ func = find_func(pevent, val);
if (func) {
- printf("%s", func->func);
+ trace_seq_puts(s, func->func);
if (show_func == 'F')
- printf("+0x%llx",
+ 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 0:
- printf(format, (int)val);
+ trace_seq_printf(s, format, (int)val);
break;
case 1:
- printf(format, (long)val);
+ trace_seq_printf(s, format, (long)val);
break;
case 2:
- printf(format, (long long)val);
+ trace_seq_printf(s, format, (long long)val);
break;
default:
die("bad count (%d)", ls);
@@ -2585,15 +3633,15 @@ static void pretty_print(void *data, int size, struct event *event)
if (!arg)
die("no matching argument");
- print_str_arg(data, size, event, arg);
+ print_str_arg(s, data, size, event, arg);
arg = arg->next;
break;
default:
- printf(">%c<", *ptr);
+ trace_seq_printf(s, ">%c<", *ptr);
}
} else
- printf("%c", *ptr);
+ trace_seq_putc(s, *ptr);
}
if (args) {
@@ -2602,31 +3650,49 @@ static void pretty_print(void *data, int size, struct event *event)
}
}
-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)
+/**
+ * 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 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;
-
- lat_flags = parse_common_flags(data);
- pc = parse_common_pc(data);
- lock_depth = parse_common_lock_depth(data);
+ 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;
- printf("%c%c%c",
+ trace_seq_printf(s, "%c%c%c",
(lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
(lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
'X' : '.',
@@ -2636,414 +3702,314 @@ static void print_lat_fmt(void *data, int size __unused)
hardirq ? 'h' : softirq ? 's' : '.');
if (pc)
- printf("%x", pc);
+ trace_seq_printf(s, "%x", pc);
else
- printf(".");
+ trace_seq_putc(s, '.');
- if (lock_depth < 0)
- printf(".");
- else
- printf("%d", lock_depth);
+ if (lock_depth_exists) {
+ if (lock_depth < 0)
+ trace_seq_putc(s, '.');
+ else
+ trace_seq_printf(s, "%d", lock_depth);
+ }
+
+ trace_seq_terminate(s);
}
-/* taken from Linux, written by Frederic Weisbecker */
-static void print_graph_cpu(int cpu)
+/**
+ * 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 record *rec)
{
- int i;
- int log10_this = log10_cpu(cpu);
- int log10_all = log10_cpu(cpus);
-
-
- /*
- * Start with a space character - to make it stand out
- * to the right a bit when trace output is pasted into
- * email:
- */
- printf(" ");
-
- /*
- * Tricky - we space the CPU field according to the max
- * number of online CPUs. On a 2-cpu system it would take
- * a maximum of 1 digit - on a 128 cpu system it would
- * take up to 3 digits:
- */
- for (i = 0; i < log10_all - log10_this; i++)
- printf(" ");
-
- printf("%d) ", cpu);
+ return trace_parse_common_type(pevent, rec->data);
}
-#define TRACE_GRAPH_PROCINFO_LENGTH 14
-#define TRACE_GRAPH_INDENT 2
-
-static void print_graph_proc(int pid, const char *comm)
+/**
+ * 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)
{
- /* sign + log10(MAX_INT) + '\0' */
- char pid_str[11];
- int spaces = 0;
- int len;
- int i;
-
- sprintf(pid_str, "%d", pid);
-
- /* 1 stands for the "-" character */
- len = strlen(comm) + strlen(pid_str) + 1;
-
- if (len < TRACE_GRAPH_PROCINFO_LENGTH)
- spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
-
- /* First spaces to align center */
- for (i = 0; i < spaces / 2; i++)
- printf(" ");
-
- printf("%s-%s", comm, pid_str);
-
- /* Last spaces to align center */
- for (i = 0; i < spaces - (spaces / 2); i++)
- printf(" ");
+ return pevent_find_event(pevent, type);
}
-static struct record *
-get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
- struct record *next)
+/**
+ * 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 record *rec)
{
- 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);
+ return parse_common_pid(pevent, rec->data);
}
-/* Signal a overhead of time execution to the output */
-static void print_graph_overhead(unsigned long long duration)
+/**
+ * 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)
{
- /* 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("+ ");
+ const char *comm;
- printf(" ");
+ comm = find_cmdline(pevent, pid);
+ return comm;
}
-static void print_graph_duration(unsigned long long duration)
+/**
+ * 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 record *record)
{
- 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;
+ int print_pretty = 1;
- sprintf(msecs_str, "%lu", usecs);
+ if (event->pevent->print_raw)
+ print_event_fields(s, record->data, record->size, event);
+ else {
- /* Print msecs */
- len = printf("%lu", usecs);
+ if (event->handler)
+ print_pretty = event->handler(s, record, event,
+ event->context);
- /* 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);
+ if (print_pretty)
+ pretty_print(s, record->data, record->size, event);
}
- printf(" us ");
-
- /* Print remaining spaces to fit the row's width */
- for (i = len; i < 7; i++)
- printf(" ");
-
- printf("| ");
+ trace_seq_terminate(s);
}
-static void
-print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
+void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
+ struct record *record)
{
- 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;
+ static char *spaces = " "; /* 20 spaces */
+ struct event_format *event;
+ unsigned long secs;
+ unsigned long usecs;
+ const char *comm;
+ void *data = record->data;
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);
+ int pid;
+ int len;
- 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);
+ secs = record->ts / NSECS_PER_SEC;
+ usecs = record->ts - secs * NSECS_PER_SEC;
+ usecs = (usecs + 500) / NSECS_PER_USEC;
- duration = rettime - calltime;
+ type = trace_parse_common_type(pevent, data);
- /* Overhead */
- print_graph_overhead(duration);
+ event = pevent_find_event(pevent, type);
+ if (!event) {
+ do_warning("ug! no event found for type %d", type);
+ return;
+ }
- /* Duration */
- print_graph_duration(duration);
+ pid = parse_common_pid(pevent, data);
+ comm = find_cmdline(pevent, pid);
- field = find_field(event, "depth");
- if (!field)
- die("can't find depth in entry graph");
- depth = read_size(data + field->offset, field->size);
+ 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);
- /* Function */
- for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
- printf(" ");
+ trace_seq_printf(s, " %5lu.%06lu: %s: ", secs, usecs, event->name);
- 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);
+ /* Space out the event names evenly. */
+ len = strlen(event->name);
+ if (len < 20)
+ trace_seq_printf(s, "%.*s", 20 - len, spaces);
- if (func)
- printf("%s();", func->func);
- else
- printf("%llx();", val);
+ pevent_event_info(s, event, record);
}
-static void print_graph_nested(struct event *event, void *data)
+static int events_id_cmp(const void *a, const void *b)
{
- 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);
+ struct event_format * const * ea = a;
+ struct event_format * const * eb = b;
- /* Function */
- for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
- printf(" ");
+ if ((*ea)->id < (*eb)->id)
+ return -1;
- 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 ((*ea)->id > (*eb)->id)
+ return 1;
- if (func)
- printf("%s() {", func->func);
- else
- printf("%llx() {", val);
+ return 0;
}
-static void
-pretty_print_func_ent(void *data, int size, struct event *event,
- int cpu, int pid, const char *comm,
- unsigned long secs, unsigned long usecs)
+static int events_name_cmp(const void *a, const void *b)
{
- struct format_field *field;
- struct record *rec;
- void *copy_data;
- unsigned long val;
-
- printf("%5lu.%06lu | ", secs, usecs);
-
- print_graph_cpu(cpu);
- print_graph_proc(pid, comm);
-
- printf(" | ");
-
- if (latency_format) {
- print_lat_fmt(data, size);
- printf(" | ");
- }
+ struct event_format * const * ea = a;
+ struct event_format * const * eb = b;
+ int res;
- field = find_field(event, "func");
- if (!field)
- die("function entry does not have func field");
+ res = strcmp((*ea)->name, (*eb)->name);
+ if (res)
+ return res;
- val = read_size(data + field->offset, field->size);
+ res = strcmp((*ea)->system, (*eb)->system);
+ if (res)
+ return res;
- /*
- * 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);
+ return events_id_cmp(a, b);
}
-static void
-pretty_print_func_ret(void *data, int size __unused, struct event *event,
- int cpu, int pid, const char *comm,
- unsigned long secs, unsigned long usecs)
+static int events_system_cmp(const void *a, const void *b)
{
- unsigned long long rettime, calltime;
- unsigned long long duration, depth;
- struct format_field *field;
- int i;
+ struct event_format * const * ea = a;
+ struct event_format * const * eb = b;
+ int res;
- printf("%5lu.%06lu | ", secs, usecs);
+ res = strcmp((*ea)->system, (*eb)->system);
+ if (res)
+ return res;
- print_graph_cpu(cpu);
- print_graph_proc(pid, comm);
+ res = strcmp((*ea)->name, (*eb)->name);
+ if (res)
+ return res;
- printf(" | ");
+ return events_id_cmp(a, b);
+}
- if (latency_format) {
- print_lat_fmt(data, size);
- printf(" | ");
- }
+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);
- field = find_field(event, "rettime");
- if (!field)
- die("can't find rettime in return graph");
- rettime = read_size(data + field->offset, field->size);
+ events = pevent->sort_events;
- field = find_field(event, "calltime");
- if (!field)
- die("can't find calltime in return graph");
- calltime = read_size(data + field->offset, field->size);
+ if (events && pevent->last_type == sort_type)
+ return events;
- duration = rettime - calltime;
+ if (!events) {
+ events = malloc(sizeof(*events) * (pevent->nr_events + 1));
+ if (!events)
+ return NULL;
- /* Overhead */
- print_graph_overhead(duration);
+ memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events);
+ events[pevent->nr_events] = NULL;
- /* Duration */
- print_graph_duration(duration);
+ pevent->sort_events = events;
- field = find_field(event, "depth");
- if (!field)
- die("can't find depth in entry graph");
- depth = read_size(data + field->offset, field->size);
+ /* the internal events are sorted by id */
+ if (sort_type == EVENT_SORT_ID) {
+ pevent->last_type = sort_type;
+ return events;
+ }
+ }
- /* Function */
- for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
- printf(" ");
+ 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;
+ }
- printf("}");
-}
+ qsort(events, pevent->nr_events, sizeof(*events), sort);
+ pevent->last_type = sort_type;
-static void
-pretty_print_func_graph(void *data, int size, struct event *event,
- int cpu, int pid, const char *comm,
- unsigned long secs, unsigned long usecs)
-{
- if (event->flags & EVENT_FL_ISFUNCENT)
- pretty_print_func_ent(data, size, event,
- cpu, pid, comm, secs, usecs);
- else if (event->flags & EVENT_FL_ISFUNCRET)
- pretty_print_func_ret(data, size, event,
- cpu, pid, comm, secs, usecs);
- printf("\n");
+ return events;
}
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm)
+static struct format_field **
+get_event_fields(const char *type, const char *name,
+ int count, struct format_field *list)
{
- struct event *event;
- unsigned long secs;
- unsigned long usecs;
- int type;
- int pid;
-
- secs = nsecs / NSECS_PER_SEC;
- nsecs -= secs * NSECS_PER_SEC;
- usecs = nsecs / NSECS_PER_USEC;
-
- type = trace_parse_common_type(data);
+ struct format_field **fields;
+ struct format_field *field;
+ int i = 0;
- event = trace_find_event(type);
- if (!event) {
- warning("ug! no event found for type %d", type);
- return;
+ 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;
+ }
}
- pid = trace_parse_common_pid(data);
+ if (i != count)
+ do_warning("event %s has less %s fields than specified",
+ name, type);
- if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET))
- return pretty_print_func_graph(data, size, event, cpu,
- pid, comm, secs, usecs);
+ fields[i] = NULL;
- if (latency_format) {
- printf("%8.8s-%-5d %3d",
- comm, pid, cpu);
- print_lat_fmt(data, size);
- } else
- printf("%16s-%-5d [%03d]", comm, pid, cpu);
-
- printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+ return fields;
+}
- if (event->flags & EVENT_FL_FAILED) {
- printf("EVENT '%s' FAILED TO PARSE\n",
- event->name);
- return;
- }
+/**
+ * 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);
+}
- pretty_print(data, size, event);
- printf("\n");
+/**
+ * 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 print_flag_sym *field)
+static void print_fields(struct trace_seq *s, struct print_flag_sym *field)
{
- printf("{ %s, %s }", field->value, field->str);
+ trace_seq_printf(s, "{ %s, %s }", field->value, field->str);
if (field->next) {
- printf(", ");
- print_fields(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:
@@ -3059,14 +4025,18 @@ static void print_args(struct print_arg *args)
printf("__print_flags(");
print_args(args->flags.field);
printf(", %s, ", args->flags.delim);
- print_fields(args->flags.flags);
+ trace_seq_init(&s);
+ print_fields(&s, args->flags.flags);
+ trace_seq_do_printf(&s);
printf(")");
break;
case PRINT_SYMBOL:
printf("__print_symbolic(");
print_args(args->symbol.field);
printf(", ");
- print_fields(args->symbol.symbols);
+ trace_seq_init(&s);
+ print_fields(&s, args->symbol.symbols);
+ trace_seq_do_printf(&s);
printf(")");
break;
case PRINT_STRING:
@@ -3097,74 +4067,211 @@ static void print_args(struct print_arg *args)
}
}
-int parse_ftrace_file(char *buf, unsigned long size)
+static void parse_header_field(const char *field,
+ int *offset, int *size, int mandatory)
{
- struct format_field *field;
- struct print_arg *arg, **list;
- struct event *event;
- int ret;
+ unsigned long long save_input_buf_ptr;
+ unsigned long long save_input_buf_siz;
+ char *token;
+ int type;
- init_input_buf(buf, size);
+ save_input_buf_ptr = input_buf_ptr;
+ save_input_buf_siz = input_buf_siz;
- event = alloc_event();
- if (!event)
- return -ENOMEM;
+ if (read_expected(EVENT_ITEM, "field") < 0)
+ return;
+ if (read_expected(EVENT_OP, ":") < 0)
+ return;
- event->flags |= EVENT_FL_ISFTRACE;
+ /* type */
+ if (read_expect_type(EVENT_ITEM, &token) < 0)
+ goto fail;
+ free_token(token);
- event->name = event_read_name();
- if (!event->name)
- die("failed to read ftrace event name");
+ /*
+ * 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 (strcmp(event->name, "function") == 0)
- event->flags |= EVENT_FL_ISFUNC;
+ 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;
- else if (strcmp(event->name, "funcgraph_entry") == 0)
- event->flags |= EVENT_FL_ISFUNCENT;
+ if (strcmp(token, "signed") != 0)
+ goto fail;
- else if (strcmp(event->name, "funcgraph_exit") == 0)
- event->flags |= EVENT_FL_ISFUNCRET;
+ free_token(token);
- else if (strcmp(event->name, "bprint") == 0)
- event->flags |= EVENT_FL_ISBPRINT;
+ if (read_expected(EVENT_OP, ":") < 0)
+ return;
- event->id = event_read_id();
- if (event->id < 0)
- die("failed to read ftrace event id");
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
- add_event(event);
+ free_token(token);
+ if (read_expected(EVENT_OP, ";") < 0)
+ return;
- ret = event_read_format(event);
- if (ret < 0)
- die("failed to read ftrace event format");
+ if (read_expect_type(EVENT_NEWLINE, &token))
+ goto fail;
+ }
+ fail:
+ free_token(token);
+ return;
- ret = event_read_print(event);
- if (ret < 0)
- die("failed to read ftrace event print fmt");
+ discard:
+ input_buf_ptr = save_input_buf_ptr;
+ input_buf_siz = save_input_buf_siz;
+ *offset = 0;
+ *size = 0;
+ free_token(token);
+}
- /* 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;
+/**
+ * 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;
}
-int parse_event_file(char *buf, unsigned long size, char *sys)
+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)
{
- struct event *event;
+ 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);
@@ -3174,58 +4281,375 @@ int parse_event_file(char *buf, unsigned long size, char *sys)
return -ENOMEM;
event->name = event_read_name();
- if (!event->name)
- die("failed to read event 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) {
- warning("failed to read event format for %s", event->name);
+ 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) {
- warning("failed to read event print fmt for %s", event->name);
+ do_warning("failed to read event print fmt for %s",
+ event->name);
+ show_warning = 1;
goto event_failed;
}
+ show_warning = 1;
- event->system = strdup(sys);
+ 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);
- add_event(event);
return 0;
event_failed:
event->flags |= EVENT_FL_FAILED;
/* still add it even if it failed */
- add_event(event);
+ add_event(pevent, event);
+ 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;
}
-void parse_set_info(int nr_cpus, int long_sz)
+/**
+ * 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)
{
- cpus = nr_cpus;
- long_size = long_sz;
+ free_format_fields(format->common_fields);
+ free_format_fields(format->fields);
}
-int common_pc(struct scripting_context *context)
+static void free_event(struct event_format *event)
{
- return parse_common_pc(context->event_data);
+ free(event->name);
+ free(event->system);
+
+ free_formats(&event->format);
+
+ free(event->print_fmt.format);
+ free_args(event->print_fmt.args);
+
+ free(event);
}
-int common_flags(struct scripting_context *context)
+/**
+ * pevent_free - free a pevent handle
+ * @pevent: the pevent handle to free
+ */
+void pevent_free(struct pevent *pevent)
{
- return parse_common_flags(context->event_data);
+ struct cmdline_list *cmdlist = pevent->cmdlist, *cmdnext;
+ struct func_list *funclist = pevent->funclist, *funcnext;
+ struct printk_list *printklist = pevent->printklist, *printknext;
+ struct pevent_function_handler *func_handler;
+ struct event_handler *handle;
+ int i;
+
+ 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);
}
-int common_lock_depth(struct scripting_context *context)
+void pevent_unref(struct pevent *pevent)
{
- return parse_common_lock_depth(context->event_data);
+ pevent_free(pevent);
}
diff --git a/tools/lib/parse-events.h b/tools/lib/parse-events.h
index b60b4df..5c10208 100644
--- a/tools/lib/parse-events.h
+++ b/tools/lib/parse-events.h
@@ -1,24 +1,105 @@
-#ifndef __PERF_TRACE_EVENTS_H
-#define __PERF_TRACE_EVENTS_H
-
-#include <stdbool.h>
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef _PARSE_EVENTS_H
+#define _PARSE_EVENTS_H
+
+#include <stdarg.h>
+#include <regex.h>
+
+#ifndef __unused
+#define __unused __attribute__ ((unused))
+#endif
-#define __unused __attribute__((unused))
+/* ----------------------- trace_seq ----------------------- */
-#ifndef PAGE_MASK
-#define PAGE_MASK (page_size - 1)
+#ifndef TRACE_SEQ_SIZE
+#define TRACE_SEQ_SIZE 4096
#endif
-enum {
- RINGBUF_TYPE_PADDING = 29,
- RINGBUF_TYPE_TIME_EXTEND = 30,
- RINGBUF_TYPE_TIME_STAMP = 31,
+struct 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;
};
-#ifndef TS_SHIFT
-#define TS_SHIFT 27
-#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[TRACE_SEQ_SIZE];
+ unsigned int len;
+ unsigned int readpos;
+ int full;
+};
+
+static inline void
+trace_seq_init(struct trace_seq *s)
+{
+ s->len = 0;
+ s->readpos = 0;
+ s->full = 0;
+}
+
+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 record *record,
+ struct event_format *event,
+ void *context);
+
+typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
+typedef int (*pevent_plugin_unload_func)(void);
+
+#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
+#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
+#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 NSECS_PER_SEC 1000000000ULL
#define NSECS_PER_USEC 1000ULL
@@ -29,16 +110,20 @@ enum format_flags {
FIELD_IS_SIGNED = 4,
FIELD_IS_STRING = 8,
FIELD_IS_DYNAMIC = 16,
- FIELD_IS_FLAG = 32,
- FIELD_IS_SYMBOLIC = 64,
+ 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;
};
@@ -85,6 +170,11 @@ struct print_arg_symbol {
struct print_flag_sym *symbols;
};
+struct print_arg_dynarray {
+ struct format_field *field;
+ struct print_arg *index;
+};
+
struct print_arg;
struct print_arg_op {
@@ -94,9 +184,11 @@ struct print_arg_op {
struct print_arg *right;
};
+struct pevent_function_handler;
+
struct print_arg_func {
- char *name;
- struct print_arg *args;
+ struct pevent_function_handler *func;
+ struct print_arg *args;
};
enum print_arg_type {
@@ -107,7 +199,9 @@ enum print_arg_type {
PRINT_SYMBOL,
PRINT_TYPE,
PRINT_STRING,
+ PRINT_DYNAMIC_ARRAY,
PRINT_OP,
+ PRINT_FUNC,
};
struct print_arg {
@@ -122,6 +216,7 @@ struct print_arg {
struct print_arg_func func;
struct print_arg_string string;
struct print_arg_op op;
+ struct print_arg_dynarray dynarray;
};
};
@@ -130,64 +225,150 @@ struct print_fmt {
struct print_arg *args;
};
-struct event {
- struct event *next;
+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_ISFUNC = 0x08,
EVENT_FL_ISFUNCENT = 0x10,
EVENT_FL_ISFUNCRET = 0x20,
EVENT_FL_FAILED = 0x80000000
};
-struct record {
- unsigned long long ts;
- int size;
- void *data;
+enum event_sort_type {
+ EVENT_SORT_ID,
+ EVENT_SORT_NAME,
+ EVENT_SORT_SYSTEM,
};
-struct record *trace_peek_data(int cpu);
-struct record *trace_read_data(int cpu);
+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);
-void parse_set_info(int nr_cpus, int long_sz);
+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
+};
-ssize_t trace_report(int fd, bool repipe);
+struct cmdline;
+struct cmdline_list;
+struct func_map;
+struct func_list;
+struct event_handler;
-void *malloc_or_die(unsigned int size);
+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;
-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 cpus;
+ int long_size;
-void print_funcs(void);
-void print_printk(void);
+ struct cmdline *cmdlines;
+ struct cmdline_list *cmdlist;
+ int cmdline_count;
-int parse_ftrace_file(char *buf, unsigned long size);
-int parse_event_file(char *buf, unsigned long size, char *sys);
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm);
+ struct func_map *func_map;
+ struct func_list *funclist;
+ unsigned int func_count;
-extern int file_bigendian;
-extern int host_bigendian;
+ struct printk_map *printk_map;
+ struct printk_list *printklist;
+ unsigned int printk_count;
-int bigendian(void);
+ struct event_format **events;
+ int nr_events;
+ struct event_format **sort_events;
+ enum event_sort_type last_type;
-static inline unsigned short __data2host2(unsigned short data)
+ 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;
+
+ 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;
+};
+
+/* Can be overridden */
+void *malloc_or_die(unsigned int size);
+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 unsigned short
+__data2host2(struct pevent *pevent, unsigned short data)
{
unsigned short swap;
- if (host_bigendian == file_bigendian)
+ if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 8) |
@@ -196,11 +377,12 @@ static inline unsigned short __data2host2(unsigned short data)
return swap;
}
-static inline unsigned int __data2host4(unsigned int data)
+static inline unsigned int
+__data2host4(struct pevent *pevent, unsigned int data)
{
unsigned int swap;
- if (host_bigendian == file_bigendian)
+ if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 24) |
@@ -211,11 +393,12 @@ static inline unsigned int __data2host4(unsigned int data)
return swap;
}
-static inline unsigned long long __data2host8(unsigned long long data)
+static inline unsigned long long
+__data2host8(struct pevent *pevent, unsigned long long data)
{
unsigned long long swap;
- if (host_bigendian == file_bigendian)
+ if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 56) |
@@ -230,37 +413,16 @@ static inline unsigned long long __data2host8(unsigned long long data)
return swap;
}
-#define data2host2(ptr) __data2host2(*(unsigned short *)ptr)
-#define data2host4(ptr) __data2host4(*(unsigned int *)ptr)
-#define data2host8(ptr) ({ \
+#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(__val); \
+ __data2host8(pevent, __val); \
})
-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;
-
-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);
-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);
-
/* taken from kernel/trace/trace.h */
enum trace_flag_type {
TRACE_FLAG_IRQS_OFF = 0x01,
@@ -270,26 +432,288 @@ enum trace_flag_type {
TRACE_FLAG_SOFTIRQ = 0x10,
};
-struct scripting_ops {
- const char *name;
- int (*start_script) (const char *script, int argc, const char **argv);
- int (*stop_script) (void);
- void (*process_event) (int cpu, void *data, int size,
- unsigned long long nsecs, char *comm);
- int (*generate_script) (const char *outfile);
+int pevent_register_comm(struct pevent *pevent, 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 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);
+
+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 record *record);
+int pevent_data_type(struct pevent *pevent, struct record *rec);
+struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
+int pevent_data_pid(struct pevent *pevent, struct 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 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);
+
+/* 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,
};
-int script_spec_register(const char *spec, struct scripting_ops *ops);
+enum filter_op_type {
+ FILTER_OP_AND = 1,
+ FILTER_OP_OR,
+ FILTER_OP_NOT,
+};
-void setup_perl_scripting(void);
-void setup_python_scripting(void);
+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,
+};
-struct scripting_context {
- void *event_data;
+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,
};
-int common_pc(struct scripting_context *context);
-int common_flags(struct scripting_context *context);
-int common_lock_depth(struct scripting_context *context);
+enum filter_value_type {
+ FILTER_NUMBER,
+ FILTER_STRING
+};
+
+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_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_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 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 /* __PERF_TRACE_EVENTS_H */
+#endif /* _PARSE_EVENTS_H */
diff --git a/tools/lib/parse-filter.c b/tools/lib/parse-filter.c
new file mode 100644
index 0000000..dead050
--- /dev/null
+++ b/tools/lib/parse-filter.c
@@ -0,0 +1,2085 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "parse-events.h"
+#include "util.h"
+
+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, ...)
+{
+ va_list ap;
+
+ if (!error_str)
+ return;
+
+ *error_str = malloc_or_die(MAX_ERR_STR_SIZE);
+
+ va_start(ap, fmt);
+ vsnprintf(*error_str, MAX_ERR_STR_SIZE, fmt, ap);
+ va_end(ap);
+}
+
+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 enum event_type
+process_paren(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str);
+
+static enum event_type
+process_not(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str);
+
+static enum event_type
+process_value_token(struct event_format *event, struct filter_arg **parg,
+ enum event_type type, char **tok, char **error_str);
+
+static enum event_type
+process_op_token(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, enum event_type type, char **tok,
+ char **error_str);
+
+/*
+ * process_token
+ * Called when a new expression is found. Processes an op, or
+ * ends early if a ')' is found.
+ *
+ * Output: tok, parg
+ */
+static enum event_type
+process_token(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg = NULL;
+ enum event_type type;
+ char *token;
+
+ *tok = NULL;
+ *parg = NULL;
+
+ type = read_token(&token);
+
+ /*
+ * This is a start of a new expresion. We expect to find
+ * a item or a parenthesis.
+ */
+ switch (type) {
+ case EVENT_SQUOTE:
+ case EVENT_DQUOTE:
+ case EVENT_ITEM:
+ type = process_value_token(event, &arg, type, &token, error_str);
+ if (type == EVENT_ERROR) {
+ free_token(token);
+ return type;
+ }
+ type = read_token(&token);
+ break;
+ case EVENT_DELIM:
+ if (strcmp(token, "(") != 0)
+ break;
+
+ free_token(token);
+ type = process_paren(event, &arg, &token, error_str);
+ if (type == EVENT_NONE) {
+ *tok = token;
+ *parg = arg;
+ return type;
+ }
+ if (arg) {
+ /*
+ * If the parenthesis was a full expression,
+ * then just return it. Otherwise, we may still
+ * need to find an op.
+ */
+ switch (arg->type) {
+ case FILTER_ARG_OP:
+ case FILTER_ARG_NUM:
+ case FILTER_ARG_STR:
+ *tok = token;
+ *parg = arg;
+ return type;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case EVENT_OP:
+ if (strcmp(token, "!") != 0)
+ break;
+
+ /*
+ * A not is its own filter, it just negates,
+ * process it by itself.
+ */
+ *tok = token;
+ type = process_not(event, parg, tok, error_str);
+ return type;
+
+ default:
+ break;
+ }
+
+ for (;;) {
+ if (type == EVENT_NONE) {
+ show_error(error_str, "unexpected end of filter");
+ type = EVENT_ERROR;
+
+ } else if (type == EVENT_DELIM && strcmp(token, ")") == 0) {
+ /* Parenthesis call this and may return at anytime. */
+ *tok = token;
+ *parg = arg;
+ return type;
+
+ } else if (type != EVENT_OP) {
+ show_error(error_str, "Expected an OP but found %s", token);
+ type = EVENT_ERROR;
+ }
+
+ if (type == EVENT_ERROR) {
+ free_token(token);
+ return type;
+ }
+
+ *tok = token;
+ *parg = NULL;
+ type = process_op_token(event, arg, parg, type, tok, error_str);
+
+ if (type == EVENT_ERROR) {
+ free_arg(*parg);
+ *parg = NULL;
+ return EVENT_ERROR;
+ }
+
+ if (!(*parg) || (*parg)->type != FILTER_ARG_EXP)
+ break;
+
+ /*
+ * This op was an expression (value return)
+ * It's not fine by itself, there had better be an OP
+ * after it.
+ */
+ token = *tok;
+ *tok = NULL;
+ arg = *parg;
+ }
+
+ return type;
+}
+
+/*
+ * Input: tok
+ * Output: parg, tok
+ */
+static enum event_type
+process_bool(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, char **tok, char **error_str)
+{
+ struct filter_arg *rarg;
+ struct filter_arg *arg;
+ enum event_type type;
+ enum filter_op_type btype;
+
+ /* Can only be called with '&&' or '||' */
+ btype = strcmp(*tok, "&&") == 0 ?
+ FILTER_OP_AND : FILTER_OP_OR;
+
+ type = process_token(event, &rarg, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(larg);
+ *parg = NULL;
+ return type;
+ }
+
+ /*
+ * If larg or rarg is null then if this is AND, the whole expression
+ * becomes NULL, else if this is an OR, then we use the non NULL
+ * condition.
+ */
+ if (!larg || !rarg) {
+ if (btype == FILTER_OP_AND ||
+ (!larg && !rarg)) {
+ free_arg(larg);
+ free_arg(rarg);
+ *parg = NULL;
+ return type;
+ }
+ *parg = larg ? larg : rarg;
+ return type;
+ }
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_OP;
+ arg->op.type = btype;
+ arg->op.left = larg;
+ arg->op.right = rarg;
+
+
+ /*
+ * If the next token is also a boolean expression, then
+ * make the next boolean the parent..
+ */
+ if (type != EVENT_OP ||
+ (strcmp(*tok, "&&") != 0 && strcmp(*tok, "||") != 0)) {
+ *parg = arg;
+ return type;
+ }
+
+ return process_bool(event, arg, parg, tok, error_str);
+}
+
+/*
+ * Input: tok
+ * Output: parg
+ */
+static enum event_type
+process_value_token(struct event_format *event, struct filter_arg **parg,
+ enum event_type type, char **tok, char **error_str)
+{
+ struct format_field *field;
+ struct filter_arg *arg;
+ char *token;
+
+ token = *tok;
+ *tok = NULL;
+
+ arg = allocate_arg();
+
+ switch (type) {
+
+ case EVENT_SQUOTE:
+ case EVENT_DQUOTE:
+ arg->type = FILTER_ARG_VALUE;
+ arg->value.type = FILTER_STRING;
+ arg->value.str = token;
+ 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 = strtoll(token, NULL, 0);
+ free_token(token);
+ break;
+ }
+ /* Consider this a field */
+ field = pevent_find_any_field(event, token);
+ free_token(token);
+ if (!field) {
+ /* not a field, so NULL it up */
+ free_arg(arg);
+ arg = NULL;
+ break;
+ }
+
+ 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);
+ free_token(token);
+ return EVENT_ERROR;
+ }
+
+ *parg = arg;
+ return type;
+}
+
+/*
+ * Output: parg, tok
+ */
+static enum event_type
+process_value(struct event_format *event, struct filter_arg **parg,
+ enum event_type *orig_type, char **tok, char **error_str)
+{
+ enum event_type type;
+ char *token;
+
+ *tok = NULL;
+ type = read_token(&token);
+ *orig_type = type;
+ if (type == EVENT_DELIM && strcmp(token, "(") == 0) {
+ type = process_paren(event, parg, &token, error_str);
+ /* Must be a expression or value */
+ if (type == EVENT_ERROR || !(*parg)) {
+ free_token(token);
+ return type;
+ }
+ switch ((*parg)->type) {
+ case FILTER_ARG_BOOLEAN:
+ case FILTER_ARG_VALUE:
+ case FILTER_ARG_FIELD:
+ case FILTER_ARG_EXP:
+ break;
+ default:
+ show_error(error_str, "expected a value");
+ free_token(token);
+ return EVENT_ERROR;
+ }
+ } else {
+ type = process_value_token(event, parg, type, &token, error_str);
+ free_token(token);
+ if (type == EVENT_ERROR)
+ return type;
+ type = read_token(&token);
+ }
+
+ *tok = token;
+ return type;
+}
+
+/*
+ * Input: larg
+ * Output: parg, tok
+ */
+static enum event_type
+process_cmp(struct event_format *event, enum filter_cmp_type op_type,
+ struct filter_arg *larg, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ struct filter_arg *rarg = NULL;
+ enum event_type orig_type;
+ enum event_type type;
+ int ret;
+
+ *parg = NULL;
+
+ type = process_value(event, &rarg, &orig_type, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(rarg);
+ return type;
+ }
+
+ arg = allocate_arg();
+ /*
+ * If either arg is NULL or right was field not found.
+ * Then make the entire expression NULL. (will turn to FALSE)
+ */
+ if (!larg || !rarg) {
+ free_arg(larg);
+ free_arg(rarg);
+ free_arg(arg);
+ arg = NULL;
+ goto cont;
+ }
+
+ switch (orig_type) {
+ case EVENT_SQUOTE:
+ /* treat this as a character if string is of length 1? */
+ if (strlen(rarg->str.val) == 1) {
+ switch (op_type) {
+ case FILTER_CMP_REGEX:
+ case FILTER_CMP_NOT_REGEX:
+ /* regex can't be used with ints */
+ break;
+ default:
+ goto as_int;
+ }
+ }
+ /* fall through */
+ case EVENT_DQUOTE:
+ arg->type = FILTER_ARG_STR;
+
+ if (larg->type != FILTER_ARG_FIELD) {
+ free(larg);
+ free(rarg);
+ show_error(error_str,
+ "Illegal lval for string comparison");
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+
+ arg->str.field = larg->field.field;
+ free_arg(larg);
+
+ /* free the rarg, and use its token */
+ arg->str.val = rarg->value.str;
+ rarg->value.str = NULL;
+ free_arg(rarg);
+
+ /* 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(&arg->str.reg, arg->str.val, REG_ICASE|REG_NOSUB);
+ if (ret) {
+ show_error(error_str,
+ "RegEx '%s' did not compute",
+ arg->str.val);
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+ break;
+ default:
+ show_error(error_str,
+ "Illegal comparison for string");
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+
+ arg->str.type = op_type;
+
+ /*
+ * Need a buffer to copy data int for tests */
+ arg->str.buffer = malloc_or_die(arg->str.field->size + 1);
+ /* Null terminate this buffer */
+ arg->str.buffer[arg->str.field->size] = 0;
+
+ break;
+ default:
+ as_int:
+ switch (op_type) {
+ case FILTER_CMP_REGEX:
+ case FILTER_CMP_NOT_REGEX:
+ show_error(error_str,
+ "Op not allowed with integers");
+ free_arg(arg);
+ return EVENT_ERROR;
+ default:
+ break;
+ }
+ /* numeric compare */
+ arg->type = FILTER_ARG_NUM;
+ arg->num.type = op_type;
+ arg->num.left = larg;
+ arg->num.right = rarg;
+ break;
+ }
+ cont:
+ *parg = arg;
+ return type;
+}
+
+/*
+ * Input: larg
+ * Output: parg, tok
+ */
+static enum event_type
+process_exp(struct event_format *event, enum filter_exp_type etype,
+ struct filter_arg *larg, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *rarg = NULL;
+ struct filter_arg *arg;
+ enum event_type orig_type;
+ enum event_type type;
+
+ type = process_value(event, &rarg, &orig_type, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(rarg);
+ return type;
+ }
+
+ /* larg can be NULL if a field did not match */
+ if (!larg) {
+ /* syntax is correct, just return NULL */
+ arg = NULL;
+ free_arg(rarg);
+ goto cont;
+ }
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_EXP;
+ arg->op.type = etype;
+ arg->op.left = larg;
+ arg->op.right = rarg;
+
+ cont:
+ /* still need a cmp */
+ type = process_op_token(event, arg, parg, type, tok, error_str);
+ return type;
+}
+
+/*
+ * Input: tok
+ * Output: parg, tok
+ */
+static enum event_type
+process_op_token(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, enum event_type type, char **tok,
+ char **error_str)
+{
+ enum filter_cmp_type ctype;
+ enum filter_exp_type etype = FILTER_EXP_NONE;
+ char *token;
+
+ token = *tok;
+ *parg = NULL;
+
+ if (type != EVENT_OP) {
+ *parg = larg;
+ return type;
+ }
+
+ if (strcmp(token, "&&") == 0 || strcmp(token, "||") == 0) {
+ /* handle boolean cases */
+ return process_bool(event, larg, parg, tok, error_str);
+ }
+
+ /* 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) {
+ free_token(token);
+ return process_exp(event, etype, larg, parg, tok, error_str);
+ }
+
+ 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 {
+ show_error(error_str,
+ "Unknown op '%s'", token);
+ free_token(token);
+ return EVENT_ERROR;
+ }
+
+ free_token(token);
+ *tok = NULL;
+ return process_cmp(event, ctype, larg, parg, tok, error_str);
+}
+
+static enum event_type
+process_filter(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *larg = NULL;
+ enum event_type type;
+
+ *parg = NULL;
+ *tok = NULL;
+
+ type = process_token(event, parg, tok, error_str);
+
+ if (type == EVENT_OP &&
+ (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
+ larg = *parg;
+ *parg = NULL;
+ type = process_bool(event, larg, parg, tok, error_str);
+ }
+
+ return type;
+}
+
+static enum event_type
+process_paren(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ enum event_type type;
+
+ *parg = NULL;
+
+ type = process_token(event, &arg, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(arg);
+ return type;
+ }
+
+ if (type == EVENT_OP &&
+ (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
+ type = process_bool(event, arg, parg, tok, error_str);
+ }
+
+ if (type != EVENT_DELIM || strcmp(*tok, ")") != 0) {
+ if (*tok)
+ show_error(error_str,
+ "Expected ')' but found %s", *tok);
+ else
+ show_error(error_str,
+ "Unexpected end of filter; Expected ')'");
+ free_token(*tok);
+ *tok = NULL;
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+ free_token(*tok);
+ *tok = NULL;
+
+ *parg = arg;
+
+ return read_token(tok);
+}
+
+static enum event_type
+process_not(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ enum event_type type;
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_OP;
+ arg->op.type = FILTER_OP_NOT;
+
+ arg->op.left = NULL;
+ type = process_token(event, &arg->op.right, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(arg);
+ *parg = NULL;
+ free_token(*tok);
+ *tok = NULL;
+ return EVENT_ERROR;
+ }
+ /* If the bool value is NULL, then make this into TRUE */
+ if (!arg->op.right) {
+ arg->type = FILTER_ARG_BOOLEAN;
+ arg->boolean.value = FILTER_TRUE;
+ }
+
+ *parg = arg;
+ free_token(*tok);
+ *tok = NULL;
+
+ return type;
+}
+
+static int
+process_event(struct event_format *event, const char *filter_str,
+ struct filter_arg **parg, char **error_str)
+{
+ enum event_type type;
+ char *token;
+
+ pevent_buffer_init(filter_str, strlen(filter_str));
+
+ type = process_filter(event, parg, &token, error_str);
+
+ if (type == EVENT_ERROR)
+ return -1;
+
+ if (type != EVENT_NONE) {
+ show_error(error_str,
+ "Expected end where %s was found",
+ token);
+ free_token(token);
+ free_arg(*parg);
+ *parg = NULL;
+ return -1;
+ }
+
+ /* 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;
+
+ 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;
+ }
+
+ free_events(events);
+
+ 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 record *record);
+
+static unsigned long long
+get_value(struct format_field *field, struct record *record)
+{
+ unsigned long long val;
+
+ 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 record *record);
+
+static unsigned long long
+get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record)
+{
+ unsigned long long lval, rval;
+
+ lval = get_arg_value(event, arg->op.left, record);
+ rval = get_arg_value(event, arg->op.right, record);
+
+ switch (arg->op.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 record *record)
+{
+ switch (arg->type) {
+ case FILTER_ARG_FIELD:
+ return get_value(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 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 int test_str(struct event_format *event,
+ struct filter_arg *arg, struct record *record)
+{
+ const char *val = record->data + arg->str.field->offset;
+ const char *buffer;
+
+ /*
+ * 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 */
+ buffer = arg->str.buffer;
+ } else
+ /* OK, it's NULL terminated */
+ buffer = val;
+
+ switch (arg->str.type) {
+ case FILTER_CMP_MATCH:
+ return strcmp(buffer, arg->str.val) == 0;
+
+ case FILTER_CMP_NOT_MATCH:
+ return strcmp(buffer, arg->str.val) != 0;
+
+ case FILTER_CMP_REGEX:
+ /* Returns zero on match */
+ return !regexec(&arg->str.reg, buffer, 0, NULL, 0);
+
+ case FILTER_CMP_NOT_REGEX:
+ return regexec(&arg->str.reg, buffer, 0, NULL, 0);
+
+ default:
+ /* ?? */
+ return 0;
+ }
+}
+
+static int test_op(struct event_format *event,
+ struct filter_arg *arg, struct 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 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 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->op.left);
+ rstr = arg_to_str(filter, arg->op.right);
+
+ switch (arg->op.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/parse-utils.c b/tools/lib/parse-utils.c
new file mode 100644
index 0000000..f023a13
--- /dev/null
+++ b/tools/lib/parse-utils.c
@@ -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/trace-seq.c b/tools/lib/trace-seq.c
new file mode 100644
index 0000000..d84a5bd
--- /dev/null
+++ b/tools/lib/trace-seq.c
@@ -0,0 +1,153 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "parse-events.h"
+
+/**
+ * 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, ...)
+{
+ int len = (TRACE_SEQ_SIZE - 1) - s->len;
+ va_list ap;
+ int ret;
+
+ if (s->full || !len)
+ return 0;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+ va_end(ap);
+
+ /* If we can't write it all, don't bother writing anything */
+ if (ret >= len) {
+ s->full = 1;
+ return 0;
+ }
+
+ 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 = (TRACE_SEQ_SIZE - 1) - s->len;
+ int ret;
+
+ if (s->full || !len)
+ return 0;
+
+ ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+
+ /* If we can't write it all, don't bother writing anything */
+ if (ret >= len) {
+ s->full = 1;
+ return 0;
+ }
+
+ s->len += ret;
+
+ return len;
+}
+
+/**
+ * trace_seq_puts - trace sequence printing of simple string
+ * @s: trace sequence descriptor
+ * @str: simple string to record
+ *
+ * The tracer may use either the sequence operations or its own
+ * copy to user routines. This function records a simple string
+ * into a special buffer (@s) for later retrieval by a sequencer
+ * or other mechanism.
+ */
+int trace_seq_puts(struct trace_seq *s, const char *str)
+{
+ int len = strlen(str);
+
+ if (s->full)
+ return 0;
+
+ if (len > ((TRACE_SEQ_SIZE - 1) - s->len)) {
+ s->full = 1;
+ return 0;
+ }
+
+ memcpy(s->buffer + s->len, str, len);
+ s->len += len;
+
+ return len;
+}
+
+int trace_seq_putc(struct trace_seq *s, unsigned char c)
+{
+ if (s->full)
+ return 0;
+
+ if (s->len >= (TRACE_SEQ_SIZE - 1)) {
+ s->full = 1;
+ return 0;
+ }
+
+ s->buffer[s->len++] = c;
+
+ return 1;
+}
+
+void trace_seq_terminate(struct trace_seq *s)
+{
+ if (!s->full)
+ s->buffer[s->len] = 0;
+}
+
+int trace_seq_do_printf(struct trace_seq *s)
+{
+ return printf("%.*s%s", s->len, s->buffer,
+ s->full ? "[truncated]" : "");
+}
diff --git a/tools/lib/util.h b/tools/lib/util.h
new file mode 100644
index 0000000..2bb9fe6
--- /dev/null
+++ b/tools/lib/util.h
@@ -0,0 +1,64 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef __UTIL_H
+#define __UTIL_H
+
+#include <ctype.h>
+
+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/perf/Makefile b/tools/perf/Makefile
index a251fb7..f7a2626 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -5,6 +5,8 @@ else
LIB_OUTPUT := ../lib/
endif
+export OUTPUT ALL_CFLAGS
+
# The default target of this Makefile is...
all::
@@ -456,6 +458,7 @@ LIB_OBJS += $(OUTPUT)util/map.o
LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
+LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@ -859,7 +862,9 @@ ifdef ASCIIDOC8
export ASCIIDOC8
endif
-$(LIB_OUTPUT)libparsevent.a:
+force:
+
+$(LIB_OUTPUT)libparsevent.a: force
$(MAKE) -C ../lib libparsevent.a
# Shell quote (do not use $(call) to accommodate ancient setups);
@@ -1242,6 +1247,7 @@ clean:
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
.PHONY: .FORCE-PERF-BUILD-OPTIONS
+.PHONY: .force
### Make sure built-ins do not have dups and listed in perf.c
#
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 31f60a2..97b3970 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -189,7 +189,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,
@@ -250,7 +250,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)
@@ -279,7 +279,7 @@ static void
process_raw_event(event_t *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);
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 821c158..fbc512a 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -344,25 +344,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);
@@ -404,7 +404,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)
@@ -468,7 +468,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)
@@ -524,7 +524,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)
@@ -571,7 +571,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)
@@ -635,7 +635,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)
@@ -654,7 +654,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)
@@ -672,7 +672,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)
@@ -690,7 +690,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)
@@ -709,7 +709,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);
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 55f3b5d..3e98de5 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -729,34 +729,34 @@ struct trace_migrate_task_event {
struct trace_sched_handler {
void (*switch_event)(struct trace_switch_event *,
struct perf_session *,
- struct event *,
+ struct event_format *,
int cpu,
u64 timestamp,
struct thread *thread);
void (*runtime_event)(struct trace_runtime_event *,
struct perf_session *,
- struct event *,
+ struct event_format *,
int cpu,
u64 timestamp,
struct thread *thread);
void (*wakeup_event)(struct trace_wakeup_event *,
struct perf_session *,
- 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 perf_session *session,
- struct event *,
+ struct event_format *,
int cpu,
u64 timestamp,
struct thread *thread);
@@ -766,7 +766,7 @@ struct trace_sched_handler {
static void
replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
struct perf_session *session __used,
- struct event *event,
+ struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -793,7 +793,7 @@ static u64 cpu_last_switched[MAX_CPUS];
static void
replay_switch_event(struct trace_switch_event *switch_event,
struct perf_session *session __used,
- struct event *event,
+ struct event_format *event,
int cpu,
u64 timestamp,
struct thread *thread __used)
@@ -836,7 +836,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)
@@ -945,7 +945,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)
@@ -1027,7 +1027,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
static void
latency_switch_event(struct trace_switch_event *switch_event,
struct perf_session *session,
- struct event *event __used,
+ struct event_format *event __used,
int cpu,
u64 timestamp,
struct thread *thread __used)
@@ -1080,7 +1080,7 @@ latency_switch_event(struct trace_switch_event *switch_event,
static void
latency_runtime_event(struct trace_runtime_event *runtime_event,
struct perf_session *session,
- struct event *event __used,
+ struct event_format *event __used,
int cpu,
u64 timestamp,
struct thread *this_thread __used)
@@ -1103,7 +1103,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event,
static void
latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
struct perf_session *session,
- struct event *__event __used,
+ struct event_format *__event __used,
int cpu __used,
u64 timestamp,
struct thread *thread __used)
@@ -1151,7 +1151,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 perf_session *session,
- struct event *__event __used,
+ struct event_format *__event __used,
int cpu __used,
u64 timestamp,
struct thread *thread __used)
@@ -1362,7 +1362,7 @@ static struct trace_sched_handler *trace_handler;
static void
process_sched_wakeup_event(void *data, struct perf_session *session,
- struct event *event,
+ struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -1398,7 +1398,7 @@ static char next_shortname2 = '0';
static void
map_switch_event(struct trace_switch_event *switch_event,
struct perf_session *session,
- struct event *event __used,
+ struct event_format *event __used,
int this_cpu,
u64 timestamp,
struct thread *thread __used)
@@ -1477,7 +1477,7 @@ map_switch_event(struct trace_switch_event *switch_event,
static void
process_sched_switch_event(void *data, struct perf_session *session,
- struct event *event,
+ struct event_format *event,
int this_cpu,
u64 timestamp __used,
struct thread *thread __used)
@@ -1511,7 +1511,7 @@ process_sched_switch_event(void *data, struct perf_session *session,
static void
process_sched_runtime_event(void *data, struct perf_session *session,
- struct event *event,
+ struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -1529,7 +1529,7 @@ process_sched_runtime_event(void *data, struct perf_session *session,
static void
process_sched_fork_event(void *data,
- struct event *event,
+ struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -1549,7 +1549,7 @@ process_sched_fork_event(void *data,
}
static void
-process_sched_exit_event(struct event *event,
+process_sched_exit_event(struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -1560,7 +1560,7 @@ process_sched_exit_event(struct event *event,
static void
process_sched_migrate_task_event(void *data, struct perf_session *session,
- struct event *event,
+ struct event_format *event,
int cpu __used,
u64 timestamp __used,
struct thread *thread __used)
@@ -1583,7 +1583,7 @@ static void
process_raw_event(event_t *raw_event __used, struct perf_session *session,
void *data, int cpu, u64 timestamp, struct thread *thread)
{
- struct event *event;
+ struct event_format *event;
int type;
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 33a6325..9e8d535 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -36,7 +36,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
@@ -135,7 +135,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)
{
@@ -185,10 +185,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];
@@ -213,7 +213,7 @@ static void python_process_event(int cpu, void *data,
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;
@@ -430,7 +430,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;
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index b157260..d5083d8 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -526,6 +526,8 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
else
buf[0] = 0;
+ read_trace_init(buf[0], buf[0]);
+
write_or_die(buf, 1);
/* save size of long */
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
new file mode 100644
index 0000000..c1d3097
--- /dev/null
+++ b/tools/perf/util/trace-event-parse.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#undef _GNU_SOURCE
+#include "../perf.h"
+#include "util.h"
+#include "trace-event.h"
+
+int header_page_size_size;
+int header_page_ts_size;
+int header_page_data_offset;
+
+struct pevent *perf_pevent;
+static struct pevent *pevent;
+
+bool latency_format;
+
+int read_trace_init(int file_bigendian, int host_bigendian)
+{
+ if (pevent)
+ return 0;
+
+ perf_pevent = pevent_alloc();
+ pevent = perf_pevent;
+
+ pevent_set_file_bigendian(pevent, file_bigendian);
+ pevent_set_host_bigendian(pevent, host_bigendian);
+
+ return 0;
+}
+
+unsigned long long
+raw_field_value(struct event_format *event, const char *name, void *data)
+{
+ struct format_field *field;
+ unsigned long long val;
+
+ field = pevent_find_any_field(event, name);
+ if (!field)
+ return 0ULL;
+
+ pevent_read_number_field(field, data, &val);
+
+ return val;
+}
+
+void *raw_field_ptr(struct event_format *event, const char *name, void *data)
+{
+ struct format_field *field;
+
+ field = pevent_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;
+}
+
+int trace_parse_common_type(void *data)
+{
+ struct record record;
+
+ record.data = data;
+ return pevent_data_type(pevent, &record);
+}
+
+struct event_format *trace_find_event(int type)
+{
+ return pevent_find_event(pevent, type);
+}
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm)
+{
+ struct record record;
+ struct trace_seq s;
+ int pid;
+
+ pevent->latency_format = latency_format;
+
+ record.ts = nsecs;
+ record.cpu = cpu;
+ record.size = size;
+ record.data = data;
+ pid = pevent_data_pid(pevent, &record);
+
+ if (!pevent_pid_is_registered(pevent, pid))
+ pevent_register_comm(pevent, comm, pid);
+
+ trace_seq_init(&s);
+ pevent_print_event(pevent, &s, &record);
+ trace_seq_do_printf(&s);
+ printf("\n");
+
+#if 0
+ struct event *event;
+ unsigned long secs;
+ unsigned long usecs;
+ int type;
+ int pid;
+
+ secs = nsecs / NSECS_PER_SEC;
+ nsecs -= secs * NSECS_PER_SEC;
+ usecs = nsecs / NSECS_PER_USEC;
+
+ 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 (latency_format) {
+ printf("%8.8s-%-5d %3d",
+ comm, pid, cpu);
+ print_lat_fmt(data, size);
+ } else
+ printf("%16s-%-5d [%03d]", comm, pid, cpu);
+
+ printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+
+ if (event->flags & EVENT_FL_FAILED) {
+ printf("EVENT '%s' FAILED TO PARSE\n",
+ event->name);
+ return;
+ }
+
+ pretty_print(data, size, event);
+ printf("\n");
+#endif
+}
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused)
+{
+ unsigned long long addr;
+ char *func;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char *mod;
+ char ch;
+ int ret;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ mod = NULL;
+ ret = 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);
+
+ /* truncate the extra ']' */
+ if (mod)
+ mod[strlen(mod) - 1] = 0;
+
+ pevent_register_function(pevent, func, addr, mod);
+ free(func);
+ free(mod);
+
+ line = strtok_r(NULL, "\n", &next);
+ }
+}
+
+void parse_ftrace_printk(char *file, unsigned int size __unused)
+{
+ unsigned long long addr;
+ char *printk;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char *fmt;
+
+ 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);
+ }
+}
+
+int parse_ftrace_file(char *buf, unsigned long size)
+{
+ return pevent_parse_event(pevent, buf, size, "ftrace");
+}
+
+int parse_event_file(char *buf, unsigned long size, char *sys)
+{
+ return pevent_parse_event(pevent, buf, size, sys);
+}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index f55cc3a..188e100 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -110,7 +110,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)
@@ -118,7 +118,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)
@@ -390,15 +390,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:
@@ -415,7 +415,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);
@@ -425,14 +425,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;
@@ -443,7 +443,7 @@ read_again:
ptr += 12;
break;
case 0:
- length = data2host4(ptr);
+ length = data2host4(perf_pevent, ptr);
ptr += 4;
die("here! length=%d", length);
break;
@@ -510,6 +510,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];
@@ -527,11 +529,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;
}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 2adc26f..e9923bc 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -4,8 +4,87 @@
#include "../../lib/parse-events.h"
#include "parse-events.h"
+extern int header_page_size_size;
+extern int header_page_ts_size;
+extern int header_page_data_offset;
+
+extern bool latency_format;
+extern struct pevent *perf_pevent;
+
+struct scripting_context {
+ void *event_data;
+};
+
+enum {
+ RINGBUF_TYPE_PADDING = 29,
+ RINGBUF_TYPE_TIME_EXTEND = 30,
+ RINGBUF_TYPE_TIME_STAMP = 31,
+};
+
+#ifndef TS_SHIFT
+#define TS_SHIFT 27
+#endif
+
+int bigendian(void);
+
+int read_trace_init(int file_bigendian, int host_bigendian);
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm);
+
+struct record *trace_read_data(int cpu);
int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
int nb_events);
+int parse_ftrace_file(char *buf, unsigned long size);
+int parse_event_file(char *buf, unsigned long size, char *sys);
+
+struct record *trace_peek_data(int cpu);
+struct event_format *trace_find_event(int type);
+
+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);
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused);
+void parse_ftrace_printk(char *file, unsigned int size __unused);
+
+ssize_t trace_report(int fd, bool repipe);
+
+struct scripting_ops {
+ const char *name;
+ int (*start_script) (const char *script, int argc, const char **argv);
+ int (*stop_script) (void);
+ void (*process_event) (int cpu, void *data, int size,
+ unsigned long long nsecs, char *comm);
+ int (*generate_script) (const char *outfile);
+};
+
+int script_spec_register(const char *spec, struct scripting_ops *ops);
+
+void setup_perl_scripting(void);
+void setup_python_scripting(void);
+
+int trace_parse_common_type(void *data);
+int trace_parse_common_pid(void *data);
+#if 0
+int parse_common_pc(void *data);
+int parse_common_flags(void *data);
+int parse_common_lock_depth(void *data);
+struct event_format *trace_find_event(int id);
+#endif
+struct event_format *trace_find_next_event(struct event_format *event);
+unsigned long long read_size(void *ptr, int size);
+#if 0
+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);
+#endif
+unsigned long long eval_flag(const char *flag);
+
+int common_pc(struct scripting_context *context);
+int common_flags(struct scripting_context *context);
+int common_lock_depth(struct scripting_context *context);
+
#endif /* _PERF_UTIL_TRACE_EVENT_H */
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 06/20] perf: Move trace stuff into tools/lib/trace
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (4 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 05/20] perf: Update the lib parse-events to the latest code Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 07/20] perf: Export debugfs utilities Borislav Petkov
` (14 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Move tracing stuff into tools/lib/trace and rewire it back into perf.
Add a top-level Makefile which selects between targets depending
on the tool we want to build.
Also, add a Makefile.lib for common facilities used by all the
Makefiles. While at it, make sure objects output directory using O=
exists.
Finally, rename trace/util.h to trace/trace-util.h so as not to conflict
with perf's util.h.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 47 +
tools/lib/Makefile | 49 -
tools/lib/parse-events.c | 4655 ----------------------------------------
tools/lib/parse-events.h | 719 -------
tools/lib/parse-filter.c | 2085 ------------------
tools/lib/parse-utils.c | 110 -
tools/lib/trace-seq.c | 153 --
tools/lib/trace/Makefile | 54 +
tools/lib/trace/parse-events.c | 4655 ++++++++++++++++++++++++++++++++++++++++
tools/lib/trace/parse-events.h | 719 +++++++
tools/lib/trace/parse-filter.c | 2085 ++++++++++++++++++
tools/lib/trace/parse-utils.c | 110 +
tools/lib/trace/trace-seq.c | 153 ++
tools/lib/trace/trace-util.h | 64 +
tools/lib/util.h | 64 -
tools/perf/Makefile | 41 +-
tools/perf/util/trace-event.h | 2 +-
tools/scripts/Makefile.lib | 33 +
18 files changed, 7923 insertions(+), 7875 deletions(-)
create mode 100644 tools/Makefile
delete mode 100644 tools/lib/Makefile
delete mode 100644 tools/lib/parse-events.c
delete mode 100644 tools/lib/parse-events.h
delete mode 100644 tools/lib/parse-filter.c
delete mode 100644 tools/lib/parse-utils.c
delete mode 100644 tools/lib/trace-seq.c
create mode 100644 tools/lib/trace/Makefile
create mode 100644 tools/lib/trace/parse-events.c
create mode 100644 tools/lib/trace/parse-events.h
create mode 100644 tools/lib/trace/parse-filter.c
create mode 100644 tools/lib/trace/parse-utils.c
create mode 100644 tools/lib/trace/trace-seq.c
create mode 100644 tools/lib/trace/trace-util.h
delete mode 100644 tools/lib/util.h
create mode 100644 tools/scripts/Makefile.lib
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..430b25e
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,47 @@
+include scripts/Makefile.lib
+
+# 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) -Wstack-protector
+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) -Wvolatile-register-var
+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
+
+export EXTRA_WARNINGS
+
+# lib includes for submake
+BASIC_CFLAGS = -I$(CURDIR)/lib/trace -I$(CURDIR)/perf -I$(CURDIR)/perf/util/include
+export BASIC_CFLAGS
+
+PERF_TOP_DIR := $(CURDIR)
+export PERF_TOP_DIR
+
+perf: libparsevent .FORCE
+ $(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
+
+libparsevent: .FORCE
+ $(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1)
+
+clean:
+ $(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
+
+.PHONY: clean .FORCE
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
deleted file mode 100644
index 206078b..0000000
--- a/tools/lib/Makefile
+++ /dev/null
@@ -1,49 +0,0 @@
-
-# Make the path relative to DESTDIR, not to prefix
-ifndef DESTDIR
-prefix = $(HOME)
-endif
-bindir_relative = bin
-bindir = $(prefix)/$(bindir_relative)
-mandir = share/man
-infodir = share/info
-sharedir = $(prefix)/share
-ifeq ($(prefix),/usr)
-sysconfdir = /etc
-else
-sysconfdir = $(prefix)/etc
-endif
-
-export prefix bindir sharedir sysconfdir
-
-CC = $(CROSS_COMPILE)gcc
-AR = $(CROSS_COMPILE)ar
-RM = rm -f
-TAR = tar
-FIND = find
-INSTALL = install
-RPMBUILD = rpmbuild
-PTHREAD_LIBS = -lpthread
-
-ifeq ("$(origin V)", "command line")
- VERBOSE = $(V)
-endif
-ifndef VERBOSE
- VERBOSE = 0
-endif
-
-all: libparsevent.a
-
-PEVENT_LIB_OBJS += parse-events.o
-PEVENT_LIB_OBJS += parse-filter.o
-PEVENT_LIB_OBJS += parse-utils.o
-PEVENT_LIB_OBJS += trace-seq.o
-
-$(OUTPUT)%.o: %.c
- $(QUIET_CC)$(CC) -g -o $@ -c $(ALL_CFLAGS) $<
-
-libparsevent.a: $(PEVENT_LIB_OBJS)
- $(RM) $@; $(AR) rcs $@ $^
-
-clean:
- $(RM) *.a *.o *~ *.so
\ No newline at end of file
diff --git a/tools/lib/parse-events.c b/tools/lib/parse-events.c
deleted file mode 100644
index 5503a18..0000000
--- a/tools/lib/parse-events.c
+++ /dev/null
@@ -1,4655 +0,0 @@
-/*
- * 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., 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
- * - 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 "parse-events.h"
-
-static const char *input_buf;
-static unsigned long long input_buf_ptr;
-static unsigned long long input_buf_siz;
-
-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;
-}
-
-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, 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 = 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, 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);
- 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:
- 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;
- 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;
- }
-
- 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 '/':
- 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);
-}
-
-/* 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 '-':
- 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;
-
- set_op_prio(arg);
-
- 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;
-
- 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 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);
- val = eval_type(val, arg, 0);
- 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 '-':
- /* check for negative */
- if (arg->op.left->type == PRINT_NULL)
- left = 0;
- else
- 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_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);
- 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);
- 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);
- 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;
-
- 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);
- }
-
- 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);
- return process_flags(event, arg, tok);
- }
- if (strcmp(token, "__print_symbolic") == 0) {
- free_token(token);
- 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)
-{
- 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:
- return 0;
- case PRINT_FUNC: {
- struct trace_seq s;
- trace_seq_init(&s);
- return process_defined_func(&s, data, size, event, arg);
- }
- 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 '|':
- 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 },
-};
-
-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_arg(struct trace_seq *s, void *data, int size,
- struct event_format *event, 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:
- trace_seq_puts(s, 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;
-
- /*
- * 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) &&
- len == 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;
- trace_seq_puts(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) {
- trace_seq_puts(s, flag->str);
- break;
- }
- if (fval && (val & fval) == fval) {
- if (print && arg->flags.delim)
- trace_seq_puts(s, arg->flags.delim);
- trace_seq_puts(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) {
- trace_seq_puts(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 = pevent_find_any_field(event, arg->string.string);
- arg->string.offset = f->offset;
- }
- str_offset = data2host4(pevent, data + arg->string.offset);
- str_offset &= 0xffff;
- trace_seq_puts(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(s, data, size, event, arg->op.right->op.left);
- else
- print_str_arg(s, data, size, event, 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, farg);
- trace_seq_terminate(&str);
- string = malloc_or_die(sizeof(*string));
- string->next = strings;
- string->str = strdup(str.buffer);
- strings = string;
- 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:
- 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_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;
-
- 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");
- printf("field->offset = %d size=%d\n", field->offset, field->size);
- 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;
- 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;
- cont_process:
- ptr++;
- switch (*ptr) {
- case '%':
- trace_seq_putc(s, '%');
- break;
- case '#':
- /* FIXME: need to handle properly */
- goto cont_process;
- case 'l':
- ls++;
- goto cont_process;
- case 'L':
- ls = 2;
- 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 > 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(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 0:
- trace_seq_printf(s, format, (int)val);
- break;
- case 1:
- trace_seq_printf(s, format, (long)val);
- break;
- case 2:
- trace_seq_printf(s, format, (long long)val);
- break;
- default:
- die("bad count (%d)", ls);
- }
- break;
- case 's':
- if (!arg)
- die("no matching argument");
-
- print_str_arg(s, data, size, event, 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 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 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 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 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 record *record)
-{
- static char *spaces = " "; /* 20 spaces */
- struct event_format *event;
- unsigned long secs;
- unsigned long usecs;
- const char *comm;
- void *data = record->data;
- int type;
- int pid;
- int len;
-
- secs = record->ts / NSECS_PER_SEC;
- usecs = record->ts - secs * NSECS_PER_SEC;
- usecs = (usecs + 500) / NSECS_PER_USEC;
-
- 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);
-
- trace_seq_printf(s, " %5lu.%06lu: %s: ", secs, 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);
- 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);
- 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);
- }
-}
-
-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;
-}
-
-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 = pevent->cmdlist, *cmdnext;
- struct func_list *funclist = pevent->funclist, *funcnext;
- struct printk_list *printklist = pevent->printklist, *printknext;
- struct pevent_function_handler *func_handler;
- struct event_handler *handle;
- int i;
-
- 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/parse-events.h b/tools/lib/parse-events.h
deleted file mode 100644
index 5c10208..0000000
--- a/tools/lib/parse-events.h
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_SIZE
-#define TRACE_SEQ_SIZE 4096
-#endif
-
-struct 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;
-};
-
-/*
- * 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[TRACE_SEQ_SIZE];
- unsigned int len;
- unsigned int readpos;
- int full;
-};
-
-static inline void
-trace_seq_init(struct trace_seq *s)
-{
- s->len = 0;
- s->readpos = 0;
- s->full = 0;
-}
-
-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 record *record,
- struct event_format *event,
- void *context);
-
-typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
-typedef int (*pevent_plugin_unload_func)(void);
-
-#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
-#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
-#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 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_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
-};
-
-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;
-
- 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;
-};
-
-/* Can be overridden */
-void *malloc_or_die(unsigned int size);
-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 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, 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 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);
-
-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 record *record);
-int pevent_data_type(struct pevent *pevent, struct record *rec);
-struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
-int pevent_data_pid(struct pevent *pevent, struct 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 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);
-
-/* 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
-};
-
-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_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_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 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/parse-filter.c b/tools/lib/parse-filter.c
deleted file mode 100644
index dead050..0000000
--- a/tools/lib/parse-filter.c
+++ /dev/null
@@ -1,2085 +0,0 @@
-/*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#include "parse-events.h"
-#include "util.h"
-
-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, ...)
-{
- va_list ap;
-
- if (!error_str)
- return;
-
- *error_str = malloc_or_die(MAX_ERR_STR_SIZE);
-
- va_start(ap, fmt);
- vsnprintf(*error_str, MAX_ERR_STR_SIZE, fmt, ap);
- va_end(ap);
-}
-
-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 enum event_type
-process_paren(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str);
-
-static enum event_type
-process_not(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str);
-
-static enum event_type
-process_value_token(struct event_format *event, struct filter_arg **parg,
- enum event_type type, char **tok, char **error_str);
-
-static enum event_type
-process_op_token(struct event_format *event, struct filter_arg *larg,
- struct filter_arg **parg, enum event_type type, char **tok,
- char **error_str);
-
-/*
- * process_token
- * Called when a new expression is found. Processes an op, or
- * ends early if a ')' is found.
- *
- * Output: tok, parg
- */
-static enum event_type
-process_token(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *arg = NULL;
- enum event_type type;
- char *token;
-
- *tok = NULL;
- *parg = NULL;
-
- type = read_token(&token);
-
- /*
- * This is a start of a new expresion. We expect to find
- * a item or a parenthesis.
- */
- switch (type) {
- case EVENT_SQUOTE:
- case EVENT_DQUOTE:
- case EVENT_ITEM:
- type = process_value_token(event, &arg, type, &token, error_str);
- if (type == EVENT_ERROR) {
- free_token(token);
- return type;
- }
- type = read_token(&token);
- break;
- case EVENT_DELIM:
- if (strcmp(token, "(") != 0)
- break;
-
- free_token(token);
- type = process_paren(event, &arg, &token, error_str);
- if (type == EVENT_NONE) {
- *tok = token;
- *parg = arg;
- return type;
- }
- if (arg) {
- /*
- * If the parenthesis was a full expression,
- * then just return it. Otherwise, we may still
- * need to find an op.
- */
- switch (arg->type) {
- case FILTER_ARG_OP:
- case FILTER_ARG_NUM:
- case FILTER_ARG_STR:
- *tok = token;
- *parg = arg;
- return type;
- default:
- break;
- }
- }
- break;
-
- case EVENT_OP:
- if (strcmp(token, "!") != 0)
- break;
-
- /*
- * A not is its own filter, it just negates,
- * process it by itself.
- */
- *tok = token;
- type = process_not(event, parg, tok, error_str);
- return type;
-
- default:
- break;
- }
-
- for (;;) {
- if (type == EVENT_NONE) {
- show_error(error_str, "unexpected end of filter");
- type = EVENT_ERROR;
-
- } else if (type == EVENT_DELIM && strcmp(token, ")") == 0) {
- /* Parenthesis call this and may return at anytime. */
- *tok = token;
- *parg = arg;
- return type;
-
- } else if (type != EVENT_OP) {
- show_error(error_str, "Expected an OP but found %s", token);
- type = EVENT_ERROR;
- }
-
- if (type == EVENT_ERROR) {
- free_token(token);
- return type;
- }
-
- *tok = token;
- *parg = NULL;
- type = process_op_token(event, arg, parg, type, tok, error_str);
-
- if (type == EVENT_ERROR) {
- free_arg(*parg);
- *parg = NULL;
- return EVENT_ERROR;
- }
-
- if (!(*parg) || (*parg)->type != FILTER_ARG_EXP)
- break;
-
- /*
- * This op was an expression (value return)
- * It's not fine by itself, there had better be an OP
- * after it.
- */
- token = *tok;
- *tok = NULL;
- arg = *parg;
- }
-
- return type;
-}
-
-/*
- * Input: tok
- * Output: parg, tok
- */
-static enum event_type
-process_bool(struct event_format *event, struct filter_arg *larg,
- struct filter_arg **parg, char **tok, char **error_str)
-{
- struct filter_arg *rarg;
- struct filter_arg *arg;
- enum event_type type;
- enum filter_op_type btype;
-
- /* Can only be called with '&&' or '||' */
- btype = strcmp(*tok, "&&") == 0 ?
- FILTER_OP_AND : FILTER_OP_OR;
-
- type = process_token(event, &rarg, tok, error_str);
- if (type == EVENT_ERROR) {
- free_arg(larg);
- *parg = NULL;
- return type;
- }
-
- /*
- * If larg or rarg is null then if this is AND, the whole expression
- * becomes NULL, else if this is an OR, then we use the non NULL
- * condition.
- */
- if (!larg || !rarg) {
- if (btype == FILTER_OP_AND ||
- (!larg && !rarg)) {
- free_arg(larg);
- free_arg(rarg);
- *parg = NULL;
- return type;
- }
- *parg = larg ? larg : rarg;
- return type;
- }
-
- arg = allocate_arg();
- arg->type = FILTER_ARG_OP;
- arg->op.type = btype;
- arg->op.left = larg;
- arg->op.right = rarg;
-
-
- /*
- * If the next token is also a boolean expression, then
- * make the next boolean the parent..
- */
- if (type != EVENT_OP ||
- (strcmp(*tok, "&&") != 0 && strcmp(*tok, "||") != 0)) {
- *parg = arg;
- return type;
- }
-
- return process_bool(event, arg, parg, tok, error_str);
-}
-
-/*
- * Input: tok
- * Output: parg
- */
-static enum event_type
-process_value_token(struct event_format *event, struct filter_arg **parg,
- enum event_type type, char **tok, char **error_str)
-{
- struct format_field *field;
- struct filter_arg *arg;
- char *token;
-
- token = *tok;
- *tok = NULL;
-
- arg = allocate_arg();
-
- switch (type) {
-
- case EVENT_SQUOTE:
- case EVENT_DQUOTE:
- arg->type = FILTER_ARG_VALUE;
- arg->value.type = FILTER_STRING;
- arg->value.str = token;
- 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 = strtoll(token, NULL, 0);
- free_token(token);
- break;
- }
- /* Consider this a field */
- field = pevent_find_any_field(event, token);
- free_token(token);
- if (!field) {
- /* not a field, so NULL it up */
- free_arg(arg);
- arg = NULL;
- break;
- }
-
- 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);
- free_token(token);
- return EVENT_ERROR;
- }
-
- *parg = arg;
- return type;
-}
-
-/*
- * Output: parg, tok
- */
-static enum event_type
-process_value(struct event_format *event, struct filter_arg **parg,
- enum event_type *orig_type, char **tok, char **error_str)
-{
- enum event_type type;
- char *token;
-
- *tok = NULL;
- type = read_token(&token);
- *orig_type = type;
- if (type == EVENT_DELIM && strcmp(token, "(") == 0) {
- type = process_paren(event, parg, &token, error_str);
- /* Must be a expression or value */
- if (type == EVENT_ERROR || !(*parg)) {
- free_token(token);
- return type;
- }
- switch ((*parg)->type) {
- case FILTER_ARG_BOOLEAN:
- case FILTER_ARG_VALUE:
- case FILTER_ARG_FIELD:
- case FILTER_ARG_EXP:
- break;
- default:
- show_error(error_str, "expected a value");
- free_token(token);
- return EVENT_ERROR;
- }
- } else {
- type = process_value_token(event, parg, type, &token, error_str);
- free_token(token);
- if (type == EVENT_ERROR)
- return type;
- type = read_token(&token);
- }
-
- *tok = token;
- return type;
-}
-
-/*
- * Input: larg
- * Output: parg, tok
- */
-static enum event_type
-process_cmp(struct event_format *event, enum filter_cmp_type op_type,
- struct filter_arg *larg, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *arg;
- struct filter_arg *rarg = NULL;
- enum event_type orig_type;
- enum event_type type;
- int ret;
-
- *parg = NULL;
-
- type = process_value(event, &rarg, &orig_type, tok, error_str);
- if (type == EVENT_ERROR) {
- free_arg(rarg);
- return type;
- }
-
- arg = allocate_arg();
- /*
- * If either arg is NULL or right was field not found.
- * Then make the entire expression NULL. (will turn to FALSE)
- */
- if (!larg || !rarg) {
- free_arg(larg);
- free_arg(rarg);
- free_arg(arg);
- arg = NULL;
- goto cont;
- }
-
- switch (orig_type) {
- case EVENT_SQUOTE:
- /* treat this as a character if string is of length 1? */
- if (strlen(rarg->str.val) == 1) {
- switch (op_type) {
- case FILTER_CMP_REGEX:
- case FILTER_CMP_NOT_REGEX:
- /* regex can't be used with ints */
- break;
- default:
- goto as_int;
- }
- }
- /* fall through */
- case EVENT_DQUOTE:
- arg->type = FILTER_ARG_STR;
-
- if (larg->type != FILTER_ARG_FIELD) {
- free(larg);
- free(rarg);
- show_error(error_str,
- "Illegal lval for string comparison");
- free_arg(arg);
- return EVENT_ERROR;
- }
-
- arg->str.field = larg->field.field;
- free_arg(larg);
-
- /* free the rarg, and use its token */
- arg->str.val = rarg->value.str;
- rarg->value.str = NULL;
- free_arg(rarg);
-
- /* 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(&arg->str.reg, arg->str.val, REG_ICASE|REG_NOSUB);
- if (ret) {
- show_error(error_str,
- "RegEx '%s' did not compute",
- arg->str.val);
- free_arg(arg);
- return EVENT_ERROR;
- }
- break;
- default:
- show_error(error_str,
- "Illegal comparison for string");
- free_arg(arg);
- return EVENT_ERROR;
- }
-
- arg->str.type = op_type;
-
- /*
- * Need a buffer to copy data int for tests */
- arg->str.buffer = malloc_or_die(arg->str.field->size + 1);
- /* Null terminate this buffer */
- arg->str.buffer[arg->str.field->size] = 0;
-
- break;
- default:
- as_int:
- switch (op_type) {
- case FILTER_CMP_REGEX:
- case FILTER_CMP_NOT_REGEX:
- show_error(error_str,
- "Op not allowed with integers");
- free_arg(arg);
- return EVENT_ERROR;
- default:
- break;
- }
- /* numeric compare */
- arg->type = FILTER_ARG_NUM;
- arg->num.type = op_type;
- arg->num.left = larg;
- arg->num.right = rarg;
- break;
- }
- cont:
- *parg = arg;
- return type;
-}
-
-/*
- * Input: larg
- * Output: parg, tok
- */
-static enum event_type
-process_exp(struct event_format *event, enum filter_exp_type etype,
- struct filter_arg *larg, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *rarg = NULL;
- struct filter_arg *arg;
- enum event_type orig_type;
- enum event_type type;
-
- type = process_value(event, &rarg, &orig_type, tok, error_str);
- if (type == EVENT_ERROR) {
- free_arg(rarg);
- return type;
- }
-
- /* larg can be NULL if a field did not match */
- if (!larg) {
- /* syntax is correct, just return NULL */
- arg = NULL;
- free_arg(rarg);
- goto cont;
- }
-
- arg = allocate_arg();
- arg->type = FILTER_ARG_EXP;
- arg->op.type = etype;
- arg->op.left = larg;
- arg->op.right = rarg;
-
- cont:
- /* still need a cmp */
- type = process_op_token(event, arg, parg, type, tok, error_str);
- return type;
-}
-
-/*
- * Input: tok
- * Output: parg, tok
- */
-static enum event_type
-process_op_token(struct event_format *event, struct filter_arg *larg,
- struct filter_arg **parg, enum event_type type, char **tok,
- char **error_str)
-{
- enum filter_cmp_type ctype;
- enum filter_exp_type etype = FILTER_EXP_NONE;
- char *token;
-
- token = *tok;
- *parg = NULL;
-
- if (type != EVENT_OP) {
- *parg = larg;
- return type;
- }
-
- if (strcmp(token, "&&") == 0 || strcmp(token, "||") == 0) {
- /* handle boolean cases */
- return process_bool(event, larg, parg, tok, error_str);
- }
-
- /* 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) {
- free_token(token);
- return process_exp(event, etype, larg, parg, tok, error_str);
- }
-
- 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 {
- show_error(error_str,
- "Unknown op '%s'", token);
- free_token(token);
- return EVENT_ERROR;
- }
-
- free_token(token);
- *tok = NULL;
- return process_cmp(event, ctype, larg, parg, tok, error_str);
-}
-
-static enum event_type
-process_filter(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *larg = NULL;
- enum event_type type;
-
- *parg = NULL;
- *tok = NULL;
-
- type = process_token(event, parg, tok, error_str);
-
- if (type == EVENT_OP &&
- (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
- larg = *parg;
- *parg = NULL;
- type = process_bool(event, larg, parg, tok, error_str);
- }
-
- return type;
-}
-
-static enum event_type
-process_paren(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *arg;
- enum event_type type;
-
- *parg = NULL;
-
- type = process_token(event, &arg, tok, error_str);
- if (type == EVENT_ERROR) {
- free_arg(arg);
- return type;
- }
-
- if (type == EVENT_OP &&
- (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
- type = process_bool(event, arg, parg, tok, error_str);
- }
-
- if (type != EVENT_DELIM || strcmp(*tok, ")") != 0) {
- if (*tok)
- show_error(error_str,
- "Expected ')' but found %s", *tok);
- else
- show_error(error_str,
- "Unexpected end of filter; Expected ')'");
- free_token(*tok);
- *tok = NULL;
- free_arg(arg);
- return EVENT_ERROR;
- }
- free_token(*tok);
- *tok = NULL;
-
- *parg = arg;
-
- return read_token(tok);
-}
-
-static enum event_type
-process_not(struct event_format *event, struct filter_arg **parg,
- char **tok, char **error_str)
-{
- struct filter_arg *arg;
- enum event_type type;
-
- arg = allocate_arg();
- arg->type = FILTER_ARG_OP;
- arg->op.type = FILTER_OP_NOT;
-
- arg->op.left = NULL;
- type = process_token(event, &arg->op.right, tok, error_str);
- if (type == EVENT_ERROR) {
- free_arg(arg);
- *parg = NULL;
- free_token(*tok);
- *tok = NULL;
- return EVENT_ERROR;
- }
- /* If the bool value is NULL, then make this into TRUE */
- if (!arg->op.right) {
- arg->type = FILTER_ARG_BOOLEAN;
- arg->boolean.value = FILTER_TRUE;
- }
-
- *parg = arg;
- free_token(*tok);
- *tok = NULL;
-
- return type;
-}
-
-static int
-process_event(struct event_format *event, const char *filter_str,
- struct filter_arg **parg, char **error_str)
-{
- enum event_type type;
- char *token;
-
- pevent_buffer_init(filter_str, strlen(filter_str));
-
- type = process_filter(event, parg, &token, error_str);
-
- if (type == EVENT_ERROR)
- return -1;
-
- if (type != EVENT_NONE) {
- show_error(error_str,
- "Expected end where %s was found",
- token);
- free_token(token);
- free_arg(*parg);
- *parg = NULL;
- return -1;
- }
-
- /* 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;
-
- 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;
- }
-
- free_events(events);
-
- 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 record *record);
-
-static unsigned long long
-get_value(struct format_field *field, struct record *record)
-{
- unsigned long long val;
-
- 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 record *record);
-
-static unsigned long long
-get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record)
-{
- unsigned long long lval, rval;
-
- lval = get_arg_value(event, arg->op.left, record);
- rval = get_arg_value(event, arg->op.right, record);
-
- switch (arg->op.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 record *record)
-{
- switch (arg->type) {
- case FILTER_ARG_FIELD:
- return get_value(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 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 int test_str(struct event_format *event,
- struct filter_arg *arg, struct record *record)
-{
- const char *val = record->data + arg->str.field->offset;
- const char *buffer;
-
- /*
- * 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 */
- buffer = arg->str.buffer;
- } else
- /* OK, it's NULL terminated */
- buffer = val;
-
- switch (arg->str.type) {
- case FILTER_CMP_MATCH:
- return strcmp(buffer, arg->str.val) == 0;
-
- case FILTER_CMP_NOT_MATCH:
- return strcmp(buffer, arg->str.val) != 0;
-
- case FILTER_CMP_REGEX:
- /* Returns zero on match */
- return !regexec(&arg->str.reg, buffer, 0, NULL, 0);
-
- case FILTER_CMP_NOT_REGEX:
- return regexec(&arg->str.reg, buffer, 0, NULL, 0);
-
- default:
- /* ?? */
- return 0;
- }
-}
-
-static int test_op(struct event_format *event,
- struct filter_arg *arg, struct 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 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 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->op.left);
- rstr = arg_to_str(filter, arg->op.right);
-
- switch (arg->op.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/parse-utils.c b/tools/lib/parse-utils.c
deleted file mode 100644
index f023a13..0000000
--- a/tools/lib/parse-utils.c
+++ /dev/null
@@ -1,110 +0,0 @@
-#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/trace-seq.c b/tools/lib/trace-seq.c
deleted file mode 100644
index d84a5bd..0000000
--- a/tools/lib/trace-seq.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-
-#include "parse-events.h"
-
-/**
- * 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, ...)
-{
- int len = (TRACE_SEQ_SIZE - 1) - s->len;
- va_list ap;
- int ret;
-
- if (s->full || !len)
- return 0;
-
- va_start(ap, fmt);
- ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
- va_end(ap);
-
- /* If we can't write it all, don't bother writing anything */
- if (ret >= len) {
- s->full = 1;
- return 0;
- }
-
- 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 = (TRACE_SEQ_SIZE - 1) - s->len;
- int ret;
-
- if (s->full || !len)
- return 0;
-
- ret = vsnprintf(s->buffer + s->len, len, fmt, args);
-
- /* If we can't write it all, don't bother writing anything */
- if (ret >= len) {
- s->full = 1;
- return 0;
- }
-
- s->len += ret;
-
- return len;
-}
-
-/**
- * trace_seq_puts - trace sequence printing of simple string
- * @s: trace sequence descriptor
- * @str: simple string to record
- *
- * The tracer may use either the sequence operations or its own
- * copy to user routines. This function records a simple string
- * into a special buffer (@s) for later retrieval by a sequencer
- * or other mechanism.
- */
-int trace_seq_puts(struct trace_seq *s, const char *str)
-{
- int len = strlen(str);
-
- if (s->full)
- return 0;
-
- if (len > ((TRACE_SEQ_SIZE - 1) - s->len)) {
- s->full = 1;
- return 0;
- }
-
- memcpy(s->buffer + s->len, str, len);
- s->len += len;
-
- return len;
-}
-
-int trace_seq_putc(struct trace_seq *s, unsigned char c)
-{
- if (s->full)
- return 0;
-
- if (s->len >= (TRACE_SEQ_SIZE - 1)) {
- s->full = 1;
- return 0;
- }
-
- s->buffer[s->len++] = c;
-
- return 1;
-}
-
-void trace_seq_terminate(struct trace_seq *s)
-{
- if (!s->full)
- s->buffer[s->len] = 0;
-}
-
-int trace_seq_do_printf(struct trace_seq *s)
-{
- return printf("%.*s%s", s->len, s->buffer,
- s->full ? "[truncated]" : "");
-}
diff --git a/tools/lib/trace/Makefile b/tools/lib/trace/Makefile
new file mode 100644
index 0000000..4c625e5
--- /dev/null
+++ b/tools/lib/trace/Makefile
@@ -0,0 +1,54 @@
+include ../../scripts/Makefile.lib
+
+# Make the path relative to DESTDIR, not to prefix
+ifndef DESTDIR
+prefix = $(HOME)
+endif
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+sharedir = $(prefix)/share
+ifeq ($(prefix),/usr)
+sysconfdir = /etc
+else
+sysconfdir = $(prefix)/etc
+endif
+
+export prefix bindir sharedir sysconfdir
+
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+RM = rm -f
+TAR = tar
+FIND = find
+INSTALL = install
+RPMBUILD = rpmbuild
+PTHREAD_LIBS = -lpthread
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+TRACE_LIB = $(LIB_OUTPUT)libparsevent.a
+
+all: $(TRACE_LIB)
+
+PEVENT_LIB_OBJS += parse-events.o
+PEVENT_LIB_OBJS += parse-filter.o
+PEVENT_LIB_OBJS += parse-utils.o
+PEVENT_LIB_OBJS += trace-seq.o
+
+$(OUTPUT)%.o: %.c
+ $(QUIET_CC)$(CC) -g -o $@ -c $(ALL_CFLAGS) $<
+
+$(TRACE_LIB): $(PEVENT_LIB_OBJS)
+ $(RM) $@; $(AR) rcs $@ $^
+
+clean:
+ $(RM) *.a *.o *~ *.so $(TRACE_LIB)
+
+.PHONY: clean
diff --git a/tools/lib/trace/parse-events.c b/tools/lib/trace/parse-events.c
new file mode 100644
index 0000000..5503a18
--- /dev/null
+++ b/tools/lib/trace/parse-events.c
@@ -0,0 +1,4655 @@
+/*
+ * 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., 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
+ * - 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 "parse-events.h"
+
+static const char *input_buf;
+static unsigned long long input_buf_ptr;
+static unsigned long long input_buf_siz;
+
+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;
+}
+
+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, 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 = 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, 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);
+ 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:
+ 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;
+ 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;
+ }
+
+ 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 '/':
+ 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);
+}
+
+/* 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 '-':
+ 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;
+
+ set_op_prio(arg);
+
+ 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;
+
+ 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 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);
+ val = eval_type(val, arg, 0);
+ 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 '-':
+ /* check for negative */
+ if (arg->op.left->type == PRINT_NULL)
+ left = 0;
+ else
+ 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_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);
+ 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);
+ 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);
+ 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;
+
+ 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);
+ }
+
+ 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);
+ return process_flags(event, arg, tok);
+ }
+ if (strcmp(token, "__print_symbolic") == 0) {
+ free_token(token);
+ 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)
+{
+ 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:
+ return 0;
+ case PRINT_FUNC: {
+ struct trace_seq s;
+ trace_seq_init(&s);
+ return process_defined_func(&s, data, size, event, arg);
+ }
+ 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 '|':
+ 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 },
+};
+
+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_arg(struct trace_seq *s, void *data, int size,
+ struct event_format *event, 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:
+ trace_seq_puts(s, 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;
+
+ /*
+ * 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) &&
+ len == 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;
+ trace_seq_puts(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) {
+ trace_seq_puts(s, flag->str);
+ break;
+ }
+ if (fval && (val & fval) == fval) {
+ if (print && arg->flags.delim)
+ trace_seq_puts(s, arg->flags.delim);
+ trace_seq_puts(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) {
+ trace_seq_puts(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 = pevent_find_any_field(event, arg->string.string);
+ arg->string.offset = f->offset;
+ }
+ str_offset = data2host4(pevent, data + arg->string.offset);
+ str_offset &= 0xffff;
+ trace_seq_puts(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(s, data, size, event, arg->op.right->op.left);
+ else
+ print_str_arg(s, data, size, event, 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, farg);
+ trace_seq_terminate(&str);
+ string = malloc_or_die(sizeof(*string));
+ string->next = strings;
+ string->str = strdup(str.buffer);
+ strings = string;
+ 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:
+ 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_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;
+
+ 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");
+ printf("field->offset = %d size=%d\n", field->offset, field->size);
+ 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;
+ 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;
+ cont_process:
+ ptr++;
+ switch (*ptr) {
+ case '%':
+ trace_seq_putc(s, '%');
+ break;
+ case '#':
+ /* FIXME: need to handle properly */
+ goto cont_process;
+ case 'l':
+ ls++;
+ goto cont_process;
+ case 'L':
+ ls = 2;
+ 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 > 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(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 0:
+ trace_seq_printf(s, format, (int)val);
+ break;
+ case 1:
+ trace_seq_printf(s, format, (long)val);
+ break;
+ case 2:
+ trace_seq_printf(s, format, (long long)val);
+ break;
+ default:
+ die("bad count (%d)", ls);
+ }
+ break;
+ case 's':
+ if (!arg)
+ die("no matching argument");
+
+ print_str_arg(s, data, size, event, 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 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 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 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 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 record *record)
+{
+ static char *spaces = " "; /* 20 spaces */
+ struct event_format *event;
+ unsigned long secs;
+ unsigned long usecs;
+ const char *comm;
+ void *data = record->data;
+ int type;
+ int pid;
+ int len;
+
+ secs = record->ts / NSECS_PER_SEC;
+ usecs = record->ts - secs * NSECS_PER_SEC;
+ usecs = (usecs + 500) / NSECS_PER_USEC;
+
+ 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);
+
+ trace_seq_printf(s, " %5lu.%06lu: %s: ", secs, 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);
+ 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);
+ 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);
+ }
+}
+
+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;
+}
+
+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 = pevent->cmdlist, *cmdnext;
+ struct func_list *funclist = pevent->funclist, *funcnext;
+ struct printk_list *printklist = pevent->printklist, *printknext;
+ struct pevent_function_handler *func_handler;
+ struct event_handler *handle;
+ int i;
+
+ 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/trace/parse-events.h b/tools/lib/trace/parse-events.h
new file mode 100644
index 0000000..5c10208
--- /dev/null
+++ b/tools/lib/trace/parse-events.h
@@ -0,0 +1,719 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_SIZE
+#define TRACE_SEQ_SIZE 4096
+#endif
+
+struct 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;
+};
+
+/*
+ * 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[TRACE_SEQ_SIZE];
+ unsigned int len;
+ unsigned int readpos;
+ int full;
+};
+
+static inline void
+trace_seq_init(struct trace_seq *s)
+{
+ s->len = 0;
+ s->readpos = 0;
+ s->full = 0;
+}
+
+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 record *record,
+ struct event_format *event,
+ void *context);
+
+typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
+typedef int (*pevent_plugin_unload_func)(void);
+
+#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
+#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
+#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 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_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
+};
+
+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;
+
+ 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;
+};
+
+/* Can be overridden */
+void *malloc_or_die(unsigned int size);
+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 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, 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 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);
+
+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 record *record);
+int pevent_data_type(struct pevent *pevent, struct record *rec);
+struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
+int pevent_data_pid(struct pevent *pevent, struct 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 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);
+
+/* 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
+};
+
+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_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_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 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/trace/parse-filter.c b/tools/lib/trace/parse-filter.c
new file mode 100644
index 0000000..9cc64fe
--- /dev/null
+++ b/tools/lib/trace/parse-filter.c
@@ -0,0 +1,2085 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "parse-events.h"
+#include "trace-util.h"
+
+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, ...)
+{
+ va_list ap;
+
+ if (!error_str)
+ return;
+
+ *error_str = malloc_or_die(MAX_ERR_STR_SIZE);
+
+ va_start(ap, fmt);
+ vsnprintf(*error_str, MAX_ERR_STR_SIZE, fmt, ap);
+ va_end(ap);
+}
+
+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 enum event_type
+process_paren(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str);
+
+static enum event_type
+process_not(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str);
+
+static enum event_type
+process_value_token(struct event_format *event, struct filter_arg **parg,
+ enum event_type type, char **tok, char **error_str);
+
+static enum event_type
+process_op_token(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, enum event_type type, char **tok,
+ char **error_str);
+
+/*
+ * process_token
+ * Called when a new expression is found. Processes an op, or
+ * ends early if a ')' is found.
+ *
+ * Output: tok, parg
+ */
+static enum event_type
+process_token(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg = NULL;
+ enum event_type type;
+ char *token;
+
+ *tok = NULL;
+ *parg = NULL;
+
+ type = read_token(&token);
+
+ /*
+ * This is a start of a new expresion. We expect to find
+ * a item or a parenthesis.
+ */
+ switch (type) {
+ case EVENT_SQUOTE:
+ case EVENT_DQUOTE:
+ case EVENT_ITEM:
+ type = process_value_token(event, &arg, type, &token, error_str);
+ if (type == EVENT_ERROR) {
+ free_token(token);
+ return type;
+ }
+ type = read_token(&token);
+ break;
+ case EVENT_DELIM:
+ if (strcmp(token, "(") != 0)
+ break;
+
+ free_token(token);
+ type = process_paren(event, &arg, &token, error_str);
+ if (type == EVENT_NONE) {
+ *tok = token;
+ *parg = arg;
+ return type;
+ }
+ if (arg) {
+ /*
+ * If the parenthesis was a full expression,
+ * then just return it. Otherwise, we may still
+ * need to find an op.
+ */
+ switch (arg->type) {
+ case FILTER_ARG_OP:
+ case FILTER_ARG_NUM:
+ case FILTER_ARG_STR:
+ *tok = token;
+ *parg = arg;
+ return type;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case EVENT_OP:
+ if (strcmp(token, "!") != 0)
+ break;
+
+ /*
+ * A not is its own filter, it just negates,
+ * process it by itself.
+ */
+ *tok = token;
+ type = process_not(event, parg, tok, error_str);
+ return type;
+
+ default:
+ break;
+ }
+
+ for (;;) {
+ if (type == EVENT_NONE) {
+ show_error(error_str, "unexpected end of filter");
+ type = EVENT_ERROR;
+
+ } else if (type == EVENT_DELIM && strcmp(token, ")") == 0) {
+ /* Parenthesis call this and may return at anytime. */
+ *tok = token;
+ *parg = arg;
+ return type;
+
+ } else if (type != EVENT_OP) {
+ show_error(error_str, "Expected an OP but found %s", token);
+ type = EVENT_ERROR;
+ }
+
+ if (type == EVENT_ERROR) {
+ free_token(token);
+ return type;
+ }
+
+ *tok = token;
+ *parg = NULL;
+ type = process_op_token(event, arg, parg, type, tok, error_str);
+
+ if (type == EVENT_ERROR) {
+ free_arg(*parg);
+ *parg = NULL;
+ return EVENT_ERROR;
+ }
+
+ if (!(*parg) || (*parg)->type != FILTER_ARG_EXP)
+ break;
+
+ /*
+ * This op was an expression (value return)
+ * It's not fine by itself, there had better be an OP
+ * after it.
+ */
+ token = *tok;
+ *tok = NULL;
+ arg = *parg;
+ }
+
+ return type;
+}
+
+/*
+ * Input: tok
+ * Output: parg, tok
+ */
+static enum event_type
+process_bool(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, char **tok, char **error_str)
+{
+ struct filter_arg *rarg;
+ struct filter_arg *arg;
+ enum event_type type;
+ enum filter_op_type btype;
+
+ /* Can only be called with '&&' or '||' */
+ btype = strcmp(*tok, "&&") == 0 ?
+ FILTER_OP_AND : FILTER_OP_OR;
+
+ type = process_token(event, &rarg, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(larg);
+ *parg = NULL;
+ return type;
+ }
+
+ /*
+ * If larg or rarg is null then if this is AND, the whole expression
+ * becomes NULL, else if this is an OR, then we use the non NULL
+ * condition.
+ */
+ if (!larg || !rarg) {
+ if (btype == FILTER_OP_AND ||
+ (!larg && !rarg)) {
+ free_arg(larg);
+ free_arg(rarg);
+ *parg = NULL;
+ return type;
+ }
+ *parg = larg ? larg : rarg;
+ return type;
+ }
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_OP;
+ arg->op.type = btype;
+ arg->op.left = larg;
+ arg->op.right = rarg;
+
+
+ /*
+ * If the next token is also a boolean expression, then
+ * make the next boolean the parent..
+ */
+ if (type != EVENT_OP ||
+ (strcmp(*tok, "&&") != 0 && strcmp(*tok, "||") != 0)) {
+ *parg = arg;
+ return type;
+ }
+
+ return process_bool(event, arg, parg, tok, error_str);
+}
+
+/*
+ * Input: tok
+ * Output: parg
+ */
+static enum event_type
+process_value_token(struct event_format *event, struct filter_arg **parg,
+ enum event_type type, char **tok, char **error_str)
+{
+ struct format_field *field;
+ struct filter_arg *arg;
+ char *token;
+
+ token = *tok;
+ *tok = NULL;
+
+ arg = allocate_arg();
+
+ switch (type) {
+
+ case EVENT_SQUOTE:
+ case EVENT_DQUOTE:
+ arg->type = FILTER_ARG_VALUE;
+ arg->value.type = FILTER_STRING;
+ arg->value.str = token;
+ 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 = strtoll(token, NULL, 0);
+ free_token(token);
+ break;
+ }
+ /* Consider this a field */
+ field = pevent_find_any_field(event, token);
+ free_token(token);
+ if (!field) {
+ /* not a field, so NULL it up */
+ free_arg(arg);
+ arg = NULL;
+ break;
+ }
+
+ 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);
+ free_token(token);
+ return EVENT_ERROR;
+ }
+
+ *parg = arg;
+ return type;
+}
+
+/*
+ * Output: parg, tok
+ */
+static enum event_type
+process_value(struct event_format *event, struct filter_arg **parg,
+ enum event_type *orig_type, char **tok, char **error_str)
+{
+ enum event_type type;
+ char *token;
+
+ *tok = NULL;
+ type = read_token(&token);
+ *orig_type = type;
+ if (type == EVENT_DELIM && strcmp(token, "(") == 0) {
+ type = process_paren(event, parg, &token, error_str);
+ /* Must be a expression or value */
+ if (type == EVENT_ERROR || !(*parg)) {
+ free_token(token);
+ return type;
+ }
+ switch ((*parg)->type) {
+ case FILTER_ARG_BOOLEAN:
+ case FILTER_ARG_VALUE:
+ case FILTER_ARG_FIELD:
+ case FILTER_ARG_EXP:
+ break;
+ default:
+ show_error(error_str, "expected a value");
+ free_token(token);
+ return EVENT_ERROR;
+ }
+ } else {
+ type = process_value_token(event, parg, type, &token, error_str);
+ free_token(token);
+ if (type == EVENT_ERROR)
+ return type;
+ type = read_token(&token);
+ }
+
+ *tok = token;
+ return type;
+}
+
+/*
+ * Input: larg
+ * Output: parg, tok
+ */
+static enum event_type
+process_cmp(struct event_format *event, enum filter_cmp_type op_type,
+ struct filter_arg *larg, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ struct filter_arg *rarg = NULL;
+ enum event_type orig_type;
+ enum event_type type;
+ int ret;
+
+ *parg = NULL;
+
+ type = process_value(event, &rarg, &orig_type, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(rarg);
+ return type;
+ }
+
+ arg = allocate_arg();
+ /*
+ * If either arg is NULL or right was field not found.
+ * Then make the entire expression NULL. (will turn to FALSE)
+ */
+ if (!larg || !rarg) {
+ free_arg(larg);
+ free_arg(rarg);
+ free_arg(arg);
+ arg = NULL;
+ goto cont;
+ }
+
+ switch (orig_type) {
+ case EVENT_SQUOTE:
+ /* treat this as a character if string is of length 1? */
+ if (strlen(rarg->str.val) == 1) {
+ switch (op_type) {
+ case FILTER_CMP_REGEX:
+ case FILTER_CMP_NOT_REGEX:
+ /* regex can't be used with ints */
+ break;
+ default:
+ goto as_int;
+ }
+ }
+ /* fall through */
+ case EVENT_DQUOTE:
+ arg->type = FILTER_ARG_STR;
+
+ if (larg->type != FILTER_ARG_FIELD) {
+ free(larg);
+ free(rarg);
+ show_error(error_str,
+ "Illegal lval for string comparison");
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+
+ arg->str.field = larg->field.field;
+ free_arg(larg);
+
+ /* free the rarg, and use its token */
+ arg->str.val = rarg->value.str;
+ rarg->value.str = NULL;
+ free_arg(rarg);
+
+ /* 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(&arg->str.reg, arg->str.val, REG_ICASE|REG_NOSUB);
+ if (ret) {
+ show_error(error_str,
+ "RegEx '%s' did not compute",
+ arg->str.val);
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+ break;
+ default:
+ show_error(error_str,
+ "Illegal comparison for string");
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+
+ arg->str.type = op_type;
+
+ /*
+ * Need a buffer to copy data int for tests */
+ arg->str.buffer = malloc_or_die(arg->str.field->size + 1);
+ /* Null terminate this buffer */
+ arg->str.buffer[arg->str.field->size] = 0;
+
+ break;
+ default:
+ as_int:
+ switch (op_type) {
+ case FILTER_CMP_REGEX:
+ case FILTER_CMP_NOT_REGEX:
+ show_error(error_str,
+ "Op not allowed with integers");
+ free_arg(arg);
+ return EVENT_ERROR;
+ default:
+ break;
+ }
+ /* numeric compare */
+ arg->type = FILTER_ARG_NUM;
+ arg->num.type = op_type;
+ arg->num.left = larg;
+ arg->num.right = rarg;
+ break;
+ }
+ cont:
+ *parg = arg;
+ return type;
+}
+
+/*
+ * Input: larg
+ * Output: parg, tok
+ */
+static enum event_type
+process_exp(struct event_format *event, enum filter_exp_type etype,
+ struct filter_arg *larg, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *rarg = NULL;
+ struct filter_arg *arg;
+ enum event_type orig_type;
+ enum event_type type;
+
+ type = process_value(event, &rarg, &orig_type, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(rarg);
+ return type;
+ }
+
+ /* larg can be NULL if a field did not match */
+ if (!larg) {
+ /* syntax is correct, just return NULL */
+ arg = NULL;
+ free_arg(rarg);
+ goto cont;
+ }
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_EXP;
+ arg->op.type = etype;
+ arg->op.left = larg;
+ arg->op.right = rarg;
+
+ cont:
+ /* still need a cmp */
+ type = process_op_token(event, arg, parg, type, tok, error_str);
+ return type;
+}
+
+/*
+ * Input: tok
+ * Output: parg, tok
+ */
+static enum event_type
+process_op_token(struct event_format *event, struct filter_arg *larg,
+ struct filter_arg **parg, enum event_type type, char **tok,
+ char **error_str)
+{
+ enum filter_cmp_type ctype;
+ enum filter_exp_type etype = FILTER_EXP_NONE;
+ char *token;
+
+ token = *tok;
+ *parg = NULL;
+
+ if (type != EVENT_OP) {
+ *parg = larg;
+ return type;
+ }
+
+ if (strcmp(token, "&&") == 0 || strcmp(token, "||") == 0) {
+ /* handle boolean cases */
+ return process_bool(event, larg, parg, tok, error_str);
+ }
+
+ /* 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) {
+ free_token(token);
+ return process_exp(event, etype, larg, parg, tok, error_str);
+ }
+
+ 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 {
+ show_error(error_str,
+ "Unknown op '%s'", token);
+ free_token(token);
+ return EVENT_ERROR;
+ }
+
+ free_token(token);
+ *tok = NULL;
+ return process_cmp(event, ctype, larg, parg, tok, error_str);
+}
+
+static enum event_type
+process_filter(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *larg = NULL;
+ enum event_type type;
+
+ *parg = NULL;
+ *tok = NULL;
+
+ type = process_token(event, parg, tok, error_str);
+
+ if (type == EVENT_OP &&
+ (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
+ larg = *parg;
+ *parg = NULL;
+ type = process_bool(event, larg, parg, tok, error_str);
+ }
+
+ return type;
+}
+
+static enum event_type
+process_paren(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ enum event_type type;
+
+ *parg = NULL;
+
+ type = process_token(event, &arg, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(arg);
+ return type;
+ }
+
+ if (type == EVENT_OP &&
+ (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) {
+ type = process_bool(event, arg, parg, tok, error_str);
+ }
+
+ if (type != EVENT_DELIM || strcmp(*tok, ")") != 0) {
+ if (*tok)
+ show_error(error_str,
+ "Expected ')' but found %s", *tok);
+ else
+ show_error(error_str,
+ "Unexpected end of filter; Expected ')'");
+ free_token(*tok);
+ *tok = NULL;
+ free_arg(arg);
+ return EVENT_ERROR;
+ }
+ free_token(*tok);
+ *tok = NULL;
+
+ *parg = arg;
+
+ return read_token(tok);
+}
+
+static enum event_type
+process_not(struct event_format *event, struct filter_arg **parg,
+ char **tok, char **error_str)
+{
+ struct filter_arg *arg;
+ enum event_type type;
+
+ arg = allocate_arg();
+ arg->type = FILTER_ARG_OP;
+ arg->op.type = FILTER_OP_NOT;
+
+ arg->op.left = NULL;
+ type = process_token(event, &arg->op.right, tok, error_str);
+ if (type == EVENT_ERROR) {
+ free_arg(arg);
+ *parg = NULL;
+ free_token(*tok);
+ *tok = NULL;
+ return EVENT_ERROR;
+ }
+ /* If the bool value is NULL, then make this into TRUE */
+ if (!arg->op.right) {
+ arg->type = FILTER_ARG_BOOLEAN;
+ arg->boolean.value = FILTER_TRUE;
+ }
+
+ *parg = arg;
+ free_token(*tok);
+ *tok = NULL;
+
+ return type;
+}
+
+static int
+process_event(struct event_format *event, const char *filter_str,
+ struct filter_arg **parg, char **error_str)
+{
+ enum event_type type;
+ char *token;
+
+ pevent_buffer_init(filter_str, strlen(filter_str));
+
+ type = process_filter(event, parg, &token, error_str);
+
+ if (type == EVENT_ERROR)
+ return -1;
+
+ if (type != EVENT_NONE) {
+ show_error(error_str,
+ "Expected end where %s was found",
+ token);
+ free_token(token);
+ free_arg(*parg);
+ *parg = NULL;
+ return -1;
+ }
+
+ /* 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;
+
+ 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;
+ }
+
+ free_events(events);
+
+ 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 record *record);
+
+static unsigned long long
+get_value(struct format_field *field, struct record *record)
+{
+ unsigned long long val;
+
+ 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 record *record);
+
+static unsigned long long
+get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record)
+{
+ unsigned long long lval, rval;
+
+ lval = get_arg_value(event, arg->op.left, record);
+ rval = get_arg_value(event, arg->op.right, record);
+
+ switch (arg->op.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 record *record)
+{
+ switch (arg->type) {
+ case FILTER_ARG_FIELD:
+ return get_value(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 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 int test_str(struct event_format *event,
+ struct filter_arg *arg, struct record *record)
+{
+ const char *val = record->data + arg->str.field->offset;
+ const char *buffer;
+
+ /*
+ * 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 */
+ buffer = arg->str.buffer;
+ } else
+ /* OK, it's NULL terminated */
+ buffer = val;
+
+ switch (arg->str.type) {
+ case FILTER_CMP_MATCH:
+ return strcmp(buffer, arg->str.val) == 0;
+
+ case FILTER_CMP_NOT_MATCH:
+ return strcmp(buffer, arg->str.val) != 0;
+
+ case FILTER_CMP_REGEX:
+ /* Returns zero on match */
+ return !regexec(&arg->str.reg, buffer, 0, NULL, 0);
+
+ case FILTER_CMP_NOT_REGEX:
+ return regexec(&arg->str.reg, buffer, 0, NULL, 0);
+
+ default:
+ /* ?? */
+ return 0;
+ }
+}
+
+static int test_op(struct event_format *event,
+ struct filter_arg *arg, struct 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 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 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->op.left);
+ rstr = arg_to_str(filter, arg->op.right);
+
+ switch (arg->op.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/trace/parse-utils.c b/tools/lib/trace/parse-utils.c
new file mode 100644
index 0000000..f023a13
--- /dev/null
+++ b/tools/lib/trace/parse-utils.c
@@ -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/trace/trace-seq.c b/tools/lib/trace/trace-seq.c
new file mode 100644
index 0000000..d84a5bd
--- /dev/null
+++ b/tools/lib/trace/trace-seq.c
@@ -0,0 +1,153 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "parse-events.h"
+
+/**
+ * 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, ...)
+{
+ int len = (TRACE_SEQ_SIZE - 1) - s->len;
+ va_list ap;
+ int ret;
+
+ if (s->full || !len)
+ return 0;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+ va_end(ap);
+
+ /* If we can't write it all, don't bother writing anything */
+ if (ret >= len) {
+ s->full = 1;
+ return 0;
+ }
+
+ 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 = (TRACE_SEQ_SIZE - 1) - s->len;
+ int ret;
+
+ if (s->full || !len)
+ return 0;
+
+ ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+
+ /* If we can't write it all, don't bother writing anything */
+ if (ret >= len) {
+ s->full = 1;
+ return 0;
+ }
+
+ s->len += ret;
+
+ return len;
+}
+
+/**
+ * trace_seq_puts - trace sequence printing of simple string
+ * @s: trace sequence descriptor
+ * @str: simple string to record
+ *
+ * The tracer may use either the sequence operations or its own
+ * copy to user routines. This function records a simple string
+ * into a special buffer (@s) for later retrieval by a sequencer
+ * or other mechanism.
+ */
+int trace_seq_puts(struct trace_seq *s, const char *str)
+{
+ int len = strlen(str);
+
+ if (s->full)
+ return 0;
+
+ if (len > ((TRACE_SEQ_SIZE - 1) - s->len)) {
+ s->full = 1;
+ return 0;
+ }
+
+ memcpy(s->buffer + s->len, str, len);
+ s->len += len;
+
+ return len;
+}
+
+int trace_seq_putc(struct trace_seq *s, unsigned char c)
+{
+ if (s->full)
+ return 0;
+
+ if (s->len >= (TRACE_SEQ_SIZE - 1)) {
+ s->full = 1;
+ return 0;
+ }
+
+ s->buffer[s->len++] = c;
+
+ return 1;
+}
+
+void trace_seq_terminate(struct trace_seq *s)
+{
+ if (!s->full)
+ s->buffer[s->len] = 0;
+}
+
+int trace_seq_do_printf(struct trace_seq *s)
+{
+ return printf("%.*s%s", s->len, s->buffer,
+ s->full ? "[truncated]" : "");
+}
diff --git a/tools/lib/trace/trace-util.h b/tools/lib/trace/trace-util.h
new file mode 100644
index 0000000..d3faf10
--- /dev/null
+++ b/tools/lib/trace/trace-util.h
@@ -0,0 +1,64 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef __TRACE_UTIL_H
+#define __TRACE_UTIL_H
+
+#include <ctype.h>
+
+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 /* __TRACE_UTIL_H */
diff --git a/tools/lib/util.h b/tools/lib/util.h
deleted file mode 100644
index 2bb9fe6..0000000
--- a/tools/lib/util.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#ifndef __UTIL_H
-#define __UTIL_H
-
-#include <ctype.h>
-
-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/perf/Makefile b/tools/perf/Makefile
index f7a2626..425a572 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -1,12 +1,7 @@
-ifeq ("$(origin O)", "command line")
- OUTPUT := $(O)/
- LIB_OUTPUT := $(OUTPUT)generic-lib/
-else
- LIB_OUTPUT := ../lib/
-endif
-
export OUTPUT ALL_CFLAGS
+include ../scripts/Makefile.lib
+
# The default target of this Makefile is...
all::
@@ -195,33 +190,6 @@ endif
# CFLAGS and LDFLAGS are for the users to override from the command line.
-#
-# 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) -Wstack-protector
-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) -Wvolatile-register-var
-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
@@ -862,11 +830,6 @@ ifdef ASCIIDOC8
export ASCIIDOC8
endif
-force:
-
-$(LIB_OUTPUT)libparsevent.a: force
- $(MAKE) -C ../lib libparsevent.a
-
# Shell quote (do not use $(call) to accommodate ancient setups);
SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index e9923bc..6597c73 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -1,7 +1,7 @@
#ifndef _PERF_UTIL_TRACE_EVENT_H
#define _PERF_UTIL_TRACE_EVENT_H
-#include "../../lib/parse-events.h"
+#include "../../lib/trace/parse-events.h"
#include "parse-events.h"
extern int header_page_size_size;
diff --git a/tools/scripts/Makefile.lib b/tools/scripts/Makefile.lib
new file mode 100644
index 0000000..f125d62
--- /dev/null
+++ b/tools/scripts/Makefile.lib
@@ -0,0 +1,33 @@
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+ QUIET_CC = @echo ' ' CC $@;
+ QUIET_AR = @echo ' ' AR $@;
+ QUIET_LINK = @echo ' ' LINK $@;
+ QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
+ QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+ export V
+ export QUIET_GEN
+ export QUIET_BUILT_IN
+endif
+endif
+
+ifeq ("$(origin O)", "command line")
+ OUTPUT := $(O)/
+ LIB_OUTPUT := $(OUTPUT)generic-lib/
+ __dummy := $(shell if [ ! -d $(LIB_OUTPUT) ]; then mkdir -p $(LIB_OUTPUT); fi)
+
+else
+ LIB_OUTPUT := $(PERF_TOP_DIR)/lib/
+endif
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 07/20] perf: Export debugfs utilities
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (5 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 06/20] perf: Move trace stuff into tools/lib/trace Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 08/20] perf: Export cpumap Borislav Petkov
` (13 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Export /proc/mounts parser and other debugfs-related helpers for general
use. Also, exit if a valid debugfs mountpoint cannot be found.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 8 +-
tools/lib/lk/Makefile | 38 +++
tools/lib/lk/debugfs.c | 256 ++++++++++++++++++
tools/lib/lk/debugfs.h | 31 +++
tools/lib/lk/types.h | 17 ++
tools/lib/lk/usage.c | 80 ++++++
tools/lib/lk/util.h | 285 ++++++++++++++++++++
tools/perf/Makefile | 11 +-
tools/perf/bench/bench.h | 2 +
tools/perf/bench/mem-memcpy.c | 2 +-
tools/perf/bench/sched-messaging.c | 2 +-
tools/perf/bench/sched-pipe.c | 2 +-
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-bench.c | 2 +-
tools/perf/builtin-diff.c | 2 +-
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-kvm.c | 2 +-
tools/perf/builtin-lock.c | 2 +-
tools/perf/builtin-probe.c | 5 +-
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-sched.c | 2 +-
tools/perf/builtin-stat.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 2 +-
tools/perf/builtin.h | 2 +-
tools/perf/perf.c | 23 +--
tools/perf/perf.h | 2 +-
tools/perf/util/build-id.c | 2 +-
tools/perf/util/cache.h | 4 +-
tools/perf/util/callchain.c | 2 +-
tools/perf/util/config.c | 2 +-
tools/perf/util/cpumap.c | 2 +-
tools/perf/util/debug.c | 2 +-
tools/perf/util/debugfs.c | 240 ----------------
tools/perf/util/debugfs.h | 25 --
| 2 +-
| 2 +-
tools/perf/util/hist.c | 2 +-
tools/perf/util/include/linux/ctype.h | 2 +-
tools/perf/util/map.h | 2 +-
tools/perf/util/parse-events.c | 5 +-
tools/perf/util/parse-options.c | 2 +-
tools/perf/util/probe-event.c | 5 +-
tools/perf/util/probe-finder.c | 2 +-
tools/perf/util/probe-finder.h | 2 +-
tools/perf/util/pstack.c | 2 +-
.../perf/util/scripting-engines/trace-event-perl.c | 2 +-
.../util/scripting-engines/trace-event-python.c | 2 +-
tools/perf/util/session.c | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/string.c | 2 +-
tools/perf/util/svghelper.h | 2 +-
tools/perf/util/thread.c | 2 +-
tools/perf/util/trace-event-info.c | 3 +-
tools/perf/util/trace-event-parse.c | 2 +-
tools/perf/util/trace-event-read.c | 2 +-
tools/perf/util/trace-event-scripting.c | 2 +-
tools/perf/util/types.h | 17 --
tools/perf/util/usage.c | 80 ------
tools/perf/util/util.c | 2 +-
tools/perf/util/util.h | 285 --------------------
tools/perf/util/values.c | 2 +-
tools/perf/util/values.h | 2 +-
65 files changed, 782 insertions(+), 728 deletions(-)
create mode 100644 tools/lib/lk/Makefile
create mode 100644 tools/lib/lk/debugfs.c
create mode 100644 tools/lib/lk/debugfs.h
create mode 100644 tools/lib/lk/types.h
create mode 100644 tools/lib/lk/usage.c
create mode 100644 tools/lib/lk/util.h
delete mode 100644 tools/perf/util/debugfs.c
delete mode 100644 tools/perf/util/debugfs.h
delete mode 100644 tools/perf/util/types.h
delete mode 100644 tools/perf/util/usage.c
delete mode 100644 tools/perf/util/util.h
diff --git a/tools/Makefile b/tools/Makefile
index 430b25e..d3b1447 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -28,20 +28,24 @@ EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
export EXTRA_WARNINGS
# lib includes for submake
-BASIC_CFLAGS = -I$(CURDIR)/lib/trace -I$(CURDIR)/perf -I$(CURDIR)/perf/util/include
+BASIC_CFLAGS = -I$(CURDIR)/lib/trace -I$(CURDIR)/lib -I$(CURDIR)/perf -I$(CURDIR)/perf/util/include
export BASIC_CFLAGS
PERF_TOP_DIR := $(CURDIR)
export PERF_TOP_DIR
-perf: libparsevent .FORCE
+perf: libparsevent lklib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
libparsevent: .FORCE
$(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1)
+lklib: .FORCE
+ $(QUIET_SUBDIR0)lib/lk/ $(QUIET_SUBDIR1)
+
clean:
$(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)lib/lk/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
.PHONY: clean .FORCE
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
new file mode 100644
index 0000000..823bbb5
--- /dev/null
+++ b/tools/lib/lk/Makefile
@@ -0,0 +1,38 @@
+include ../../scripts/Makefile.lib
+
+# guard against environment variables
+LIB_H=
+LIB_OBJS=
+
+LIB_H += debugfs.h
+LIB_H += util.h
+LIB_H += types.h
+
+LIB_OBJS += debugfs.o
+LIB_OBJS += usage.o
+
+LIBFILE = $(LIB_OUTPUT)lklib.a
+
+CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+EXTLIBS = -lpthread -lrt -lelf -lm
+ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+ALL_LDFLAGS = $(LDFLAGS)
+
+RM = rm -f
+
+$(LIBFILE): $(LIB_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+
+$(LIB_OBJS): $(LIB_H)
+
+%.o: %.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+%.s: %.c
+ $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+%.o: %.S
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
+clean:
+ $(RM) $(LIB_OBJS) $(LIBFILE)
+
+.PHONY: clean
diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c
new file mode 100644
index 0000000..c4e7ffe
--- /dev/null
+++ b/tools/lib/lk/debugfs.c
@@ -0,0 +1,256 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "util.h"
+#include "debugfs.h"
+
+static int debugfs_premounted;
+static char debugfs_mountpoint[MAX_PATH+1];
+char debugfs_mntpt[MAXPATHLEN];
+
+static const char *debugfs_known_mountpoints[] = {
+ "/sys/kernel/debug/",
+ "/debug/",
+ 0,
+};
+
+/* use this to force a umount */
+void debugfs_force_cleanup(void)
+{
+ debugfs_find_mountpoint();
+ debugfs_premounted = 0;
+ debugfs_umount();
+}
+
+/* construct a full path to a debugfs element */
+int debugfs_make_path(const char *element, char *buffer, int size)
+{
+ int len;
+
+ if (strlen(debugfs_mountpoint) == 0) {
+ buffer[0] = '\0';
+ return -1;
+ }
+
+ len = strlen(debugfs_mountpoint) + strlen(element) + 1;
+ if (len >= size)
+ return len+1;
+
+ snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
+ return 0;
+}
+
+static int debugfs_found;
+
+/* find the path to the mounted debugfs */
+const char *debugfs_find_mountpoint(void)
+{
+ const char **ptr;
+ char type[100];
+ FILE *fp;
+
+ if (debugfs_found)
+ return (const char *) debugfs_mountpoint;
+
+ ptr = debugfs_known_mountpoints;
+ while (*ptr) {
+ if (debugfs_valid_mountpoint(*ptr) == 0) {
+ debugfs_found = 1;
+ strcpy(debugfs_mountpoint, *ptr);
+ return debugfs_mountpoint;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ die("Can't open /proc/mounts for read");
+
+ while (fscanf(fp, "%*s %"
+ STR(MAX_PATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs_mountpoint, type) == 2) {
+ if (strcmp(type, "debugfs") == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0)
+ return NULL;
+
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* verify that a mountpoint is actually a debugfs instance */
+
+int debugfs_valid_mountpoint(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+
+ return 0;
+}
+
+
+int debugfs_valid_entry(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return -errno;
+
+ return 0;
+}
+
+/* mount the debugfs somewhere if it's not mounted */
+
+char *debugfs_mount(const char *mountpoint)
+{
+ /* see if it's already mounted */
+ if (debugfs_find_mountpoint()) {
+ debugfs_premounted = 1;
+ return debugfs_mountpoint;
+ }
+
+ /* if not mounted and no argument */
+ if (mountpoint == NULL) {
+ /* see if environment variable set */
+ mountpoint = getenv(LK_DEBUGFS_ENVIRONMENT);
+ /* if no environment variable, use default */
+ if (mountpoint == NULL)
+ mountpoint = "/sys/kernel/debug";
+ }
+
+ if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
+ return NULL;
+
+ /* save the mountpoint */
+ strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* umount the debugfs */
+
+int debugfs_umount(void)
+{
+ char umountcmd[128];
+ int ret;
+
+ /* if it was already mounted, leave it */
+ if (debugfs_premounted)
+ return 0;
+
+ /* make sure it's a valid mount point */
+ ret = debugfs_valid_mountpoint(debugfs_mountpoint);
+ if (ret)
+ return ret;
+
+ snprintf(umountcmd, sizeof(umountcmd),
+ "/bin/umount %s", debugfs_mountpoint);
+ return system(umountcmd);
+}
+
+int debugfs_write(const char *entry, const char *value)
+{
+ char path[MAX_PATH+1];
+ int ret, count;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* get how many chars we're going to write */
+ count = strlen(value);
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -errno;
+
+ while (count > 0) {
+ /* write it */
+ ret = write(fd, value, count);
+ if (ret <= 0) {
+ if (ret == EAGAIN)
+ continue;
+ close(fd);
+ return -errno;
+ }
+ count -= ret;
+ }
+
+ /* close it */
+ close(fd);
+
+ /* return success */
+ return 0;
+}
+
+/*
+ * read a debugfs entry
+ * returns the number of chars read or a negative errno
+ */
+int debugfs_read(const char *entry, char *buffer, size_t size)
+{
+ char path[MAX_PATH+1];
+ int ret;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ do {
+ /* read it */
+ ret = read(fd, buffer, size);
+ if (ret == 0) {
+ close(fd);
+ return EOF;
+ }
+ } while (ret < 0 && errno == EAGAIN);
+
+ /* close it */
+ close(fd);
+
+ /* make *sure* there's a null character at the end */
+ buffer[ret] = '\0';
+
+ /* return the number of chars read */
+ return ret;
+}
+
+/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
+int get_debugfs_mntpt(void)
+{
+ const char *path = debugfs_mount(NULL);
+
+ if (path) {
+ strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
+ return 0;
+ }
+ debugfs_mntpt[0] = '\0';
+ return 1;
+}
diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h
new file mode 100644
index 0000000..5970f0b
--- /dev/null
+++ b/tools/lib/lk/debugfs.h
@@ -0,0 +1,31 @@
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+#include <sys/mount.h>
+#include <sys/types.h>
+
+#ifndef MAX_PATH
+# define MAX_PATH 256
+#endif
+
+#ifndef STR
+# define _STR(x) #x
+# define STR(x) _STR(x)
+#endif
+
+#define LK_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
+
+extern char debugfs_mntpt[];
+
+extern const char *debugfs_find_mountpoint(void);
+extern int debugfs_valid_mountpoint(const char *debugfs);
+extern int debugfs_valid_entry(const char *path);
+extern char *debugfs_mount(const char *mountpoint);
+extern int debugfs_umount(void);
+extern int debugfs_write(const char *entry, const char *value);
+extern int debugfs_read(const char *entry, char *buffer, size_t size);
+extern void debugfs_force_cleanup(void);
+extern int debugfs_make_path(const char *element, char *buffer, int size);
+extern int get_debugfs_mntpt(void);
+
+#endif /* __DEBUGFS_H__ */
diff --git a/tools/lib/lk/types.h b/tools/lib/lk/types.h
new file mode 100644
index 0000000..eda6c0c
--- /dev/null
+++ b/tools/lib/lk/types.h
@@ -0,0 +1,17 @@
+#ifndef __LK_TYPES_H
+#define __LK_TYPES_H
+
+/*
+ * We define u64 as unsigned long long for every architecture
+ * so that we can print it with %Lx without getting warnings.
+ */
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned char u8;
+typedef signed char s8;
+
+#endif /* __LK_TYPES_H */
diff --git a/tools/lib/lk/usage.c b/tools/lib/lk/usage.c
new file mode 100644
index 0000000..e16bf9a
--- /dev/null
+++ b/tools/lib/lk/usage.c
@@ -0,0 +1,80 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "util.h"
+
+static void report(const char *prefix, const char *err, va_list params)
+{
+ char msg[1024];
+ vsnprintf(msg, sizeof(msg), err, params);
+ fprintf(stderr, " %s%s\n", prefix, msg);
+}
+
+static NORETURN void usage_builtin(const char *err)
+{
+ fprintf(stderr, "\n Usage: %s\n", err);
+ exit(129);
+}
+
+static NORETURN void die_builtin(const char *err, va_list params)
+{
+ report(" Fatal: ", err, params);
+ exit(128);
+}
+
+static void error_builtin(const char *err, va_list params)
+{
+ report(" Error: ", err, params);
+}
+
+static void warn_builtin(const char *warn, va_list params)
+{
+ report(" Warning: ", warn, params);
+}
+
+/* If we are in a dlopen()ed .so write to a global variable would segfault
+ * (ugh), so keep things static. */
+static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
+static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
+static void (*error_routine)(const char *err, va_list params) = error_builtin;
+static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
+
+void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
+{
+ die_routine = routine;
+}
+
+void usage(const char *err)
+{
+ usage_routine(err);
+}
+
+void die(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ die_routine(err, params);
+ va_end(params);
+}
+
+int error(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ error_routine(err, params);
+ va_end(params);
+ return -1;
+}
+
+void warning(const char *warn, ...)
+{
+ va_list params;
+
+ va_start(params, warn);
+ warn_routine(warn, params);
+ va_end(params);
+}
diff --git a/tools/lib/lk/util.h b/tools/lib/lk/util.h
new file mode 100644
index 0000000..f380fed
--- /dev/null
+++ b/tools/lib/lk/util.h
@@ -0,0 +1,285 @@
+#ifndef GIT_COMPAT_UTIL_H
+#define GIT_COMPAT_UTIL_H
+
+#define _FILE_OFFSET_BITS 64
+
+#ifndef FLEX_ARRAY
+/*
+ * See if our compiler is known to support flexible array members.
+ */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
+#elif defined(__GNUC__)
+# if (__GNUC__ >= 3)
+# define FLEX_ARRAY /* empty */
+# else
+# define FLEX_ARRAY 0 /* older GNU extension */
+# endif
+#endif
+
+/*
+ * Otherwise, default to safer but a bit wasteful traditional style
+ */
+#ifndef FLEX_ARRAY
+# define FLEX_ARRAY 1
+#endif
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#ifdef __GNUC__
+#define TYPEOF(x) (__typeof__(x))
+#else
+#define TYPEOF(x)
+#endif
+
+#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
+#define HAS_MULTI_BITS(i) ((i) & ((i) - 1)) /* checks if an integer has more than 1 bit set */
+
+/* Approximation of the length of the decimal representation of this type. */
+#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
+
+#define _ALL_SOURCE 1
+#define _GNU_SOURCE 1
+#define _BSD_SOURCE 1
+#define HAS_BOOL
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifndef NO_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <netinet/in.h>
+#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"
+#include <sys/ttydefaults.h>
+
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
+
+extern const char *graph_line;
+extern const char *graph_dotted_line;
+extern char buildid_dir[];
+
+/* On most systems <limits.h> would have given us this, but
+ * not on some systems (e.g. GNU/Hurd).
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifndef PRIuMAX
+#define PRIuMAX "llu"
+#endif
+
+#ifndef PRIu32
+#define PRIu32 "u"
+#endif
+
+#ifndef PRIx32
+#define PRIx32 "x"
+#endif
+
+#ifndef PATH_SEP
+#define PATH_SEP ':'
+#endif
+
+#ifndef STRIP_EXTENSION
+#define STRIP_EXTENSION ""
+#endif
+
+#ifndef has_dos_drive_prefix
+#define has_dos_drive_prefix(path) 0
+#endif
+
+#ifndef is_dir_sep
+#define is_dir_sep(c) ((c) == '/')
+#endif
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+#endif
+
+/* General helper functions */
+extern void usage(const char *err) NORETURN;
+extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
+extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+#include "../../../include/linux/stringify.h"
+
+#define DIE_IF(cnd) \
+ do { if (cnd) \
+ die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \
+ __stringify(cnd) "\n"); \
+ } while (0)
+
+
+extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
+
+extern int prefixcmp(const char *str, const char *prefix);
+extern void set_buildid_dir(void);
+extern void disable_buildid_cache(void);
+
+static inline const char *skip_prefix(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 1)
+#define HAVE_STRCHRNUL
+#endif
+#endif
+
+#ifndef HAVE_STRCHRNUL
+#define strchrnul gitstrchrnul
+static inline char *gitstrchrnul(const char *s, int c)
+{
+ while (*s && *s != c)
+ s++;
+ return (char *)s;
+}
+#endif
+
+/*
+ * Wrappers:
+ */
+extern char *xstrdup(const char *str);
+extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
+
+
+static inline void *zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline int has_extension(const char *filename, const char *ext)
+{
+ size_t len = strlen(filename);
+ size_t extlen = strlen(ext);
+
+ return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
+}
+
+/* Sane ctype - no locale, and works with signed chars */
+#undef isascii
+#undef isspace
+#undef isdigit
+#undef isxdigit
+#undef isalpha
+#undef isprint
+#undef isalnum
+#undef tolower
+#undef toupper
+
+extern unsigned char sane_ctype[256];
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PRINT_EXTRA 0x20
+#define GIT_PRINT 0x3E
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isascii(x) (((x) & ~0x7f) == 0)
+#define isspace(x) sane_istest(x,GIT_SPACE)
+#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isxdigit(x) \
+ (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
+#define isalpha(x) sane_istest(x,GIT_ALPHA)
+#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isprint(x) sane_istest(x,GIT_PRINT)
+#define tolower(x) sane_case((unsigned char)(x), 0x20)
+#define toupper(x) sane_case((unsigned char)(x), 0)
+
+static inline int sane_case(int x, int high)
+{
+ if (sane_istest(x, GIT_ALPHA))
+ x = (x & ~0x20) | high;
+ return x;
+}
+
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
+int mkdir_p(char *path, mode_t mode);
+int copyfile(const char *from, const char *to);
+
+s64 perf_atoll(const char *str);
+char **argv_split(const char *str, int *argcp);
+void argv_free(char **argv);
+bool strglobmatch(const char *str, const char *pat);
+bool strlazymatch(const char *str, const char *pat);
+unsigned long convert_unit(unsigned long value, char *unit);
+
+#ifndef ESC
+#define ESC 27
+#endif
+
+static inline bool is_exit_key(int key)
+{
+ char up;
+ if (key == CTRL('c') || key == ESC)
+ return true;
+ up = toupper(key);
+ return up == 'Q';
+}
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+#endif
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 425a572..d8ef5a5 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -270,7 +270,7 @@ endif
# Those must not be GNU-specific; they are shared with perl/ which may
# be built by a different compiler. (Note that this is an artifact now
# but it still might be nice to keep that distinction.)
-BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include
+BASIC_CFLAGS += -Iarch/$(ARCH)/include
BASIC_LDFLAGS =
# Guard against environment variables
@@ -328,7 +328,7 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a
-EXTRA_LIB_FILE=$(LIB_OUTPUT)libparsevent.a
+EXTRA_LIB_FILES=$(LIB_OUTPUT)libparsevent.a $(LIB_OUTPUT)lklib.a
LIB_H += ../../include/linux/perf_event.h
LIB_H += ../../include/linux/rbtree.h
@@ -360,16 +360,13 @@ LIB_H += util/cache.h
LIB_H += util/callchain.h
LIB_H += util/build-id.h
LIB_H += util/debug.h
-LIB_H += util/debugfs.h
LIB_H += util/event.h
LIB_H += util/exec_cmd.h
-LIB_H += util/types.h
LIB_H += util/levenshtein.h
LIB_H += util/map.h
LIB_H += util/parse-options.h
LIB_H += util/parse-events.h
LIB_H += util/quote.h
-LIB_H += util/util.h
LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
@@ -395,7 +392,6 @@ LIB_OBJS += $(OUTPUT)util/alias.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
-LIB_OBJS += $(OUTPUT)util/debugfs.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
@@ -412,7 +408,6 @@ LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
LIB_OBJS += $(OUTPUT)util/string.o
LIB_OBJS += $(OUTPUT)util/strlist.o
-LIB_OBJS += $(OUTPUT)util/usage.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/symbol.o
@@ -465,7 +460,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
-PERFLIBS = $(LIB_FILE) $(EXTRA_LIB_FILE)
+PERFLIBS = $(LIB_FILE) $(EXTRA_LIB_FILES)
#
# Platform specific tweaks
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index f7781c6..0c7ee07 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -1,6 +1,8 @@
#ifndef BENCH_H
#define BENCH_H
+#include <linux/compiler.h>
+
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used);
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index 38dae74..1b2f508 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -8,7 +8,7 @@
#include <ctype.h>
#include "../perf.h"
-#include "../util/util.h"
+#include <lk/util.h>
#include "../util/parse-options.h"
#include "../util/header.h"
#include "bench.h"
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
index d1d1b30..37f12ad 100644
--- a/tools/perf/bench/sched-messaging.c
+++ b/tools/perf/bench/sched-messaging.c
@@ -10,7 +10,7 @@
*/
#include "../perf.h"
-#include "../util/util.h"
+#include <lk/util.h>
#include "../util/parse-options.h"
#include "../builtin.h"
#include "bench.h"
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c
index d9ab3ce..9b05c92 100644
--- a/tools/perf/bench/sched-pipe.c
+++ b/tools/perf/bench/sched-pipe.c
@@ -11,7 +11,7 @@
*/
#include "../perf.h"
-#include "../util/util.h"
+#include <lk/util.h>
#include "../util/parse-options.h"
#include "../builtin.h"
#include "bench.h"
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 1478dc6..19f2751 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -7,7 +7,7 @@
*/
#include "builtin.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/color.h"
#include <linux/list.h>
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index fcb9626..4ae8ea2 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -17,7 +17,7 @@
*/
#include "perf.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/parse-options.h"
#include "builtin.h"
#include "bench/bench.h"
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index fca1d44..83c0a89 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -12,7 +12,7 @@
#include "util/session.h"
#include "util/sort.h"
#include "util/symbol.h"
-#include "util/util.h"
+#include <lk/util.h>
#include <stdlib.h>
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 97b3970..95901eb 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 34d1e85..3e5d0fc 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index fbc512a..76253d7 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..a762878 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -32,13 +32,14 @@
#include <string.h>
#undef _GNU_SOURCE
+#include <lk/util.h>
+#include <lk/debugfs.h>
+
#include "perf.h"
#include "builtin.h"
-#include "util/util.h"
#include "util/strlist.h"
#include "util/symbol.h"
#include "util/debug.h"
-#include "util/debugfs.h"
#include "util/parse-options.h"
#include "util/probe-finder.h"
#include "util/probe-event.h"
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ff77b80..936e231 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -12,7 +12,7 @@
#include "perf.h"
#include "util/build-id.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 55fc1f4..f8564f9 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -7,7 +7,7 @@
*/
#include "builtin.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/color.h"
#include <linux/list.h>
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 3e98de5..a9ec0c3 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index a6b4d44..12dabd3 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -39,7 +39,7 @@
#include "perf.h"
#include "builtin.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/event.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9bcc38f..fe79fc6 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -14,7 +14,7 @@
#include "builtin.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/color.h"
#include <linux/list.h>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index b513e40..7ba3759 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -24,7 +24,7 @@
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/util.h"
+#include <lk/util.h>
#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 40a6a29..beebd43 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -10,7 +10,7 @@
#include "util/symbol.h"
#include "util/thread.h"
#include "util/trace-event.h"
-#include "util/util.h"
+#include <lk/util.h>
static char const *script_name;
static char const *generate_script_lang;
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 921245b..08e69d2 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -1,7 +1,7 @@
#ifndef BUILTIN_H
#define BUILTIN_H
-#include "util/util.h"
+#include <lk/util.h>
#include "util/strbuf.h"
extern const char perf_version_string[];
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index cdd6c03..1fa597c 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -6,14 +6,14 @@
* This is the main hub from which the sub-commands (perf stat,
* perf top, perf record, perf report, etc.) are started.
*/
-#include "builtin.h"
+#include <lk/debugfs.h>
+#include "builtin.h"
#include "util/exec_cmd.h"
#include "util/cache.h"
#include "util/quote.h"
#include "util/run-command.h"
#include "util/parse-events.h"
-#include "util/debugfs.h"
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -29,8 +29,6 @@ struct pager_config {
int val;
};
-static char debugfs_mntpt[MAXPATHLEN];
-
static int pager_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
@@ -85,7 +83,7 @@ static void set_debugfs_path(void)
{
char *path;
- path = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ path = getenv(LK_DEBUGFS_ENVIRONMENT);
snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
"tracing/events");
}
@@ -415,17 +413,6 @@ static int run_argv(int *argcp, const char ***argv)
return done_alias;
}
-/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
-static void get_debugfs_mntpt(void)
-{
- const char *path = debugfs_mount(NULL);
-
- if (path)
- strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
- else
- debugfs_mntpt[0] = '\0';
-}
-
int main(int argc, const char **argv)
{
const char *cmd;
@@ -434,7 +421,9 @@ int main(int argc, const char **argv)
if (!cmd)
cmd = "perf-help";
/* get debugfs mount point from /proc/mounts */
- get_debugfs_mntpt();
+ if(get_debugfs_mntpt())
+ die("cannot find debugfs mountpoint");
+
/*
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
*
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 95aaf56..4bfd513 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -91,7 +91,7 @@ void get_term_dimensions(struct winsize *ws);
#include <sys/syscall.h>
#include "../../include/linux/perf_event.h"
-#include "util/types.h"
+#include <lk/types.h>
#include <stdbool.h>
/*
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e437edb..ce89873 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -6,7 +6,7 @@
* Copyright (C) 2009, 2010 Red Hat Inc.
* Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
-#include "util.h"
+#include <lk/util.h>
#include <stdio.h>
#include "build-id.h"
#include "event.h"
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 27e9ebe..ce83e82 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -2,9 +2,10 @@
#define __PERF_CACHE_H
#include <stdbool.h>
-#include "util.h"
+#include <lk/util.h>
#include "strbuf.h"
#include "../perf.h"
+#include <linux/compiler.h>
#define CMD_EXEC_PATH "--exec-path"
#define CMD_PERF_DIR "--perf-dir="
@@ -15,7 +16,6 @@
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
-#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index f231f43..56fe538 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -15,7 +15,7 @@
#include <errno.h>
#include <math.h>
-#include "util.h"
+#include <lk/util.h>
#include "callchain.h"
bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e02d78c..bb2f5a0 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -5,7 +5,7 @@
* Copyright (C) Johannes Schindelin, 2005
*
*/
-#include "util.h"
+#include <lk/util.h>
#include "cache.h"
#include "exec_cmd.h"
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 0f9b8d7..8ef8cef 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -1,4 +1,4 @@
-#include "util.h"
+#include <lk/util.h>
#include "../perf.h"
#include "cpumap.h"
#include <assert.h>
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index f9c7e3a..2b7ce22 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -10,7 +10,7 @@
#include "color.h"
#include "event.h"
#include "debug.h"
-#include "util.h"
+#include <lk/util.h>
int verbose = 0;
bool dump_trace = false;
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c
deleted file mode 100644
index a88fefc..0000000
--- a/tools/perf/util/debugfs.c
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "util.h"
-#include "debugfs.h"
-#include "cache.h"
-
-static int debugfs_premounted;
-static char debugfs_mountpoint[MAX_PATH+1];
-
-static const char *debugfs_known_mountpoints[] = {
- "/sys/kernel/debug/",
- "/debug/",
- 0,
-};
-
-/* use this to force a umount */
-void debugfs_force_cleanup(void)
-{
- debugfs_find_mountpoint();
- debugfs_premounted = 0;
- debugfs_umount();
-}
-
-/* construct a full path to a debugfs element */
-int debugfs_make_path(const char *element, char *buffer, int size)
-{
- int len;
-
- if (strlen(debugfs_mountpoint) == 0) {
- buffer[0] = '\0';
- return -1;
- }
-
- len = strlen(debugfs_mountpoint) + strlen(element) + 1;
- if (len >= size)
- return len+1;
-
- snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
- return 0;
-}
-
-static int debugfs_found;
-
-/* find the path to the mounted debugfs */
-const char *debugfs_find_mountpoint(void)
-{
- const char **ptr;
- char type[100];
- FILE *fp;
-
- if (debugfs_found)
- return (const char *) debugfs_mountpoint;
-
- ptr = debugfs_known_mountpoints;
- while (*ptr) {
- if (debugfs_valid_mountpoint(*ptr) == 0) {
- debugfs_found = 1;
- strcpy(debugfs_mountpoint, *ptr);
- return debugfs_mountpoint;
- }
- ptr++;
- }
-
- /* give up and parse /proc/mounts */
- fp = fopen("/proc/mounts", "r");
- if (fp == NULL)
- die("Can't open /proc/mounts for read");
-
- while (fscanf(fp, "%*s %"
- STR(MAX_PATH)
- "s %99s %*s %*d %*d\n",
- debugfs_mountpoint, type) == 2) {
- if (strcmp(type, "debugfs") == 0)
- break;
- }
- fclose(fp);
-
- if (strcmp(type, "debugfs") != 0)
- return NULL;
-
- debugfs_found = 1;
-
- return debugfs_mountpoint;
-}
-
-/* verify that a mountpoint is actually a debugfs instance */
-
-int debugfs_valid_mountpoint(const char *debugfs)
-{
- struct statfs st_fs;
-
- if (statfs(debugfs, &st_fs) < 0)
- return -ENOENT;
- else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
- return -ENOENT;
-
- return 0;
-}
-
-
-int debugfs_valid_entry(const char *path)
-{
- struct stat st;
-
- if (stat(path, &st))
- return -errno;
-
- return 0;
-}
-
-/* mount the debugfs somewhere if it's not mounted */
-
-char *debugfs_mount(const char *mountpoint)
-{
- /* see if it's already mounted */
- if (debugfs_find_mountpoint()) {
- debugfs_premounted = 1;
- return debugfs_mountpoint;
- }
-
- /* if not mounted and no argument */
- if (mountpoint == NULL) {
- /* see if environment variable set */
- mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
- /* if no environment variable, use default */
- if (mountpoint == NULL)
- mountpoint = "/sys/kernel/debug";
- }
-
- if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
- return NULL;
-
- /* save the mountpoint */
- strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
- debugfs_found = 1;
-
- return debugfs_mountpoint;
-}
-
-/* umount the debugfs */
-
-int debugfs_umount(void)
-{
- char umountcmd[128];
- int ret;
-
- /* if it was already mounted, leave it */
- if (debugfs_premounted)
- return 0;
-
- /* make sure it's a valid mount point */
- ret = debugfs_valid_mountpoint(debugfs_mountpoint);
- if (ret)
- return ret;
-
- snprintf(umountcmd, sizeof(umountcmd),
- "/bin/umount %s", debugfs_mountpoint);
- return system(umountcmd);
-}
-
-int debugfs_write(const char *entry, const char *value)
-{
- char path[MAX_PATH+1];
- int ret, count;
- int fd;
-
- /* construct the path */
- snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
-
- /* verify that it exists */
- ret = debugfs_valid_entry(path);
- if (ret)
- return ret;
-
- /* get how many chars we're going to write */
- count = strlen(value);
-
- /* open the debugfs entry */
- fd = open(path, O_RDWR);
- if (fd < 0)
- return -errno;
-
- while (count > 0) {
- /* write it */
- ret = write(fd, value, count);
- if (ret <= 0) {
- if (ret == EAGAIN)
- continue;
- close(fd);
- return -errno;
- }
- count -= ret;
- }
-
- /* close it */
- close(fd);
-
- /* return success */
- return 0;
-}
-
-/*
- * read a debugfs entry
- * returns the number of chars read or a negative errno
- */
-int debugfs_read(const char *entry, char *buffer, size_t size)
-{
- char path[MAX_PATH+1];
- int ret;
- int fd;
-
- /* construct the path */
- snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
-
- /* verify that it exists */
- ret = debugfs_valid_entry(path);
- if (ret)
- return ret;
-
- /* open the debugfs entry */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
-
- do {
- /* read it */
- ret = read(fd, buffer, size);
- if (ret == 0) {
- close(fd);
- return EOF;
- }
- } while (ret < 0 && errno == EAGAIN);
-
- /* close it */
- close(fd);
-
- /* make *sure* there's a null character at the end */
- buffer[ret] = '\0';
-
- /* return the number of chars read */
- return ret;
-}
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h
deleted file mode 100644
index 83a0287..0000000
--- a/tools/perf/util/debugfs.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __DEBUGFS_H__
-#define __DEBUGFS_H__
-
-#include <sys/mount.h>
-
-#ifndef MAX_PATH
-# define MAX_PATH 256
-#endif
-
-#ifndef STR
-# define _STR(x) #x
-# define STR(x) _STR(x)
-#endif
-
-extern const char *debugfs_find_mountpoint(void);
-extern int debugfs_valid_mountpoint(const char *debugfs);
-extern int debugfs_valid_entry(const char *path);
-extern char *debugfs_mount(const char *mountpoint);
-extern int debugfs_umount(void);
-extern int debugfs_write(const char *entry, const char *value);
-extern int debugfs_read(const char *entry, char *buffer, size_t size);
-extern void debugfs_force_cleanup(void);
-extern int debugfs_make_path(const char *element, char *buffer, int size);
-
-#endif /* __DEBUGFS_H__ */
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d7e67b1..0ebd301 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -8,7 +8,7 @@
#include <linux/list.h>
#include <linux/kernel.h>
-#include "util.h"
+#include <lk/util.h>
#include "header.h"
#include "../perf.h"
#include "trace-event.h"
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 402ac24..fb6f0eb 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -4,7 +4,7 @@
#include "../../../include/linux/perf_event.h"
#include <sys/types.h>
#include <stdbool.h>
-#include "types.h"
+#include <lk/types.h>
#include "event.h"
#include <linux/bitmap.h>
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index be22ae6..cfbfd6b 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,4 +1,4 @@
-#include "util.h"
+#include <lk/util.h>
#include "build-id.h"
#include "hist.h"
#include "session.h"
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h
index a53d4ee..0698f26 100644
--- a/tools/perf/util/include/linux/ctype.h
+++ b/tools/perf/util/include/linux/ctype.h
@@ -1 +1 @@
-#include "../util.h"
+#include <lk/util.h>
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 7857579..729469e 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -6,7 +6,7 @@
#include <linux/rbtree.h>
#include <stdio.h>
#include <stdbool.h>
-#include "types.h"
+#include <lk/types.h>
enum map_type {
MAP__FUNCTION = 0,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4af5bd5..01686b5 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,5 +1,7 @@
+#include <lk/util.h>
+#include <lk/debugfs.h>
+
#include "../../../include/linux/hw_breakpoint.h"
-#include "util.h"
#include "../perf.h"
#include "parse-options.h"
#include "parse-events.h"
@@ -8,7 +10,6 @@
#include "symbol.h"
#include "cache.h"
#include "header.h"
-#include "debugfs.h"
int nr_counters;
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 99d02aa..25b57fc 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -1,4 +1,4 @@
-#include "util.h"
+#include <lk/util.h>
#include "parse-options.h"
#include "cache.h"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index fcc16e4..5f97d04 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -33,7 +33,9 @@
#include <limits.h>
#undef _GNU_SOURCE
-#include "util.h"
+#include <lk/util.h>
+#include <lk/debugfs.h>
+
#include "event.h"
#include "string.h"
#include "strlist.h"
@@ -42,7 +44,6 @@
#include "color.h"
#include "symbol.h"
#include "thread.h"
-#include "debugfs.h"
#include "trace-event.h" /* For __unused */
#include "probe-event.h"
#include "probe-finder.h"
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 32b81f7..c7a8bdb 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -35,7 +35,7 @@
#include "event.h"
#include "debug.h"
-#include "util.h"
+#include <lk/util.h>
#include "symbol.h"
#include "probe-finder.h"
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 4507d51..fe0457b 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -2,7 +2,7 @@
#define _PROBE_FINDER_H
#include <stdbool.h>
-#include "util.h"
+#include <lk/util.h>
#include "probe-event.h"
#define MAX_PATH_LEN 256
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
index 13d36fa..aacedb8 100644
--- a/tools/perf/util/pstack.c
+++ b/tools/perf/util/pstack.c
@@ -4,7 +4,7 @@
* (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
-#include "util.h"
+#include <lk/util.h>
#include "pstack.h"
#include <linux/kernel.h>
#include <stdlib.h>
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index b059dc5..78af7d1 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -26,7 +26,7 @@
#include <errno.h>
#include "../../perf.h"
-#include "../util.h"
+#include <lk/util.h>
#include "../trace-event.h"
#include <EXTERN.h>
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 9e8d535..524d355 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -28,7 +28,7 @@
#include <errno.h>
#include "../../perf.h"
-#include "../util.h"
+#include <lk/util.h>
#include "../trace-event.h"
PyMODINIT_FUNC initperf_trace_context(void);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index fa9d652..73cac6a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -9,7 +9,7 @@
#include "session.h"
#include "sort.h"
-#include "util.h"
+#include <lk/util.h>
static int perf_session__open(struct perf_session *self, bool force)
{
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 46e531d..0c68709 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -2,7 +2,7 @@
#define __PERF_SORT_H
#include "../builtin.h"
-#include "util.h"
+#include <lk/util.h>
#include "color.h"
#include <linux/list.h>
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 0409fc7..881ef63 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -1,4 +1,4 @@
-#include "util.h"
+#include <lk/util.h>
#include "string.h"
#define K 1024LL
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index e078198..ac74b40 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -1,7 +1,7 @@
#ifndef __PERF_SVGHELPER_H
#define __PERF_SVGHELPER_H
-#include "types.h"
+#include <lk/types.h>
extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 8c72d88..8ab6e09 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -4,7 +4,7 @@
#include <string.h>
#include "session.h"
#include "thread.h"
-#include "util.h"
+#include <lk/util.h>
#include "debug.h"
/* Skip "." and ".." directories */
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index d5083d8..44831be 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -36,9 +36,10 @@
#include <stdbool.h>
#include <linux/kernel.h>
+#include <lk/debugfs.h>
+
#include "../perf.h"
#include "trace-event.h"
-#include "debugfs.h"
#define VERSION "0.5"
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index c1d3097..489907c 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -27,7 +27,7 @@
#undef _GNU_SOURCE
#include "../perf.h"
-#include "util.h"
+#include <lk/util.h>
#include "trace-event.h"
int header_page_size_size;
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 188e100..d42a5a9 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -37,7 +37,7 @@
#include <errno.h>
#include "../perf.h"
-#include "util.h"
+#include <lk/util.h>
#include "trace-event.h"
static int input_fd;
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index f7af2fc..cc4f4d5 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -26,7 +26,7 @@
#include <errno.h>
#include "../perf.h"
-#include "util.h"
+#include <lk/util.h>
#include "trace-event.h"
struct scripting_context *scripting_context;
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
deleted file mode 100644
index 7d6b833..0000000
--- a/tools/perf/util/types.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __PERF_TYPES_H
-#define __PERF_TYPES_H
-
-/*
- * We define u64 as unsigned long long for every architecture
- * so that we can print it with %Lx without getting warnings.
- */
-typedef unsigned long long u64;
-typedef signed long long s64;
-typedef unsigned int u32;
-typedef signed int s32;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned char u8;
-typedef signed char s8;
-
-#endif /* __PERF_TYPES_H */
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
deleted file mode 100644
index e16bf9a..0000000
--- a/tools/perf/util/usage.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-#include "util.h"
-
-static void report(const char *prefix, const char *err, va_list params)
-{
- char msg[1024];
- vsnprintf(msg, sizeof(msg), err, params);
- fprintf(stderr, " %s%s\n", prefix, msg);
-}
-
-static NORETURN void usage_builtin(const char *err)
-{
- fprintf(stderr, "\n Usage: %s\n", err);
- exit(129);
-}
-
-static NORETURN void die_builtin(const char *err, va_list params)
-{
- report(" Fatal: ", err, params);
- exit(128);
-}
-
-static void error_builtin(const char *err, va_list params)
-{
- report(" Error: ", err, params);
-}
-
-static void warn_builtin(const char *warn, va_list params)
-{
- report(" Warning: ", warn, params);
-}
-
-/* If we are in a dlopen()ed .so write to a global variable would segfault
- * (ugh), so keep things static. */
-static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
-static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
-static void (*error_routine)(const char *err, va_list params) = error_builtin;
-static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
-
-void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
-{
- die_routine = routine;
-}
-
-void usage(const char *err)
-{
- usage_routine(err);
-}
-
-void die(const char *err, ...)
-{
- va_list params;
-
- va_start(params, err);
- die_routine(err, params);
- va_end(params);
-}
-
-int error(const char *err, ...)
-{
- va_list params;
-
- va_start(params, err);
- error_routine(err, params);
- va_end(params);
- return -1;
-}
-
-void warning(const char *warn, ...)
-{
- va_list params;
-
- va_start(params, warn);
- warn_routine(warn, params);
- va_end(params);
-}
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 2142656..4f35719 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -1,4 +1,4 @@
-#include "util.h"
+#include <lk/util.h>
#include <sys/mman.h>
int mkdir_p(char *path, mode_t mode)
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
deleted file mode 100644
index f380fed..0000000
--- a/tools/perf/util/util.h
+++ /dev/null
@@ -1,285 +0,0 @@
-#ifndef GIT_COMPAT_UTIL_H
-#define GIT_COMPAT_UTIL_H
-
-#define _FILE_OFFSET_BITS 64
-
-#ifndef FLEX_ARRAY
-/*
- * See if our compiler is known to support flexible array members.
- */
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-# define FLEX_ARRAY /* empty */
-#elif defined(__GNUC__)
-# if (__GNUC__ >= 3)
-# define FLEX_ARRAY /* empty */
-# else
-# define FLEX_ARRAY 0 /* older GNU extension */
-# endif
-#endif
-
-/*
- * Otherwise, default to safer but a bit wasteful traditional style
- */
-#ifndef FLEX_ARRAY
-# define FLEX_ARRAY 1
-#endif
-#endif
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-
-#ifdef __GNUC__
-#define TYPEOF(x) (__typeof__(x))
-#else
-#define TYPEOF(x)
-#endif
-
-#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
-#define HAS_MULTI_BITS(i) ((i) & ((i) - 1)) /* checks if an integer has more than 1 bit set */
-
-/* Approximation of the length of the decimal representation of this type. */
-#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
-
-#define _ALL_SOURCE 1
-#define _GNU_SOURCE 1
-#define _BSD_SOURCE 1
-#define HAS_BOOL
-
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <time.h>
-#include <signal.h>
-#include <fnmatch.h>
-#include <assert.h>
-#include <regex.h>
-#include <utime.h>
-#include <sys/wait.h>
-#include <sys/poll.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#ifndef NO_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#include <netinet/in.h>
-#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"
-#include <sys/ttydefaults.h>
-
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-
-extern const char *graph_line;
-extern const char *graph_dotted_line;
-extern char buildid_dir[];
-
-/* On most systems <limits.h> would have given us this, but
- * not on some systems (e.g. GNU/Hurd).
- */
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifndef PRIuMAX
-#define PRIuMAX "llu"
-#endif
-
-#ifndef PRIu32
-#define PRIu32 "u"
-#endif
-
-#ifndef PRIx32
-#define PRIx32 "x"
-#endif
-
-#ifndef PATH_SEP
-#define PATH_SEP ':'
-#endif
-
-#ifndef STRIP_EXTENSION
-#define STRIP_EXTENSION ""
-#endif
-
-#ifndef has_dos_drive_prefix
-#define has_dos_drive_prefix(path) 0
-#endif
-
-#ifndef is_dir_sep
-#define is_dir_sep(c) ((c) == '/')
-#endif
-
-#ifdef __GNUC__
-#define NORETURN __attribute__((__noreturn__))
-#else
-#define NORETURN
-#ifndef __attribute__
-#define __attribute__(x)
-#endif
-#endif
-
-/* General helper functions */
-extern void usage(const char *err) NORETURN;
-extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
-extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
-
-#include "../../../include/linux/stringify.h"
-
-#define DIE_IF(cnd) \
- do { if (cnd) \
- die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \
- __stringify(cnd) "\n"); \
- } while (0)
-
-
-extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
-
-extern int prefixcmp(const char *str, const char *prefix);
-extern void set_buildid_dir(void);
-extern void disable_buildid_cache(void);
-
-static inline const char *skip_prefix(const char *str, const char *prefix)
-{
- size_t len = strlen(prefix);
- return strncmp(str, prefix, len) ? NULL : str + len;
-}
-
-#ifdef __GLIBC_PREREQ
-#if __GLIBC_PREREQ(2, 1)
-#define HAVE_STRCHRNUL
-#endif
-#endif
-
-#ifndef HAVE_STRCHRNUL
-#define strchrnul gitstrchrnul
-static inline char *gitstrchrnul(const char *s, int c)
-{
- while (*s && *s != c)
- s++;
- return (char *)s;
-}
-#endif
-
-/*
- * Wrappers:
- */
-extern char *xstrdup(const char *str);
-extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
-
-
-static inline void *zalloc(size_t size)
-{
- return calloc(1, size);
-}
-
-static inline int has_extension(const char *filename, const char *ext)
-{
- size_t len = strlen(filename);
- size_t extlen = strlen(ext);
-
- return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
-/* Sane ctype - no locale, and works with signed chars */
-#undef isascii
-#undef isspace
-#undef isdigit
-#undef isxdigit
-#undef isalpha
-#undef isprint
-#undef isalnum
-#undef tolower
-#undef toupper
-
-extern unsigned char sane_ctype[256];
-#define GIT_SPACE 0x01
-#define GIT_DIGIT 0x02
-#define GIT_ALPHA 0x04
-#define GIT_GLOB_SPECIAL 0x08
-#define GIT_REGEX_SPECIAL 0x10
-#define GIT_PRINT_EXTRA 0x20
-#define GIT_PRINT 0x3E
-#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
-#define isascii(x) (((x) & ~0x7f) == 0)
-#define isspace(x) sane_istest(x,GIT_SPACE)
-#define isdigit(x) sane_istest(x,GIT_DIGIT)
-#define isxdigit(x) \
- (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
-#define isalpha(x) sane_istest(x,GIT_ALPHA)
-#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
-#define isprint(x) sane_istest(x,GIT_PRINT)
-#define tolower(x) sane_case((unsigned char)(x), 0x20)
-#define toupper(x) sane_case((unsigned char)(x), 0)
-
-static inline int sane_case(int x, int high)
-{
- if (sane_istest(x, GIT_ALPHA))
- x = (x & ~0x20) | high;
- return x;
-}
-
-#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
-# define FORCE_DIR_SET_GID S_ISGID
-#else
-# define FORCE_DIR_SET_GID 0
-#endif
-
-#ifdef NO_NSEC
-#undef USE_NSEC
-#define ST_CTIME_NSEC(st) 0
-#define ST_MTIME_NSEC(st) 0
-#else
-#ifdef USE_ST_TIMESPEC
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
-#else
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
-#endif
-#endif
-
-int mkdir_p(char *path, mode_t mode);
-int copyfile(const char *from, const char *to);
-
-s64 perf_atoll(const char *str);
-char **argv_split(const char *str, int *argcp);
-void argv_free(char **argv);
-bool strglobmatch(const char *str, const char *pat);
-bool strlazymatch(const char *str, const char *pat);
-unsigned long convert_unit(unsigned long value, char *unit);
-
-#ifndef ESC
-#define ESC 27
-#endif
-
-static inline bool is_exit_key(int key)
-{
- char up;
- if (key == CTRL('c') || key == ESC)
- return true;
- up = toupper(key);
- return up == 'Q';
-}
-
-#define _STR(x) #x
-#define STR(x) _STR(x)
-
-#endif
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c
index cfa55d6..648fc48 100644
--- a/tools/perf/util/values.c
+++ b/tools/perf/util/values.c
@@ -1,6 +1,6 @@
#include <stdlib.h>
-#include "util.h"
+#include <lk/util.h>
#include "values.h"
void perf_read_values_init(struct perf_read_values *values)
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h
index 2fa967e..f762cb7 100644
--- a/tools/perf/util/values.h
+++ b/tools/perf/util/values.h
@@ -1,7 +1,7 @@
#ifndef __PERF_VALUES_H
#define __PERF_VALUES_H
-#include "types.h"
+#include <lk/types.h>
struct perf_read_values {
int threads;
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 08/20] perf: Export cpumap
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (6 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 07/20] perf: Export debugfs utilities Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 09/20] perf: Carve out mmap helpers for general use Borislav Petkov
` (12 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Export cpu counting and cpumap manipulating utils for general use. This
pulls ctype.c along.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 3 +
tools/lib/lk/cpumap.c | 114 +++++++++++++++++++++++++++++++++++++++++++
tools/lib/lk/cpumap.h | 7 +++
tools/lib/lk/ctype.c | 39 +++++++++++++++
tools/perf/Makefile | 3 -
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-stat.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/util/cpumap.c | 114 -------------------------------------------
tools/perf/util/cpumap.h | 7 ---
tools/perf/util/ctype.c | 39 ---------------
11 files changed, 166 insertions(+), 166 deletions(-)
create mode 100644 tools/lib/lk/cpumap.c
create mode 100644 tools/lib/lk/cpumap.h
create mode 100644 tools/lib/lk/ctype.c
delete mode 100644 tools/perf/util/cpumap.c
delete mode 100644 tools/perf/util/cpumap.h
delete mode 100644 tools/perf/util/ctype.c
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index 823bbb5..ff94b2e 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -7,9 +7,12 @@ LIB_OBJS=
LIB_H += debugfs.h
LIB_H += util.h
LIB_H += types.h
+LIB_H += cpumap.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
+LIB_OBJS += cpumap.o
+LIB_OBJS += ctype.o
LIBFILE = $(LIB_OUTPUT)lklib.a
diff --git a/tools/lib/lk/cpumap.c b/tools/lib/lk/cpumap.c
new file mode 100644
index 0000000..7c3008a
--- /dev/null
+++ b/tools/lib/lk/cpumap.c
@@ -0,0 +1,114 @@
+#include <lk/util.h>
+#include <perf.h>
+#include "cpumap.h"
+#include <assert.h>
+#include <stdio.h>
+
+int cpumap[MAX_NR_CPUS];
+
+static int default_cpu_map(void)
+{
+ int nr_cpus, i;
+
+ nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ assert(nr_cpus <= MAX_NR_CPUS);
+ assert((int)nr_cpus >= 0);
+
+ for (i = 0; i < nr_cpus; ++i)
+ cpumap[i] = i;
+
+ return nr_cpus;
+}
+
+static int read_all_cpu_map(void)
+{
+ FILE *onlnf;
+ int nr_cpus = 0;
+ int n, cpu, prev;
+ char sep;
+
+ onlnf = fopen("/sys/devices/system/cpu/online", "r");
+ if (!onlnf)
+ return default_cpu_map();
+
+ sep = 0;
+ prev = -1;
+ for (;;) {
+ n = fscanf(onlnf, "%u%c", &cpu, &sep);
+ if (n <= 0)
+ break;
+ if (prev >= 0) {
+ assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS);
+ while (++prev < cpu)
+ cpumap[nr_cpus++] = prev;
+ }
+ assert (nr_cpus < MAX_NR_CPUS);
+ cpumap[nr_cpus++] = cpu;
+ if (n == 2 && sep == '-')
+ prev = cpu;
+ else
+ prev = -1;
+ if (n == 1 || sep == '\n')
+ break;
+ }
+ fclose(onlnf);
+ if (nr_cpus > 0)
+ return nr_cpus;
+
+ return default_cpu_map();
+}
+
+int read_cpu_map(const char *cpu_list)
+{
+ unsigned long start_cpu, end_cpu = 0;
+ char *p = NULL;
+ int i, nr_cpus = 0;
+
+ if (!cpu_list)
+ return read_all_cpu_map();
+
+ if (!isdigit(*cpu_list))
+ goto invalid;
+
+ while (isdigit(*cpu_list)) {
+ p = NULL;
+ start_cpu = strtoul(cpu_list, &p, 0);
+ if (start_cpu >= INT_MAX
+ || (*p != '\0' && *p != ',' && *p != '-'))
+ goto invalid;
+
+ if (*p == '-') {
+ cpu_list = ++p;
+ p = NULL;
+ end_cpu = strtoul(cpu_list, &p, 0);
+
+ if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
+ goto invalid;
+
+ if (end_cpu < start_cpu)
+ goto invalid;
+ } else {
+ end_cpu = start_cpu;
+ }
+
+ for (; start_cpu <= end_cpu; start_cpu++) {
+ /* check for duplicates */
+ for (i = 0; i < nr_cpus; i++)
+ if (cpumap[i] == (int)start_cpu)
+ goto invalid;
+
+ assert(nr_cpus < MAX_NR_CPUS);
+ cpumap[nr_cpus++] = (int)start_cpu;
+ }
+ if (*p)
+ ++p;
+
+ cpu_list = p;
+ }
+ if (nr_cpus > 0)
+ return nr_cpus;
+
+ return default_cpu_map();
+invalid:
+ return -1;
+}
diff --git a/tools/lib/lk/cpumap.h b/tools/lib/lk/cpumap.h
new file mode 100644
index 0000000..1d7bd56
--- /dev/null
+++ b/tools/lib/lk/cpumap.h
@@ -0,0 +1,7 @@
+#ifndef __LK_CPUMAP_H
+#define __LK_CPUMAP_H
+
+extern int read_cpu_map(const char *cpu_list);
+extern int cpumap[];
+
+#endif /* __LK_CPUMAP_H */
diff --git a/tools/lib/lk/ctype.c b/tools/lib/lk/ctype.c
new file mode 100644
index 0000000..aada3ac
--- /dev/null
+++ b/tools/lib/lk/ctype.c
@@ -0,0 +1,39 @@
+/*
+ * Sane locale-independent, ASCII ctype.
+ *
+ * No surprises, and works with signed and unsigned chars.
+ */
+#include "util.h"
+
+enum {
+ S = GIT_SPACE,
+ A = GIT_ALPHA,
+ D = GIT_DIGIT,
+ G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
+ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
+ P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */
+
+ PS = GIT_SPACE | GIT_PRINT_EXTRA,
+};
+
+unsigned char sane_ctype[256] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
+ PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
+ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
+ A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
+ A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */
+ /* Nothing in the 128.. range */
+};
+
+const char *graph_line =
+ "_____________________________________________________________________"
+ "_____________________________________________________________________";
+const char *graph_dotted_line =
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------";
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index d8ef5a5..49690b8 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -385,13 +385,11 @@ LIB_H += util/trace-event.h
LIB_H += util/probe-finder.h
LIB_H += util/probe-event.h
LIB_H += util/pstack.h
-LIB_H += util/cpumap.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
-LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
@@ -430,7 +428,6 @@ LIB_OBJS += $(OUTPUT)util/sort.o
LIB_OBJS += $(OUTPUT)util/hist.o
LIB_OBJS += $(OUTPUT)util/probe-event.o
LIB_OBJS += $(OUTPUT)util/util.o
-LIB_OBJS += $(OUTPUT)util/cpumap.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 936e231..1fa7a06 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -21,7 +21,7 @@
#include "util/debug.h"
#include "util/session.h"
#include "util/symbol.h"
-#include "util/cpumap.h"
+#include <lk/cpumap.h>
#include <unistd.h>
#include <sched.h>
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 12dabd3..1f60239 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -45,7 +45,7 @@
#include "util/event.h"
#include "util/debug.h"
#include "util/header.h"
-#include "util/cpumap.h"
+#include <lk/cpumap.h>
#include "util/thread.h"
#include <sys/prctl.h>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 7ba3759..b5bc157 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -28,7 +28,7 @@
#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
-#include "util/cpumap.h"
+#include <lk/cpumap.h>
#include "util/debug.h"
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
deleted file mode 100644
index 8ef8cef..0000000
--- a/tools/perf/util/cpumap.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include <lk/util.h>
-#include "../perf.h"
-#include "cpumap.h"
-#include <assert.h>
-#include <stdio.h>
-
-int cpumap[MAX_NR_CPUS];
-
-static int default_cpu_map(void)
-{
- int nr_cpus, i;
-
- nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- assert(nr_cpus <= MAX_NR_CPUS);
- assert((int)nr_cpus >= 0);
-
- for (i = 0; i < nr_cpus; ++i)
- cpumap[i] = i;
-
- return nr_cpus;
-}
-
-static int read_all_cpu_map(void)
-{
- FILE *onlnf;
- int nr_cpus = 0;
- int n, cpu, prev;
- char sep;
-
- onlnf = fopen("/sys/devices/system/cpu/online", "r");
- if (!onlnf)
- return default_cpu_map();
-
- sep = 0;
- prev = -1;
- for (;;) {
- n = fscanf(onlnf, "%u%c", &cpu, &sep);
- if (n <= 0)
- break;
- if (prev >= 0) {
- assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS);
- while (++prev < cpu)
- cpumap[nr_cpus++] = prev;
- }
- assert (nr_cpus < MAX_NR_CPUS);
- cpumap[nr_cpus++] = cpu;
- if (n == 2 && sep == '-')
- prev = cpu;
- else
- prev = -1;
- if (n == 1 || sep == '\n')
- break;
- }
- fclose(onlnf);
- if (nr_cpus > 0)
- return nr_cpus;
-
- return default_cpu_map();
-}
-
-int read_cpu_map(const char *cpu_list)
-{
- unsigned long start_cpu, end_cpu = 0;
- char *p = NULL;
- int i, nr_cpus = 0;
-
- if (!cpu_list)
- return read_all_cpu_map();
-
- if (!isdigit(*cpu_list))
- goto invalid;
-
- while (isdigit(*cpu_list)) {
- p = NULL;
- start_cpu = strtoul(cpu_list, &p, 0);
- if (start_cpu >= INT_MAX
- || (*p != '\0' && *p != ',' && *p != '-'))
- goto invalid;
-
- if (*p == '-') {
- cpu_list = ++p;
- p = NULL;
- end_cpu = strtoul(cpu_list, &p, 0);
-
- if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
- goto invalid;
-
- if (end_cpu < start_cpu)
- goto invalid;
- } else {
- end_cpu = start_cpu;
- }
-
- for (; start_cpu <= end_cpu; start_cpu++) {
- /* check for duplicates */
- for (i = 0; i < nr_cpus; i++)
- if (cpumap[i] == (int)start_cpu)
- goto invalid;
-
- assert(nr_cpus < MAX_NR_CPUS);
- cpumap[nr_cpus++] = (int)start_cpu;
- }
- if (*p)
- ++p;
-
- cpu_list = p;
- }
- if (nr_cpus > 0)
- return nr_cpus;
-
- return default_cpu_map();
-invalid:
- return -1;
-}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
deleted file mode 100644
index 3e60f56..0000000
--- a/tools/perf/util/cpumap.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __PERF_CPUMAP_H
-#define __PERF_CPUMAP_H
-
-extern int read_cpu_map(const char *cpu_list);
-extern int cpumap[];
-
-#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c
deleted file mode 100644
index 3507362..0000000
--- a/tools/perf/util/ctype.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Sane locale-independent, ASCII ctype.
- *
- * No surprises, and works with signed and unsigned chars.
- */
-#include "cache.h"
-
-enum {
- S = GIT_SPACE,
- A = GIT_ALPHA,
- D = GIT_DIGIT,
- G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
- R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
- P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */
-
- PS = GIT_SPACE | GIT_PRINT_EXTRA,
-};
-
-unsigned char sane_ctype[256] = {
-/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
- PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
- D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
- P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
- A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */
- P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
- A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */
- /* Nothing in the 128.. range */
-};
-
-const char *graph_line =
- "_____________________________________________________________________"
- "_____________________________________________________________________";
-const char *graph_dotted_line =
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------";
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 09/20] perf: Carve out mmap helpers for general use
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (7 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 08/20] perf: Export cpumap Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 10/20] perf: Export util.ch into library Borislav Petkov
` (11 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Export the mmap_read* helpers into tools/lib/perf/mmap.[ch]
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 6 ++-
tools/lib/perf/Makefile | 35 ++++++++++++++
tools/lib/perf/mmap.c | 95 ++++++++++++++++++++++++++++++++++++++
tools/lib/perf/mmap.h | 15 ++++++
tools/perf/Makefile | 2 +-
tools/perf/builtin-record.c | 107 ++++---------------------------------------
tools/perf/builtin-top.c | 28 ++---------
7 files changed, 166 insertions(+), 122 deletions(-)
create mode 100644 tools/lib/perf/Makefile
create mode 100644 tools/lib/perf/mmap.c
create mode 100644 tools/lib/perf/mmap.h
diff --git a/tools/Makefile b/tools/Makefile
index d3b1447..691f78b 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -34,7 +34,7 @@ export BASIC_CFLAGS
PERF_TOP_DIR := $(CURDIR)
export PERF_TOP_DIR
-perf: libparsevent lklib .FORCE
+perf: libparsevent lklib lkperflib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
libparsevent: .FORCE
@@ -43,9 +43,13 @@ libparsevent: .FORCE
lklib: .FORCE
$(QUIET_SUBDIR0)lib/lk/ $(QUIET_SUBDIR1)
+lkperflib: .FORCE
+ $(QUIET_SUBDIR0)lib/perf/ $(QUIET_SUBDIR1)
+
clean:
$(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)lib/lk/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)lib/perf/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
.PHONY: clean .FORCE
diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
new file mode 100644
index 0000000..9942d52
--- /dev/null
+++ b/tools/lib/perf/Makefile
@@ -0,0 +1,35 @@
+include ../../scripts/Makefile.lib
+
+# guard against environment variables
+LIB_H=
+LIB_OBJS=
+
+LIB_H += mmap.h
+
+LIB_OBJS += mmap.o
+
+LIBFILE = $(LIB_OUTPUT)lkperflib.a
+
+CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+EXTLIBS = -lpthread -lrt -lelf -lm
+ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+ALL_LDFLAGS = $(LDFLAGS)
+
+RM = rm -f
+
+$(LIBFILE): $(LIB_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+
+$(LIB_OBJS): $(LIB_H)
+
+%.o: %.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+%.s: %.c
+ $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+%.o: %.S
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
+clean:
+ $(RM) $(LIB_OBJS) $(LIBFILE)
+
+.PHONY: clean
diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c
new file mode 100644
index 0000000..b154ccc
--- /dev/null
+++ b/tools/lib/perf/mmap.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <perf.h>
+#include "mmap.h"
+
+unsigned long mmap_read_head(struct mmap_data *md)
+{
+ struct perf_event_mmap_page *pc = md->base;
+ long head;
+
+ head = pc->data_head;
+ rmb();
+
+ return head;
+}
+
+static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
+{
+ struct perf_event_mmap_page *pc = md->base;
+
+ /*
+ * ensure all reads are done before we write the tail out.
+ */
+ /* mb(); */
+ pc->data_tail = tail;
+}
+
+static unsigned long mmap_read(struct mmap_data *md,
+ void (*write_output)(void *, size_t))
+{
+ unsigned int head = mmap_read_head(md);
+ unsigned int old = md->prev;
+ unsigned int page_size;
+ unsigned char *data;
+ unsigned long size, samples = 0;
+ void *buf;
+ int diff;
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+ data = md->base + page_size;
+
+ /*
+ * If we're further behind than half the buffer, there's a chance
+ * the writer will bite our tail and mess up the samples under us.
+ *
+ * If we somehow ended up ahead of the head, we got messed up.
+ *
+ * In either case, truncate and restart at head.
+ */
+ diff = head - old;
+ if (diff < 0) {
+ fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
+ /*
+ * head points to a known good entry, start there.
+ */
+ old = head;
+ }
+
+ if (old != head)
+ samples++;
+
+ size = head - old;
+
+ if ((old & md->mask) + size != (head & md->mask)) {
+ buf = &data[old & md->mask];
+ size = md->mask + 1 - (old & md->mask);
+ old += size;
+
+ write_output(buf, size);
+ }
+
+ buf = &data[old & md->mask];
+ size = head - old;
+ old += size;
+
+ write_output(buf, size);
+
+ md->prev = old;
+ mmap_write_tail(md, old);
+
+ return samples;
+}
+
+unsigned long mmap_read_all(struct mmap_data *mmap_array, int nr_cpus,
+ void (*write_output)(void *, size_t))
+{
+ int i;
+ unsigned long samples = 0;
+
+ for (i = 0; i < nr_cpus; i++) {
+ if (mmap_array[i].base)
+ samples += mmap_read(&mmap_array[i], write_output);
+ }
+
+ return samples;
+}
diff --git a/tools/lib/perf/mmap.h b/tools/lib/perf/mmap.h
new file mode 100644
index 0000000..b6622e5
--- /dev/null
+++ b/tools/lib/perf/mmap.h
@@ -0,0 +1,15 @@
+#ifndef __PERF_MMAP_H
+#define __PERF_MMAP_H
+
+struct mmap_data {
+ int counter;
+ void *base;
+ unsigned int mask;
+ unsigned int prev;
+};
+
+unsigned long mmap_read_head(struct mmap_data *md);
+unsigned long mmap_read_all(struct mmap_data *, int,
+ void (*write_output)(void *, size_t));
+
+#endif /* __PERF_MMAP_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 49690b8..e8c1ada 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -328,7 +328,7 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a
-EXTRA_LIB_FILES=$(LIB_OUTPUT)libparsevent.a $(LIB_OUTPUT)lklib.a
+EXTRA_LIB_FILES=$(LIB_OUTPUT)libparsevent.a $(LIB_OUTPUT)lklib.a $(LIB_OUTPUT)lkperflib.a
LIB_H += ../../include/linux/perf_event.h
LIB_H += ../../include/linux/rbtree.h
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 1fa7a06..99a71c5 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -6,22 +6,21 @@
* later analysis via perf report.
*/
#define _FILE_OFFSET_BITS 64
-
#include "builtin.h"
-
#include "perf.h"
#include "util/build-id.h"
-#include <lk/util.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
-
#include "util/header.h"
#include "util/event.h"
#include "util/debug.h"
#include "util/session.h"
#include "util/symbol.h"
+
#include <lk/cpumap.h>
+#include <lk/util.h>
+#include <perf/mmap.h>
#include <unistd.h>
#include <sched.h>
@@ -62,7 +61,7 @@ static bool no_samples = false;
static bool sample_address = false;
static bool no_buildid = false;
-static long samples = 0;
+static unsigned long samples = 0;
static u64 bytes_written = 0;
static struct pollfd *event_array;
@@ -76,37 +75,8 @@ static off_t post_processing_offset;
static struct perf_session *session;
static const char *cpu_list;
-struct mmap_data {
- int counter;
- void *base;
- unsigned int mask;
- unsigned int prev;
-};
-
static struct mmap_data mmap_array[MAX_NR_CPUS];
-static unsigned long mmap_read_head(struct mmap_data *md)
-{
- struct perf_event_mmap_page *pc = md->base;
- long head;
-
- head = pc->data_head;
- rmb();
-
- return head;
-}
-
-static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
-{
- struct perf_event_mmap_page *pc = md->base;
-
- /*
- * ensure all reads are done before we write the tail out.
- */
- /* mb(); */
- pc->data_tail = tail;
-}
-
static void advance_output(size_t size)
{
bytes_written += size;
@@ -134,55 +104,6 @@ static int process_synthesized_event(event_t *event,
return 0;
}
-static void mmap_read(struct mmap_data *md)
-{
- unsigned int head = mmap_read_head(md);
- unsigned int old = md->prev;
- unsigned char *data = md->base + page_size;
- unsigned long size;
- void *buf;
- int diff;
-
- /*
- * If we're further behind than half the buffer, there's a chance
- * the writer will bite our tail and mess up the samples under us.
- *
- * If we somehow ended up ahead of the head, we got messed up.
- *
- * In either case, truncate and restart at head.
- */
- diff = head - old;
- if (diff < 0) {
- fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
- /*
- * head points to a known good entry, start there.
- */
- old = head;
- }
-
- if (old != head)
- samples++;
-
- size = head - old;
-
- if ((old & md->mask) + size != (head & md->mask)) {
- buf = &data[old & md->mask];
- size = md->mask + 1 - (old & md->mask);
- old += size;
-
- write_output(buf, size);
- }
-
- buf = &data[old & md->mask];
- size = head - old;
- old += size;
-
- write_output(buf, size);
-
- md->prev = old;
- mmap_write_tail(md, old);
-}
-
static volatile int done = 0;
static volatile int signr = -1;
@@ -485,19 +406,6 @@ static struct perf_event_header finished_round_event = {
.type = PERF_RECORD_FINISHED_ROUND,
};
-static void mmap_read_all(void)
-{
- int i;
-
- for (i = 0; i < nr_cpu; i++) {
- if (mmap_array[i].base)
- mmap_read(&mmap_array[i]);
- }
-
- if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
- write_output(&finished_round_event, sizeof(finished_round_event));
-}
-
static int __cmd_record(int argc, const char **argv)
{
int i, counter;
@@ -734,10 +642,13 @@ static int __cmd_record(int argc, const char **argv)
close(go_pipe[1]);
for (;;) {
- int hits = samples;
+ unsigned long hits = samples;
int thread;
- mmap_read_all();
+ samples = mmap_read_all(mmap_array, nr_cpu, write_output);
+
+ if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
+ write_output(&finished_round_event, sizeof(finished_round_event));
if (hits == samples) {
if (done)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index b5bc157..773676f 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -24,11 +24,8 @@
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include <lk/util.h>
-#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
-#include <lk/cpumap.h>
#include "util/debug.h"
@@ -52,6 +49,11 @@
#include <sys/uio.h>
#include <sys/mman.h>
+#include <lk/util.h>
+#include <lk/cpumap.h>
+#include <perf/mmap.h>
+
+#include <linux/rbtree.h>
#include <linux/unistd.h>
#include <linux/types.h>
@@ -1082,24 +1084,6 @@ static void event__process_sample(const event_t *self,
}
}
-struct mmap_data {
- int counter;
- void *base;
- int mask;
- unsigned int prev;
-};
-
-static unsigned int mmap_read_head(struct mmap_data *md)
-{
- struct perf_event_mmap_page *pc = md->base;
- int head;
-
- head = pc->data_head;
- rmb();
-
- return head;
-}
-
static void perf_session__mmap_read_counter(struct perf_session *self,
struct mmap_data *md)
{
@@ -1117,7 +1101,7 @@ static void perf_session__mmap_read_counter(struct perf_session *self,
* In either case, truncate and restart at head.
*/
diff = head - old;
- if (diff > md->mask / 2 || diff < 0) {
+ if ((unsigned)diff > md->mask / 2 || diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
/*
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 10/20] perf: Export util.ch into library
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (8 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 09/20] perf: Carve out mmap helpers for general use Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 11/20] perf: Move rbtree to library Borislav Petkov
` (10 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
This is needed for sharing common functionality.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 1 +
tools/lib/lk/cpumap.c | 1 +
tools/lib/lk/util.c | 116 ++++++++++++++++++++++++++++++++++++++++
tools/lib/perf/Makefile | 1 +
tools/lib/perf/util.h | 8 +++
tools/perf/Makefile | 1 -
tools/perf/perf.h | 3 -
tools/perf/util/parse-events.h | 1 +
tools/perf/util/util.c | 116 ----------------------------------------
9 files changed, 128 insertions(+), 120 deletions(-)
create mode 100644 tools/lib/lk/util.c
create mode 100644 tools/lib/perf/util.h
delete mode 100644 tools/perf/util/util.c
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index ff94b2e..985214a 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -11,6 +11,7 @@ LIB_H += cpumap.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
+LIB_OBJS += util.o
LIB_OBJS += cpumap.o
LIB_OBJS += ctype.o
diff --git a/tools/lib/lk/cpumap.c b/tools/lib/lk/cpumap.c
index 7c3008a..0c6c12c 100644
--- a/tools/lib/lk/cpumap.c
+++ b/tools/lib/lk/cpumap.c
@@ -1,4 +1,5 @@
#include <lk/util.h>
+#include <perf/util.h>
#include <perf.h>
#include "cpumap.h"
#include <assert.h>
diff --git a/tools/lib/lk/util.c b/tools/lib/lk/util.c
new file mode 100644
index 0000000..4f35719
--- /dev/null
+++ b/tools/lib/lk/util.c
@@ -0,0 +1,116 @@
+#include <lk/util.h>
+#include <sys/mman.h>
+
+int mkdir_p(char *path, mode_t mode)
+{
+ struct stat st;
+ int err;
+ char *d = path;
+
+ if (*d != '/')
+ return -1;
+
+ if (stat(path, &st) == 0)
+ return 0;
+
+ while (*++d == '/');
+
+ while ((d = strchr(d, '/'))) {
+ *d = '\0';
+ err = stat(path, &st) && mkdir(path, mode);
+ *d++ = '/';
+ if (err)
+ return -1;
+ while (*d == '/')
+ ++d;
+ }
+ return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
+}
+
+static int slow_copyfile(const char *from, const char *to)
+{
+ int err = 0;
+ char *line = NULL;
+ size_t n;
+ FILE *from_fp = fopen(from, "r"), *to_fp;
+
+ if (from_fp == NULL)
+ goto out;
+
+ to_fp = fopen(to, "w");
+ if (to_fp == NULL)
+ goto out_fclose_from;
+
+ while (getline(&line, &n, from_fp) > 0)
+ if (fputs(line, to_fp) == EOF)
+ goto out_fclose_to;
+ err = 0;
+out_fclose_to:
+ fclose(to_fp);
+ free(line);
+out_fclose_from:
+ fclose(from_fp);
+out:
+ return err;
+}
+
+int copyfile(const char *from, const char *to)
+{
+ int fromfd, tofd;
+ struct stat st;
+ void *addr;
+ int err = -1;
+
+ if (stat(from, &st))
+ goto out;
+
+ if (st.st_size == 0) /* /proc? do it slowly... */
+ return slow_copyfile(from, to);
+
+ fromfd = open(from, O_RDONLY);
+ if (fromfd < 0)
+ goto out;
+
+ tofd = creat(to, 0755);
+ if (tofd < 0)
+ goto out_close_from;
+
+ addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
+ if (addr == MAP_FAILED)
+ goto out_close_to;
+
+ if (write(tofd, addr, st.st_size) == st.st_size)
+ err = 0;
+
+ munmap(addr, st.st_size);
+out_close_to:
+ close(tofd);
+ if (err)
+ unlink(to);
+out_close_from:
+ close(fromfd);
+out:
+ return err;
+}
+
+unsigned long convert_unit(unsigned long value, char *unit)
+{
+ *unit = ' ';
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'K';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'M';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'G';
+ }
+
+ return value;
+}
diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
index 9942d52..5815664 100644
--- a/tools/lib/perf/Makefile
+++ b/tools/lib/perf/Makefile
@@ -5,6 +5,7 @@ LIB_H=
LIB_OBJS=
LIB_H += mmap.h
+LIB_H += util.h
LIB_OBJS += mmap.o
diff --git a/tools/lib/perf/util.h b/tools/lib/perf/util.h
new file mode 100644
index 0000000..4b774b9
--- /dev/null
+++ b/tools/lib/perf/util.h
@@ -0,0 +1,8 @@
+#ifndef __PERF_UTIL_H
+#define __PERF_UTIL_H
+
+#define MAX_COUNTERS 256
+#define MAX_NR_CPUS 256
+
+#endif /* __PERF_UTIL_H */
+
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index e8c1ada..6d06417 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -427,7 +427,6 @@ LIB_OBJS += $(OUTPUT)util/svghelper.o
LIB_OBJS += $(OUTPUT)util/sort.o
LIB_OBJS += $(OUTPUT)util/hist.o
LIB_OBJS += $(OUTPUT)util/probe-event.o
-LIB_OBJS += $(OUTPUT)util/util.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 4bfd513..785a200 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -136,9 +136,6 @@ sys_perf_event_open(struct perf_event_attr *attr,
group_fd, flags);
}
-#define MAX_COUNTERS 256
-#define MAX_NR_CPUS 256
-
struct ip_callchain {
u64 nr;
u64 ips[0];
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index fc4ab3f..9492e3c 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -3,6 +3,7 @@
/*
* Parse symbolic events/counts passed in as options:
*/
+#include <perf/util.h>
struct option;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
deleted file mode 100644
index 4f35719..0000000
--- a/tools/perf/util/util.c
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <lk/util.h>
-#include <sys/mman.h>
-
-int mkdir_p(char *path, mode_t mode)
-{
- struct stat st;
- int err;
- char *d = path;
-
- if (*d != '/')
- return -1;
-
- if (stat(path, &st) == 0)
- return 0;
-
- while (*++d == '/');
-
- while ((d = strchr(d, '/'))) {
- *d = '\0';
- err = stat(path, &st) && mkdir(path, mode);
- *d++ = '/';
- if (err)
- return -1;
- while (*d == '/')
- ++d;
- }
- return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
-}
-
-static int slow_copyfile(const char *from, const char *to)
-{
- int err = 0;
- char *line = NULL;
- size_t n;
- FILE *from_fp = fopen(from, "r"), *to_fp;
-
- if (from_fp == NULL)
- goto out;
-
- to_fp = fopen(to, "w");
- if (to_fp == NULL)
- goto out_fclose_from;
-
- while (getline(&line, &n, from_fp) > 0)
- if (fputs(line, to_fp) == EOF)
- goto out_fclose_to;
- err = 0;
-out_fclose_to:
- fclose(to_fp);
- free(line);
-out_fclose_from:
- fclose(from_fp);
-out:
- return err;
-}
-
-int copyfile(const char *from, const char *to)
-{
- int fromfd, tofd;
- struct stat st;
- void *addr;
- int err = -1;
-
- if (stat(from, &st))
- goto out;
-
- if (st.st_size == 0) /* /proc? do it slowly... */
- return slow_copyfile(from, to);
-
- fromfd = open(from, O_RDONLY);
- if (fromfd < 0)
- goto out;
-
- tofd = creat(to, 0755);
- if (tofd < 0)
- goto out_close_from;
-
- addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
- if (addr == MAP_FAILED)
- goto out_close_to;
-
- if (write(tofd, addr, st.st_size) == st.st_size)
- err = 0;
-
- munmap(addr, st.st_size);
-out_close_to:
- close(tofd);
- if (err)
- unlink(to);
-out_close_from:
- close(fromfd);
-out:
- return err;
-}
-
-unsigned long convert_unit(unsigned long value, char *unit)
-{
- *unit = ' ';
-
- if (value > 1000) {
- value /= 1000;
- *unit = 'K';
- }
-
- if (value > 1000) {
- value /= 1000;
- *unit = 'M';
- }
-
- if (value > 1000) {
- value /= 1000;
- *unit = 'G';
- }
-
- return value;
-}
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 11/20] perf: Move rbtree to library
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (9 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 10/20] perf: Export util.ch into library Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 12/20] perf: Export generic kernel utils " Borislav Petkov
` (9 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
This is generic code anyway and will be needed by other utils so move it
to a generic library.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 5 +++++
tools/lib/lk/rbtree.h | 1 +
tools/perf/Makefile | 6 ------
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/util/callchain.h | 2 +-
tools/perf/util/include/linux/rbtree.h | 1 -
tools/perf/util/map.h | 2 +-
tools/perf/util/session.h | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/strlist.h | 2 +-
tools/perf/util/symbol.h | 2 +-
tools/perf/util/thread.h | 2 +-
tools/perf/util/ui/browser.c | 2 +-
tools/perf/util/ui/browsers/hists.c | 2 +-
18 files changed, 20 insertions(+), 21 deletions(-)
create mode 100644 tools/lib/lk/rbtree.h
delete mode 100644 tools/perf/util/include/linux/rbtree.h
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index 985214a..32cf118 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -8,12 +8,15 @@ LIB_H += debugfs.h
LIB_H += util.h
LIB_H += types.h
LIB_H += cpumap.h
+LIB_H += ../../../include/linux/rbtree.h
+LIB_H += rbtree.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
LIB_OBJS += util.o
LIB_OBJS += cpumap.o
LIB_OBJS += ctype.o
+LIB_OBJS += rbtree.o
LIBFILE = $(LIB_OUTPUT)lklib.a
@@ -29,6 +32,8 @@ $(LIBFILE): $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H)
+rbtree.o: ../../../lib/rbtree.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -I../../../include $<
%.o: %.c
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
%.s: %.c
diff --git a/tools/lib/lk/rbtree.h b/tools/lib/lk/rbtree.h
new file mode 100644
index 0000000..965901d
--- /dev/null
+++ b/tools/lib/lk/rbtree.h
@@ -0,0 +1 @@
+#include "../../../include/linux/rbtree.h"
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 6d06417..2c82537 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -331,7 +331,6 @@ LIB_FILE=$(OUTPUT)libperf.a
EXTRA_LIB_FILES=$(LIB_OUTPUT)libparsevent.a $(LIB_OUTPUT)lklib.a $(LIB_OUTPUT)lkperflib.a
LIB_H += ../../include/linux/perf_event.h
-LIB_H += ../../include/linux/rbtree.h
LIB_H += ../../include/linux/list.h
LIB_H += ../../include/linux/hash.h
LIB_H += ../../include/linux/stringify.h
@@ -344,7 +343,6 @@ LIB_H += util/include/linux/list.h
LIB_H += util/include/linux/module.h
LIB_H += util/include/linux/poison.h
LIB_H += util/include/linux/prefetch.h
-LIB_H += util/include/linux/rbtree.h
LIB_H += util/include/linux/string.h
LIB_H += util/include/linux/types.h
LIB_H += util/include/asm/asm-offsets.h
@@ -398,7 +396,6 @@ LIB_OBJS += $(OUTPUT)util/levenshtein.o
LIB_OBJS += $(OUTPUT)util/parse-options.o
LIB_OBJS += $(OUTPUT)util/parse-events.o
LIB_OBJS += $(OUTPUT)util/path.o
-LIB_OBJS += $(OUTPUT)util/rbtree.o
LIB_OBJS += $(OUTPUT)util/bitmap.o
LIB_OBJS += $(OUTPUT)util/hweight.o
LIB_OBJS += $(OUTPUT)util/run-command.o
@@ -958,9 +955,6 @@ $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/ui/browsers/map.o: util/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
- $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
-
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 19f2751..7f9983b 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -12,7 +12,7 @@
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "util/symbol.h"
#include "perf.h"
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 95901eb..f157a9c 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -13,7 +13,7 @@
#include "util/debug.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
struct alloc_stat;
typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f8564f9..76e6967 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -12,7 +12,7 @@
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "util/symbol.h"
#include "util/callchain.h"
#include "util/strlist.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index fe79fc6..aa9e4fc 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -19,7 +19,7 @@
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "util/symbol.h"
#include "util/callchain.h"
#include "util/strlist.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 773676f..3727a5c 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -53,7 +53,7 @@
#include <lk/cpumap.h>
#include <perf/mmap.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <linux/unistd.h>
#include <linux/types.h>
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 6de4313..e0d0dfb 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -3,7 +3,7 @@
#include "../perf.h"
#include <linux/list.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "event.h"
#include "symbol.h"
diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h
deleted file mode 100644
index 7a243a1..0000000
--- a/tools/perf/util/include/linux/rbtree.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../../include/linux/rbtree.h"
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 729469e..4219466 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -3,7 +3,7 @@
#include <linux/compiler.h>
#include <linux/list.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <stdio.h>
#include <stdbool.h>
#include <lk/types.h>
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 9fa0fc2..6f2af58 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -6,7 +6,7 @@
#include "header.h"
#include "symbol.h"
#include "thread.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "../../../include/linux/perf_event.h"
struct sample_queue;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 0c68709..4c015e9 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -7,7 +7,7 @@
#include "color.h"
#include <linux/list.h>
#include "cache.h"
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "symbol.h"
#include "string.h"
#include "callchain.h"
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 3ba8390..649eae3 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -1,7 +1,7 @@
#ifndef __PERF_STRLIST_H
#define __PERF_STRLIST_H
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <stdbool.h>
struct str_node {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index ea95c27..adc7211 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -6,7 +6,7 @@
#include <stdint.h>
#include "map.h"
#include <linux/list.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <stdio.h>
#ifdef HAVE_CPLUS_DEMANGLE
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 688500f..1a70f60 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -1,7 +1,7 @@
#ifndef __PERF_THREAD_H
#define __PERF_THREAD_H
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <unistd.h>
#include "symbol.h"
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
index 66f2d58..638a545 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/util/ui/browser.c
@@ -12,7 +12,7 @@
#endif
#include <slang.h>
#include <linux/list.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include <stdlib.h>
#include <sys/ttydefaults.h>
#include "browser.h"
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index 6866aa4..1d4ee98 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include <newt.h>
-#include <linux/rbtree.h>
+#include <lk/rbtree.h>
#include "../../hist.h"
#include "../../pstack.h"
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 12/20] perf: Export generic kernel utils to library
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (10 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 11/20] perf: Move rbtree to library Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 13/20] perf: Export compiler.h to the generic library Borislav Petkov
` (8 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Move all bitops-related stuff together.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 5 ++
tools/lib/lk/bitmap.c | 21 ++++++
tools/lib/lk/bitmap.h | 35 ++++++++++
tools/lib/lk/bitops.h | 30 +++++++++
tools/lib/lk/hweight.c | 31 +++++++++
tools/lib/lk/kernel.h | 113 ++++++++++++++++++++++++++++++++
tools/perf/Makefile | 6 --
tools/perf/util/bitmap.c | 21 ------
tools/perf/util/build-id.c | 2 +-
tools/perf/util/event.h | 1 +
| 2 +-
| 2 +-
tools/perf/util/hweight.c | 31 ---------
tools/perf/util/include/asm/hweight.h | 8 --
tools/perf/util/include/linux/bitmap.h | 35 ----------
tools/perf/util/include/linux/bitops.h | 27 --------
tools/perf/util/include/linux/kernel.h | 111 -------------------------------
tools/perf/util/map.h | 4 +-
tools/perf/util/parse-options.h | 2 +-
tools/perf/util/pstack.c | 2 +-
tools/perf/util/session.c | 2 +-
tools/perf/util/strlist.h | 1 +
tools/perf/util/trace-event-info.c | 2 +-
23 files changed, 247 insertions(+), 247 deletions(-)
create mode 100644 tools/lib/lk/bitmap.c
create mode 100644 tools/lib/lk/bitmap.h
create mode 100644 tools/lib/lk/bitops.h
create mode 100644 tools/lib/lk/hweight.c
create mode 100644 tools/lib/lk/kernel.h
delete mode 100644 tools/perf/util/bitmap.c
delete mode 100644 tools/perf/util/hweight.c
delete mode 100644 tools/perf/util/include/asm/hweight.h
delete mode 100644 tools/perf/util/include/linux/bitmap.h
delete mode 100644 tools/perf/util/include/linux/bitops.h
delete mode 100644 tools/perf/util/include/linux/kernel.h
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index 32cf118..f074216 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -10,6 +10,9 @@ LIB_H += types.h
LIB_H += cpumap.h
LIB_H += ../../../include/linux/rbtree.h
LIB_H += rbtree.h
+LIB_H += bitops.h
+LIB_H += bitmap.h
+LIB_H += kernel.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
@@ -17,6 +20,8 @@ LIB_OBJS += util.o
LIB_OBJS += cpumap.o
LIB_OBJS += ctype.o
LIB_OBJS += rbtree.o
+LIB_OBJS += hweight.o
+LIB_OBJS += bitmap.o
LIBFILE = $(LIB_OUTPUT)lklib.a
diff --git a/tools/lib/lk/bitmap.c b/tools/lib/lk/bitmap.c
new file mode 100644
index 0000000..502340d
--- /dev/null
+++ b/tools/lib/lk/bitmap.c
@@ -0,0 +1,21 @@
+/*
+ * From lib/bitmap.c
+ * Helper functions for bitmap.h.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include "bitmap.h"
+
+int __bitmap_weight(const unsigned long *bitmap, int bits)
+{
+ int k, w = 0, lim = bits/BITS_PER_LONG;
+
+ for (k = 0; k < lim; k++)
+ w += hweight_long(bitmap[k]);
+
+ if (bits % BITS_PER_LONG)
+ w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
+
+ return w;
+}
diff --git a/tools/lib/lk/bitmap.h b/tools/lib/lk/bitmap.h
new file mode 100644
index 0000000..9e9b619
--- /dev/null
+++ b/tools/lib/lk/bitmap.h
@@ -0,0 +1,35 @@
+#ifndef __LK_BITMAP_H
+#define __LK_BITMAP_H
+
+#include <string.h>
+#include <lk/bitops.h>
+
+int __bitmap_weight(const unsigned long *bitmap, int bits);
+
+#define BITMAP_LAST_WORD_MASK(nbits) \
+( \
+ ((nbits) % BITS_PER_LONG) ? \
+ (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
+)
+
+#define small_const_nbits(nbits) \
+ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
+static inline void bitmap_zero(unsigned long *dst, int nbits)
+{
+ if (small_const_nbits(nbits))
+ *dst = 0UL;
+ else {
+ int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memset(dst, 0, len);
+ }
+}
+
+static inline int bitmap_weight(const unsigned long *src, int nbits)
+{
+ if (small_const_nbits(nbits))
+ return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
+ return __bitmap_weight(src, nbits);
+}
+
+#endif /* __LK_BITMAP_H */
diff --git a/tools/lib/lk/bitops.h b/tools/lib/lk/bitops.h
new file mode 100644
index 0000000..fabefb6
--- /dev/null
+++ b/tools/lib/lk/bitops.h
@@ -0,0 +1,30 @@
+#ifndef __LK_BITOPS_H
+#define __LK_BITOPS_H
+
+#include "kernel.h"
+#include "types.h"
+
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+
+extern unsigned int hweight32(unsigned int w);
+extern unsigned long hweight64(u64 w);
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return ((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+static inline unsigned long hweight_long(unsigned long w)
+{
+ return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
+
+#endif
diff --git a/tools/lib/lk/hweight.c b/tools/lib/lk/hweight.c
new file mode 100644
index 0000000..12aba74
--- /dev/null
+++ b/tools/lib/lk/hweight.c
@@ -0,0 +1,31 @@
+#include "bitops.h"
+
+/**
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+unsigned long hweight64(u64 w)
+{
+#if BITS_PER_LONG == 32
+ return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
+#elif BITS_PER_LONG == 64
+ u64 res = w - ((w >> 1) & 0x5555555555555555ul);
+ res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
+ res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
+ res = res + (res >> 8);
+ res = res + (res >> 16);
+ return (res + (res >> 32)) & 0x00000000000000FFul;
+#endif
+}
diff --git a/tools/lib/lk/kernel.h b/tools/lib/lk/kernel.h
new file mode 100644
index 0000000..6c73c043
--- /dev/null
+++ b/tools/lib/lk/kernel.h
@@ -0,0 +1,113 @@
+#ifndef __LK_KERNEL_H
+#define __LK_KERNEL_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#ifndef BUILD_BUG_ON_ZERO
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#endif
+
+#ifndef max
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(cond) assert(!(cond))
+#endif
+
+/*
+ * Both need more care to handle endianness
+ * (Don't use bitmap_copy_le() for now)
+ */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+
+static inline int
+vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int i;
+ ssize_t ssize = size;
+
+ i = vsnprintf(buf, size, fmt, args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
+{
+ va_list args;
+ ssize_t ssize = size;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline unsigned long
+simple_strtoul(const char *nptr, char **endptr, int base)
+{
+ return strtoul(nptr, endptr, base);
+}
+
+int eprintf(int level,
+ const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#define pr_err(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug(fmt, ...) \
+ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n, fmt, ...) \
+ eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+
+#endif /* __LK_KERNEL_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 2c82537..6a4a4f5 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -334,11 +334,8 @@ LIB_H += ../../include/linux/perf_event.h
LIB_H += ../../include/linux/list.h
LIB_H += ../../include/linux/hash.h
LIB_H += ../../include/linux/stringify.h
-LIB_H += util/include/linux/bitmap.h
-LIB_H += util/include/linux/bitops.h
LIB_H += util/include/linux/compiler.h
LIB_H += util/include/linux/ctype.h
-LIB_H += util/include/linux/kernel.h
LIB_H += util/include/linux/list.h
LIB_H += util/include/linux/module.h
LIB_H += util/include/linux/poison.h
@@ -348,7 +345,6 @@ LIB_H += util/include/linux/types.h
LIB_H += util/include/asm/asm-offsets.h
LIB_H += util/include/asm/bug.h
LIB_H += util/include/asm/byteorder.h
-LIB_H += util/include/asm/hweight.h
LIB_H += util/include/asm/swab.h
LIB_H += util/include/asm/system.h
LIB_H += util/include/asm/uaccess.h
@@ -396,8 +392,6 @@ LIB_OBJS += $(OUTPUT)util/levenshtein.o
LIB_OBJS += $(OUTPUT)util/parse-options.o
LIB_OBJS += $(OUTPUT)util/parse-events.o
LIB_OBJS += $(OUTPUT)util/path.o
-LIB_OBJS += $(OUTPUT)util/bitmap.o
-LIB_OBJS += $(OUTPUT)util/hweight.o
LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c
deleted file mode 100644
index 5e230ac..0000000
--- a/tools/perf/util/bitmap.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * From lib/bitmap.c
- * Helper functions for bitmap.h.
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2. See the file COPYING for more details.
- */
-#include <linux/bitmap.h>
-
-int __bitmap_weight(const unsigned long *bitmap, int bits)
-{
- int k, w = 0, lim = bits/BITS_PER_LONG;
-
- for (k = 0; k < lim; k++)
- w += hweight_long(bitmap[k]);
-
- if (bits % BITS_PER_LONG)
- w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
-
- return w;
-}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index ce89873..ce4c7d3 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -11,7 +11,7 @@
#include "build-id.h"
#include "event.h"
#include "symbol.h"
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include "debug.h"
static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 8e790da..05587ce 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -2,6 +2,7 @@
#define __PERF_RECORD_H
#include <limits.h>
+#include <lk/kernel.h>
#include "../perf.h"
#include "map.h"
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 0ebd301..c64df23 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -6,7 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include <lk/util.h>
#include "header.h"
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index fb6f0eb..274a829 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -7,7 +7,7 @@
#include <lk/types.h>
#include "event.h"
-#include <linux/bitmap.h>
+#include <lk/bitmap.h>
struct perf_header_attr {
struct perf_event_attr attr;
diff --git a/tools/perf/util/hweight.c b/tools/perf/util/hweight.c
deleted file mode 100644
index 5c1d0d0..0000000
--- a/tools/perf/util/hweight.c
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <linux/bitops.h>
-
-/**
- * hweightN - returns the hamming weight of a N-bit word
- * @x: the word to weigh
- *
- * The Hamming Weight of a number is the total number of bits set in it.
- */
-
-unsigned int hweight32(unsigned int w)
-{
- unsigned int res = w - ((w >> 1) & 0x55555555);
- res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
- res = (res + (res >> 4)) & 0x0F0F0F0F;
- res = res + (res >> 8);
- return (res + (res >> 16)) & 0x000000FF;
-}
-
-unsigned long hweight64(__u64 w)
-{
-#if BITS_PER_LONG == 32
- return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
-#elif BITS_PER_LONG == 64
- __u64 res = w - ((w >> 1) & 0x5555555555555555ul);
- res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
- res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
- res = res + (res >> 8);
- res = res + (res >> 16);
- return (res + (res >> 32)) & 0x00000000000000FFul;
-#endif
-}
diff --git a/tools/perf/util/include/asm/hweight.h b/tools/perf/util/include/asm/hweight.h
deleted file mode 100644
index 36cf26d..0000000
--- a/tools/perf/util/include/asm/hweight.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef PERF_HWEIGHT_H
-#define PERF_HWEIGHT_H
-
-#include <linux/types.h>
-unsigned int hweight32(unsigned int w);
-unsigned long hweight64(__u64 w);
-
-#endif /* PERF_HWEIGHT_H */
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
deleted file mode 100644
index eda4416..0000000
--- a/tools/perf/util/include/linux/bitmap.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef _PERF_BITOPS_H
-#define _PERF_BITOPS_H
-
-#include <string.h>
-#include <linux/bitops.h>
-
-int __bitmap_weight(const unsigned long *bitmap, int bits);
-
-#define BITMAP_LAST_WORD_MASK(nbits) \
-( \
- ((nbits) % BITS_PER_LONG) ? \
- (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
-)
-
-#define small_const_nbits(nbits) \
- (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
-
-static inline void bitmap_zero(unsigned long *dst, int nbits)
-{
- if (small_const_nbits(nbits))
- *dst = 0UL;
- else {
- int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
- memset(dst, 0, len);
- }
-}
-
-static inline int bitmap_weight(const unsigned long *src, int nbits)
-{
- if (small_const_nbits(nbits))
- return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
- return __bitmap_weight(src, nbits);
-}
-
-#endif /* _PERF_BITOPS_H */
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
deleted file mode 100644
index bb4ac2e..0000000
--- a/tools/perf/util/include/linux/bitops.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef _PERF_LINUX_BITOPS_H_
-#define _PERF_LINUX_BITOPS_H_
-
-#include <linux/kernel.h>
-#include <asm/hweight.h>
-
-#define BITS_PER_LONG __WORDSIZE
-#define BITS_PER_BYTE 8
-#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
-
-static inline void set_bit(int nr, unsigned long *addr)
-{
- addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
-}
-
-static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
-{
- return ((1UL << (nr % BITS_PER_LONG)) &
- (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
-}
-
-static inline unsigned long hweight_long(unsigned long w)
-{
- return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
-}
-
-#endif
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
deleted file mode 100644
index 1eb804f..0000000
--- a/tools/perf/util/include/linux/kernel.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#ifndef PERF_LINUX_KERNEL_H_
-#define PERF_LINUX_KERNEL_H_
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
-
-#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
-#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
-
-#ifndef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-
-#ifndef container_of
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof(((type *)0)->member) * __mptr = (ptr); \
- (type *)((char *)__mptr - offsetof(type, member)); })
-#endif
-
-#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
-
-#ifndef max
-#define max(x, y) ({ \
- typeof(x) _max1 = (x); \
- typeof(y) _max2 = (y); \
- (void) (&_max1 == &_max2); \
- _max1 > _max2 ? _max1 : _max2; })
-#endif
-
-#ifndef min
-#define min(x, y) ({ \
- typeof(x) _min1 = (x); \
- typeof(y) _min2 = (y); \
- (void) (&_min1 == &_min2); \
- _min1 < _min2 ? _min1 : _min2; })
-#endif
-
-#ifndef BUG_ON
-#define BUG_ON(cond) assert(!(cond))
-#endif
-
-/*
- * Both need more care to handle endianness
- * (Don't use bitmap_copy_le() for now)
- */
-#define cpu_to_le64(x) (x)
-#define cpu_to_le32(x) (x)
-
-static inline int
-vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
-{
- int i;
- ssize_t ssize = size;
-
- i = vsnprintf(buf, size, fmt, args);
-
- return (i >= ssize) ? (ssize - 1) : i;
-}
-
-static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
-{
- va_list args;
- ssize_t ssize = size;
- int i;
-
- va_start(args, fmt);
- i = vsnprintf(buf, size, fmt, args);
- va_end(args);
-
- return (i >= ssize) ? (ssize - 1) : i;
-}
-
-static inline unsigned long
-simple_strtoul(const char *nptr, char **endptr, int base)
-{
- return strtoul(nptr, endptr, base);
-}
-
-int eprintf(int level,
- const char *fmt, ...) __attribute__((format(printf, 2, 3)));
-
-#ifndef pr_fmt
-#define pr_fmt(fmt) fmt
-#endif
-
-#define pr_err(fmt, ...) \
- eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_warning(fmt, ...) \
- eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_info(fmt, ...) \
- eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debug(fmt, ...) \
- eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debugN(n, fmt, ...) \
- eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
-
-#endif
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 4219466..a00d888 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -3,10 +3,12 @@
#include <linux/compiler.h>
#include <linux/list.h>
-#include <lk/rbtree.h>
#include <stdio.h>
#include <stdbool.h>
+#include <sys/types.h>
#include <lk/types.h>
+#include <lk/rbtree.h>
+#include <lk/kernel.h>
enum map_type {
MAP__FUNCTION = 0,
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index c7d72dc..dbcdf33 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -1,7 +1,7 @@
#ifndef __PERF_PARSE_OPTIONS_H
#define __PERF_PARSE_OPTIONS_H
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include <stdbool.h>
enum parse_opt_type {
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
index aacedb8..12f5221 100644
--- a/tools/perf/util/pstack.c
+++ b/tools/perf/util/pstack.c
@@ -6,7 +6,7 @@
#include <lk/util.h>
#include "pstack.h"
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include <stdlib.h>
struct pstack {
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 73cac6a..ddeef41 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1,6 +1,6 @@
#define _FILE_OFFSET_BITS 64
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include <byteswap.h>
#include <unistd.h>
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 649eae3..964da20 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -1,6 +1,7 @@
#ifndef __PERF_STRLIST_H
#define __PERF_STRLIST_H
+#include <lk/kernel.h>
#include <lk/rbtree.h>
#include <stdbool.h>
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 44831be..fadc2c0 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -34,7 +34,7 @@
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
-#include <linux/kernel.h>
+#include <lk/kernel.h>
#include <lk/debugfs.h>
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 13/20] perf: Export compiler.h to the generic library
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (11 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 12/20] perf: Export generic kernel utils " Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 14/20] perf: Export color.ch and config.ch Borislav Petkov
` (7 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Sucked in by other utils later.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 1 +
tools/lib/lk/compiler.h | 12 ++++++++++++
tools/perf/Makefile | 1 -
tools/perf/bench/bench.h | 2 +-
tools/perf/util/cache.h | 2 +-
tools/perf/util/include/linux/compiler.h | 12 ------------
tools/perf/util/map.h | 2 +-
7 files changed, 16 insertions(+), 16 deletions(-)
create mode 100644 tools/lib/lk/compiler.h
delete mode 100644 tools/perf/util/include/linux/compiler.h
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index f074216..d62e898 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -13,6 +13,7 @@ LIB_H += rbtree.h
LIB_H += bitops.h
LIB_H += bitmap.h
LIB_H += kernel.h
+LIB_H += compiler.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
diff --git a/tools/lib/lk/compiler.h b/tools/lib/lk/compiler.h
new file mode 100644
index 0000000..8e8cc91
--- /dev/null
+++ b/tools/lib/lk/compiler.h
@@ -0,0 +1,12 @@
+#ifndef __LK_COMPILER_H
+#define __LK_COMPILER_H
+
+#ifndef __always_inline
+#define __always_inline inline
+#endif
+#define __user
+#define __attribute_const__
+
+#define __used __attribute__((__unused__))
+
+#endif /* __LK_COMPILER_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 6a4a4f5..2e18b37 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -334,7 +334,6 @@ LIB_H += ../../include/linux/perf_event.h
LIB_H += ../../include/linux/list.h
LIB_H += ../../include/linux/hash.h
LIB_H += ../../include/linux/stringify.h
-LIB_H += util/include/linux/compiler.h
LIB_H += util/include/linux/ctype.h
LIB_H += util/include/linux/list.h
LIB_H += util/include/linux/module.h
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 0c7ee07..565fce8 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -1,7 +1,7 @@
#ifndef BENCH_H
#define BENCH_H
-#include <linux/compiler.h>
+#include <lk/compiler.h>
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index ce83e82..720f2de 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -5,7 +5,7 @@
#include <lk/util.h>
#include "strbuf.h"
#include "../perf.h"
-#include <linux/compiler.h>
+#include <lk/compiler.h>
#define CMD_EXEC_PATH "--exec-path"
#define CMD_PERF_DIR "--perf-dir="
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h
deleted file mode 100644
index 791f9dd..0000000
--- a/tools/perf/util/include/linux/compiler.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _PERF_LINUX_COMPILER_H_
-#define _PERF_LINUX_COMPILER_H_
-
-#ifndef __always_inline
-#define __always_inline inline
-#endif
-#define __user
-#define __attribute_const__
-
-#define __used __attribute__((__unused__))
-
-#endif
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index a00d888..efbff5b 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -1,7 +1,7 @@
#ifndef __PERF_MAP_H
#define __PERF_MAP_H
-#include <linux/compiler.h>
+#include <lk/compiler.h>
#include <linux/list.h>
#include <stdio.h>
#include <stdbool.h>
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 14/20] perf: Export color.ch and config.ch
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (12 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 13/20] perf: Export compiler.h to the generic library Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 15/20] perf: Export strlist.ch Borislav Petkov
` (6 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Will be needed by stuff exported later, move perf-wide defines to
perf/config.ch temporarily.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/compiler.h | 2 +
tools/lib/perf/Makefile | 5 +
tools/lib/perf/color.c | 324 ++++++++++++++++++++++++++
tools/lib/perf/color.h | 51 ++++
tools/lib/perf/config.c | 502 ++++++++++++++++++++++++++++++++++++++++
tools/lib/perf/config.h | 31 +++
tools/lib/perf/path.c | 156 +++++++++++++
tools/lib/trace/Makefile | 2 +
tools/lib/trace/parse-events.c | 11 +-
tools/lib/trace/parse-events.h | 8 +-
tools/lib/trace/parse-utils.c | 3 +-
tools/perf/Makefile | 8 +-
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-help.c | 2 +
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/perf.c | 1 +
tools/perf/util/abspath.c | 1 +
tools/perf/util/alias.c | 1 +
tools/perf/util/cache.h | 19 --
tools/perf/util/color.c | 324 --------------------------
tools/perf/util/color.h | 46 ----
tools/perf/util/config.c | 492 ---------------------------------------
tools/perf/util/debug.c | 2 +-
tools/perf/util/environment.c | 1 -
tools/perf/util/exec_cmd.c | 10 +
tools/perf/util/help.c | 2 +
| 15 +-
tools/perf/util/path.c | 156 -------------
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/ui/browser.c | 2 +-
33 files changed, 1110 insertions(+), 1079 deletions(-)
create mode 100644 tools/lib/perf/color.c
create mode 100644 tools/lib/perf/color.h
create mode 100644 tools/lib/perf/config.c
create mode 100644 tools/lib/perf/config.h
create mode 100644 tools/lib/perf/path.c
delete mode 100644 tools/perf/util/color.c
delete mode 100644 tools/perf/util/color.h
delete mode 100644 tools/perf/util/config.c
delete mode 100644 tools/perf/util/path.c
diff --git a/tools/lib/lk/compiler.h b/tools/lib/lk/compiler.h
index 8e8cc91..acf31cd 100644
--- a/tools/lib/lk/compiler.h
+++ b/tools/lib/lk/compiler.h
@@ -1,6 +1,8 @@
#ifndef __LK_COMPILER_H
#define __LK_COMPILER_H
+#define __weak __attribute__((weak))
+
#ifndef __always_inline
#define __always_inline inline
#endif
diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
index 5815664..7709a46 100644
--- a/tools/lib/perf/Makefile
+++ b/tools/lib/perf/Makefile
@@ -6,8 +6,13 @@ LIB_OBJS=
LIB_H += mmap.h
LIB_H += util.h
+LIB_H += color.h
+LIB_H += config.h
LIB_OBJS += mmap.o
+LIB_OBJS += color.o
+LIB_OBJS += config.o
+LIB_OBJS += path.o
LIBFILE = $(LIB_OUTPUT)lkperflib.a
diff --git a/tools/lib/perf/color.c b/tools/lib/perf/color.c
new file mode 100644
index 0000000..b97eedc
--- /dev/null
+++ b/tools/lib/perf/color.c
@@ -0,0 +1,324 @@
+#include "color.h"
+#include "config.h"
+
+int perf_use_color_default = -1;
+
+static int parse_color(const char *name, int len)
+{
+ static const char * const color_names[] = {
+ "normal", "black", "red", "green", "yellow",
+ "blue", "magenta", "cyan", "white"
+ };
+ char *end;
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
+ const char *str = color_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return i - 1;
+ }
+ i = strtol(name, &end, 10);
+ if (end - name == len && i >= -1 && i <= 255)
+ return i;
+ return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+ static const int attr_values[] = { 1, 2, 4, 5, 7 };
+ static const char * const attr_names[] = {
+ "bold", "dim", "ul", "blink", "reverse"
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+ const char *str = attr_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return attr_values[i];
+ }
+ return -1;
+}
+
+void color_parse(const char *value, const char *var, char *dst)
+{
+ color_parse_mem(value, strlen(value), var, dst);
+}
+
+void color_parse_mem(const char *value, int value_len, const char *var,
+ char *dst)
+{
+ const char *ptr = value;
+ int len = value_len;
+ int attr = -1;
+ int fg = -2;
+ int bg = -2;
+
+ if (!strncasecmp(value, "reset", len)) {
+ strcpy(dst, PERF_COLOR_RESET);
+ return;
+ }
+
+ /* [fg [bg]] [attr] */
+ while (len > 0) {
+ const char *word = ptr;
+ int val, wordlen = 0;
+
+ while (len > 0 && !isspace(word[wordlen])) {
+ wordlen++;
+ len--;
+ }
+
+ ptr = word + wordlen;
+ while (len > 0 && isspace(*ptr)) {
+ ptr++;
+ len--;
+ }
+
+ val = parse_color(word, wordlen);
+ if (val >= -1) {
+ if (fg == -2) {
+ fg = val;
+ continue;
+ }
+ if (bg == -2) {
+ bg = val;
+ continue;
+ }
+ goto bad;
+ }
+ val = parse_attr(word, wordlen);
+ if (val < 0 || attr != -1)
+ goto bad;
+ attr = val;
+ }
+
+ if (attr >= 0 || fg >= 0 || bg >= 0) {
+ int sep = 0;
+
+ *dst++ = '\033';
+ *dst++ = '[';
+ if (attr >= 0) {
+ *dst++ = '0' + attr;
+ sep++;
+ }
+ if (fg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (fg < 8) {
+ *dst++ = '3';
+ *dst++ = '0' + fg;
+ } else {
+ dst += sprintf(dst, "38;5;%d", fg);
+ }
+ }
+ if (bg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (bg < 8) {
+ *dst++ = '4';
+ *dst++ = '0' + bg;
+ } else {
+ dst += sprintf(dst, "48;5;%d", bg);
+ }
+ }
+ *dst++ = 'm';
+ }
+ *dst = 0;
+ return;
+bad:
+ die("bad color value '%.*s' for variable '%s'", value_len, value, var);
+}
+
+int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
+{
+ if (value) {
+ if (!strcasecmp(value, "never"))
+ return 0;
+ if (!strcasecmp(value, "always"))
+ return 1;
+ if (!strcasecmp(value, "auto"))
+ goto auto_color;
+ }
+
+ /* Missing or explicit false to turn off colorization */
+ if (!perf_config_bool(var, value))
+ return 0;
+
+ /* any normal truth value defaults to 'auto' */
+ auto_color:
+ if (stdout_is_tty < 0)
+ stdout_is_tty = isatty(1);
+ if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
+ char *term = getenv("TERM");
+ if (term && strcmp(term, "dumb"))
+ return 1;
+ }
+ return 0;
+}
+
+int perf_color_default_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "color.ui")) {
+ perf_use_color_default = perf_config_colorbool(var, value, -1);
+ return 0;
+ }
+
+ return perf_default_config(var, value, cb);
+}
+
+static int __color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args, const char *trail)
+{
+ int r = 0;
+
+ /*
+ * Auto-detect:
+ */
+ if (perf_use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ perf_use_color_default = 1;
+ else
+ perf_use_color_default = 0;
+ }
+
+ if (perf_use_color_default && *color)
+ r += snprintf(bf, size, "%s", color);
+ r += vsnprintf(bf + r, size - r, fmt, args);
+ if (perf_use_color_default && *color)
+ r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
+ if (trail)
+ r += snprintf(bf + r, size - r, "%s", trail);
+ return r;
+}
+
+static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
+ va_list args, const char *trail)
+{
+ int r = 0;
+
+ /*
+ * Auto-detect:
+ */
+ if (perf_use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ perf_use_color_default = 1;
+ else
+ perf_use_color_default = 0;
+ }
+
+ if (perf_use_color_default && *color)
+ r += fprintf(fp, "%s", color);
+ r += vfprintf(fp, fmt, args);
+ if (perf_use_color_default && *color)
+ r += fprintf(fp, "%s", PERF_COLOR_RESET);
+ if (trail)
+ r += fprintf(fp, "%s", trail);
+ return r;
+}
+
+int color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args)
+{
+ return __color_vsnprintf(bf, size, color, fmt, args, NULL);
+}
+
+int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
+{
+ return __color_vfprintf(fp, color, fmt, args, NULL);
+}
+
+int color_snprintf(char *bf, size_t size, const char *color,
+ const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = color_vsnprintf(bf, size, color, fmt, args);
+ va_end(args);
+ return r;
+}
+
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = color_vfprintf(fp, color, fmt, args);
+ va_end(args);
+ return r;
+}
+
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+ va_start(args, fmt);
+ r = __color_vfprintf(fp, color, fmt, args, "\n");
+ va_end(args);
+ return r;
+}
+
+/*
+ * This function splits the buffer by newlines and colors the lines individually.
+ *
+ * Returns 0 on success.
+ */
+int color_fwrite_lines(FILE *fp, const char *color,
+ size_t count, const char *buf)
+{
+ if (!*color)
+ return fwrite(buf, count, 1, fp) != 1;
+
+ while (count) {
+ char *p = memchr(buf, '\n', count);
+
+ if (p != buf && (fputs(color, fp) < 0 ||
+ fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
+ fputs(PERF_COLOR_RESET, fp) < 0))
+ return -1;
+ if (!p)
+ return 0;
+ if (fputc('\n', fp) < 0)
+ return -1;
+ count -= p + 1 - buf;
+ buf = p + 1;
+ }
+ return 0;
+}
+
+const char *get_percent_color(double percent)
+{
+ const char *color = PERF_COLOR_NORMAL;
+
+ /*
+ * We color high-overhead entries in red, mid-overhead
+ * entries in green - and keep the low overhead places
+ * normal:
+ */
+ if (percent >= MIN_RED)
+ color = PERF_COLOR_RED;
+ else {
+ if (percent > MIN_GREEN)
+ color = PERF_COLOR_GREEN;
+ }
+ return color;
+}
+
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
+{
+ int r;
+ const char *color;
+
+ color = get_percent_color(percent);
+ r = color_fprintf(fp, color, fmt, percent);
+
+ return r;
+}
+
+int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent)
+{
+ const char *color = get_percent_color(percent);
+ return color_snprintf(bf, size, color, fmt, percent);
+}
diff --git a/tools/lib/perf/color.h b/tools/lib/perf/color.h
new file mode 100644
index 0000000..799447b
--- /dev/null
+++ b/tools/lib/perf/color.h
@@ -0,0 +1,51 @@
+#ifndef __PERF_COLOR_H
+#define __PERF_COLOR_H
+
+#include <lk/util.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+#define COLOR_MAXLEN 24
+
+#define PERF_COLOR_NORMAL ""
+#define PERF_COLOR_RESET "\033[m"
+#define PERF_COLOR_BOLD "\033[1m"
+#define PERF_COLOR_RED "\033[31m"
+#define PERF_COLOR_GREEN "\033[32m"
+#define PERF_COLOR_YELLOW "\033[33m"
+#define PERF_COLOR_BLUE "\033[34m"
+#define PERF_COLOR_MAGENTA "\033[35m"
+#define PERF_COLOR_CYAN "\033[36m"
+#define PERF_COLOR_BG_RED "\033[41m"
+
+#define MIN_GREEN 0.5
+#define MIN_RED 5.0
+
+/*
+ * This variable stores the value of color.ui
+ */
+extern int perf_use_color_default;
+
+/*
+ * Use this instead of perf_default_config if you need the value of color.ui.
+ */
+int perf_color_default_config(const char *var, const char *value, void *cb);
+
+int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
+void color_parse(const char *value, const char *var, char *dst);
+void color_parse_mem(const char *value, int len, const char *var, char *dst);
+int color_vsnprintf(char *bf, size_t size, const char *color,
+ const char *fmt, va_list args);
+int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
+int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
+int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent);
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
+const char *get_percent_color(double percent);
+
+#endif /* __PERF_COLOR_H */
diff --git a/tools/lib/perf/config.c b/tools/lib/perf/config.c
new file mode 100644
index 0000000..bb72a55
--- /dev/null
+++ b/tools/lib/perf/config.c
@@ -0,0 +1,502 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Johannes Schindelin, 2005
+ *
+ */
+#include <lk/util.h>
+#include <lk/compiler.h>
+#include "config.h"
+
+#define MAXNAME (256)
+
+#define DEBUG_CACHE_DIR ".debug"
+
+int spawned_pager;
+int pager_use_color = 1;
+
+char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
+
+static FILE *config_file;
+static const char *config_file_name;
+static int config_linenr;
+static int config_file_eof;
+
+static const char *config_exclusive_filename;
+
+static int get_next_char(void)
+{
+ int c;
+ FILE *f;
+
+ c = '\n';
+ if ((f = config_file) != NULL) {
+ c = fgetc(f);
+ if (c == '\r') {
+ /* DOS like systems */
+ c = fgetc(f);
+ if (c != '\n') {
+ ungetc(c, f);
+ c = '\r';
+ }
+ }
+ if (c == '\n')
+ config_linenr++;
+ if (c == EOF) {
+ config_file_eof = 1;
+ c = '\n';
+ }
+ }
+ return c;
+}
+
+static char *parse_value(void)
+{
+ static char value[1024];
+ int quote = 0, comment = 0, space = 0;
+ size_t len = 0;
+
+ for (;;) {
+ int c = get_next_char();
+
+ if (len >= sizeof(value) - 1)
+ return NULL;
+ if (c == '\n') {
+ if (quote)
+ return NULL;
+ value[len] = 0;
+ return value;
+ }
+ if (comment)
+ continue;
+ if (isspace(c) && !quote) {
+ space = 1;
+ continue;
+ }
+ if (!quote) {
+ if (c == ';' || c == '#') {
+ comment = 1;
+ continue;
+ }
+ }
+ if (space) {
+ if (len)
+ value[len++] = ' ';
+ space = 0;
+ }
+ if (c == '\\') {
+ c = get_next_char();
+ switch (c) {
+ case '\n':
+ continue;
+ case 't':
+ c = '\t';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ /* Some characters escape as themselves */
+ case '\\': case '"':
+ break;
+ /* Reject unknown escape sequences */
+ default:
+ return NULL;
+ }
+ value[len++] = c;
+ continue;
+ }
+ if (c == '"') {
+ quote = 1-quote;
+ continue;
+ }
+ value[len++] = c;
+ }
+}
+
+static inline int iskeychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
+{
+ int c;
+ char *value;
+
+ /* Get the full name */
+ for (;;) {
+ c = get_next_char();
+ if (config_file_eof)
+ break;
+ if (!iskeychar(c))
+ break;
+ name[len++] = c;
+ if (len >= MAXNAME)
+ return -1;
+ }
+ name[len] = 0;
+ while (c == ' ' || c == '\t')
+ c = get_next_char();
+
+ value = NULL;
+ if (c != '\n') {
+ if (c != '=')
+ return -1;
+ value = parse_value();
+ if (!value)
+ return -1;
+ }
+ return fn(name, value, data);
+}
+
+static int get_extended_base_var(char *name, int baselen, int c)
+{
+ do {
+ if (c == '\n')
+ return -1;
+ c = get_next_char();
+ } while (isspace(c));
+
+ /* We require the format to be '[base "extension"]' */
+ if (c != '"')
+ return -1;
+ name[baselen++] = '.';
+
+ for (;;) {
+ int ch = get_next_char();
+
+ if (ch == '\n')
+ return -1;
+ if (ch == '"')
+ break;
+ if (ch == '\\') {
+ ch = get_next_char();
+ if (ch == '\n')
+ return -1;
+ }
+ name[baselen++] = ch;
+ if (baselen > MAXNAME / 2)
+ return -1;
+ }
+
+ /* Final ']' */
+ if (get_next_char() != ']')
+ return -1;
+ return baselen;
+}
+
+static int get_base_var(char *name)
+{
+ int baselen = 0;
+
+ for (;;) {
+ int c = get_next_char();
+ if (config_file_eof)
+ return -1;
+ if (c == ']')
+ return baselen;
+ if (isspace(c))
+ return get_extended_base_var(name, baselen, c);
+ if (!iskeychar(c) && c != '.')
+ return -1;
+ if (baselen > MAXNAME / 2)
+ return -1;
+ name[baselen++] = tolower(c);
+ }
+}
+
+static int perf_parse_file(config_fn_t fn, void *data)
+{
+ int comment = 0;
+ int baselen = 0;
+ static char var[MAXNAME];
+
+ /* U+FEFF Byte Order Mark in UTF8 */
+ static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
+ const unsigned char *bomptr = utf8_bom;
+
+ for (;;) {
+ int c = get_next_char();
+ if (bomptr && *bomptr) {
+ /* We are at the file beginning; skip UTF8-encoded BOM
+ * if present. Sane editors won't put this in on their
+ * own, but e.g. Windows Notepad will do it happily. */
+ if ((unsigned char) c == *bomptr) {
+ bomptr++;
+ continue;
+ } else {
+ /* Do not tolerate partial BOM. */
+ if (bomptr != utf8_bom)
+ break;
+ /* No BOM at file beginning. Cool. */
+ bomptr = NULL;
+ }
+ }
+ if (c == '\n') {
+ if (config_file_eof)
+ return 0;
+ comment = 0;
+ continue;
+ }
+ if (comment || isspace(c))
+ continue;
+ if (c == '#' || c == ';') {
+ comment = 1;
+ continue;
+ }
+ if (c == '[') {
+ baselen = get_base_var(var);
+ if (baselen <= 0)
+ break;
+ var[baselen++] = '.';
+ var[baselen] = 0;
+ continue;
+ }
+ if (!isalpha(c))
+ break;
+ var[baselen] = tolower(c);
+ if (get_value(fn, data, var, baselen+1) < 0)
+ break;
+ }
+ die("bad config file line %d in %s", config_linenr, config_file_name);
+}
+
+static int parse_unit_factor(const char *end, unsigned long *val)
+{
+ if (!*end)
+ return 1;
+ else if (!strcasecmp(end, "k")) {
+ *val *= 1024;
+ return 1;
+ }
+ else if (!strcasecmp(end, "m")) {
+ *val *= 1024 * 1024;
+ return 1;
+ }
+ else if (!strcasecmp(end, "g")) {
+ *val *= 1024 * 1024 * 1024;
+ return 1;
+ }
+ return 0;
+}
+
+static int perf_parse_long(const char *value, long *ret)
+{
+ if (value && *value) {
+ char *end;
+ long val = strtol(value, &end, 0);
+ unsigned long factor = 1;
+ if (!parse_unit_factor(end, &factor))
+ return 0;
+ *ret = val * factor;
+ return 1;
+ }
+ return 0;
+}
+
+static void die_bad_config(const char *name)
+{
+ if (config_file_name)
+ die("bad config value for '%s' in %s", name, config_file_name);
+ die("bad config value for '%s'", name);
+}
+
+int perf_config_int(const char *name, const char *value)
+{
+ long ret = 0;
+ if (!perf_parse_long(value, &ret))
+ die_bad_config(name);
+ return ret;
+}
+
+static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+ *is_bool = 1;
+ if (!value)
+ return 1;
+ if (!*value)
+ return 0;
+ if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+ return 1;
+ if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+ return 0;
+ *is_bool = 0;
+ return perf_config_int(name, value);
+}
+
+int perf_config_bool(const char *name, const char *value)
+{
+ int discard;
+ return !!perf_config_bool_or_int(name, value, &discard);
+}
+
+const char *perf_config_dirname(const char *name, const char *value)
+{
+ if (!name)
+ return NULL;
+ return value;
+}
+
+static int perf_default_core_config(const char *var __used, const char *value __used)
+{
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+int perf_default_config(const char *var, const char *value, void *dummy __used)
+{
+ if (!prefixcmp(var, "core."))
+ return perf_default_core_config(var, value);
+
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+ int ret;
+ FILE *f = fopen(filename, "r");
+
+ ret = -1;
+ if (f) {
+ config_file = f;
+ config_file_name = filename;
+ config_linenr = 1;
+ config_file_eof = 0;
+ ret = perf_parse_file(fn, data);
+ fclose(f);
+ config_file_name = NULL;
+ }
+ return ret;
+}
+
+const char * __weak perf_etc_perfconfig(void)
+{
+ return "";
+}
+
+static int perf_env_bool(const char *k, int def)
+{
+ const char *v = getenv(k);
+ return v ? perf_config_bool(k, v) : def;
+}
+
+static int perf_config_system(void)
+{
+ return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
+}
+
+static int perf_config_global(void)
+{
+ return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
+}
+
+int perf_config(config_fn_t fn, void *data)
+{
+ int ret = 0, found = 0;
+ char *repo_config = NULL;
+ const char *home = NULL;
+
+ /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
+ if (config_exclusive_filename)
+ return perf_config_from_file(fn, config_exclusive_filename, data);
+ if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
+ ret += perf_config_from_file(fn, perf_etc_perfconfig(),
+ data);
+ found += 1;
+ }
+
+ home = getenv("HOME");
+ if (perf_config_global() && home) {
+ char *user_config = strdup(mkpath("%s/.perfconfig", home));
+ if (!access(user_config, R_OK)) {
+ ret += perf_config_from_file(fn, user_config, data);
+ found += 1;
+ }
+ free(user_config);
+ }
+
+ repo_config = perf_pathdup("config");
+ if (!access(repo_config, R_OK)) {
+ ret += perf_config_from_file(fn, repo_config, data);
+ found += 1;
+ }
+ free(repo_config);
+ if (found == 0)
+ return -1;
+ return ret;
+}
+
+/*
+ * Call this to report error for your variable that should not
+ * get a boolean value (i.e. "[my] var" means "true").
+ */
+int config_error_nonbool(const char *var)
+{
+ return error("Missing value for '%s'", var);
+}
+
+struct buildid_dir_config {
+ char *dir;
+};
+
+static int buildid_dir_command_config(const char *var, const char *value,
+ void *data)
+{
+ struct buildid_dir_config *c = data;
+ const char *v;
+
+ /* same dir for all commands */
+ if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
+ v = perf_config_dirname(var, value);
+ if (!v)
+ return -1;
+ strncpy(c->dir, v, MAXPATHLEN-1);
+ c->dir[MAXPATHLEN-1] = '\0';
+ }
+ return 0;
+}
+
+static void check_buildid_dir_config(void)
+{
+ struct buildid_dir_config c;
+ c.dir = buildid_dir;
+ perf_config(buildid_dir_command_config, &c);
+}
+
+void set_buildid_dir(void)
+{
+ buildid_dir[0] = '\0';
+
+ /* try config file */
+ check_buildid_dir_config();
+
+ /* default to $HOME/.debug */
+ if (buildid_dir[0] == '\0') {
+ char *v = getenv("HOME");
+ if (v) {
+ snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
+ v, DEBUG_CACHE_DIR);
+ } else {
+ strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
+ }
+ buildid_dir[MAXPATHLEN-1] = '\0';
+ }
+ /* for communicating with external commands */
+ setenv("PERF_BUILDID_DIR", buildid_dir, 1);
+}
+
+int pager_in_use(void)
+{
+ const char *env;
+
+ if (spawned_pager)
+ return 1;
+
+ env = getenv("PERF_PAGER_IN_USE");
+ return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0;
+}
diff --git a/tools/lib/perf/config.h b/tools/lib/perf/config.h
new file mode 100644
index 0000000..5ef9c1d
--- /dev/null
+++ b/tools/lib/perf/config.h
@@ -0,0 +1,31 @@
+#ifndef __PERF_CONFIG_H
+#define __PERF_CONFIG_H
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <lk/util.h>
+
+extern int spawned_pager;
+
+typedef int (*config_fn_t)(const char *, const char *, void *);
+extern int perf_default_config(const char *, const char *, void *);
+extern int perf_config(config_fn_t fn, void *);
+extern int perf_config_int(const char *, const char *);
+extern int perf_config_bool(const char *, const char *);
+extern int config_error_nonbool(const char *);
+extern const char *perf_config_dirname(const char *, const char *);
+
+extern int pager_in_use(void);
+extern int pager_use_color;
+
+extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+const char *perf_etc_perfconfig(void);
+
+extern size_t strlcpy(char *dest, const char *src, size_t size);
+extern char *strip_path_suffix(const char *path, const char *suffix);
+extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+#endif /* __PERF_CONFIG_H */
diff --git a/tools/lib/perf/path.c b/tools/lib/perf/path.c
new file mode 100644
index 0000000..16c73b7
--- /dev/null
+++ b/tools/lib/perf/path.c
@@ -0,0 +1,156 @@
+/*
+ * I'm tired of doing "vsnprintf()" etc just to open a
+ * file, so here's a "return static buffer with printf"
+ * interface for paths.
+ *
+ * It's obviously not thread-safe. Sue me. But it's quite
+ * useful for doing things like
+ *
+ * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY);
+ *
+ * which is what it's designed for.
+ */
+#include "config.h"
+
+static char bad_path[] = "/bad-path/";
+/*
+ * Two hacks:
+ */
+
+static const char *get_perf_dir(void)
+{
+ return ".";
+}
+
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
+
+
+static char *get_pathname(void)
+{
+ static char pathname_array[4][PATH_MAX];
+ static int idx;
+
+ return pathname_array[3 & ++idx];
+}
+
+static char *cleanup_path(char *path)
+{
+ /* Clean it up */
+ if (!memcmp(path, "./", 2)) {
+ path += 2;
+ while (*path == '/')
+ path++;
+ }
+ return path;
+}
+
+static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+{
+ const char *perf_dir = get_perf_dir();
+ size_t len;
+
+ len = strlen(perf_dir);
+ if (n < len + 1)
+ goto bad;
+ memcpy(buf, perf_dir, len);
+ if (len && !is_dir_sep(perf_dir[len-1]))
+ buf[len++] = '/';
+ len += vsnprintf(buf + len, n - len, fmt, args);
+ if (len >= n)
+ goto bad;
+ return cleanup_path(buf);
+bad:
+ strlcpy(buf, bad_path, n);
+ return buf;
+}
+
+char *perf_pathdup(const char *fmt, ...)
+{
+ char path[PATH_MAX];
+ va_list args;
+ va_start(args, fmt);
+ (void)perf_vsnpath(path, sizeof(path), fmt, args);
+ va_end(args);
+ return xstrdup(path);
+}
+
+char *mkpath(const char *fmt, ...)
+{
+ va_list args;
+ unsigned len;
+ char *pathname = get_pathname();
+
+ va_start(args, fmt);
+ len = vsnprintf(pathname, PATH_MAX, fmt, args);
+ va_end(args);
+ if (len >= PATH_MAX)
+ return bad_path;
+ return cleanup_path(pathname);
+}
+
+char *perf_path(const char *fmt, ...)
+{
+ const char *perf_dir = get_perf_dir();
+ char *pathname = get_pathname();
+ va_list args;
+ unsigned len;
+
+ len = strlen(perf_dir);
+ if (len > PATH_MAX-100)
+ return bad_path;
+ memcpy(pathname, perf_dir, len);
+ if (len && perf_dir[len-1] != '/')
+ pathname[len++] = '/';
+ va_start(args, fmt);
+ len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+ va_end(args);
+ if (len >= PATH_MAX)
+ return bad_path;
+ return cleanup_path(pathname);
+}
+
+/* strip arbitrary amount of directory separators at end of path */
+static inline int chomp_trailing_dir_sep(const char *path, int len)
+{
+ while (len && is_dir_sep(path[len - 1]))
+ len--;
+ return len;
+}
+
+/*
+ * If path ends with suffix (complete path components), returns the
+ * part before suffix (sans trailing directory separators).
+ * Otherwise returns NULL.
+ */
+char *strip_path_suffix(const char *path, const char *suffix)
+{
+ int path_len = strlen(path), suffix_len = strlen(suffix);
+
+ while (suffix_len) {
+ if (!path_len)
+ return NULL;
+
+ if (is_dir_sep(path[path_len - 1])) {
+ if (!is_dir_sep(suffix[suffix_len - 1]))
+ return NULL;
+ path_len = chomp_trailing_dir_sep(path, path_len);
+ suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
+ }
+ else if (path[--path_len] != suffix[--suffix_len])
+ return NULL;
+ }
+
+ if (path_len && !is_dir_sep(path[path_len - 1]))
+ return NULL;
+ return strndup(path, chomp_trailing_dir_sep(path, path_len));
+}
diff --git a/tools/lib/trace/Makefile b/tools/lib/trace/Makefile
index 4c625e5..22fa7b6 100644
--- a/tools/lib/trace/Makefile
+++ b/tools/lib/trace/Makefile
@@ -42,6 +42,8 @@ PEVENT_LIB_OBJS += parse-filter.o
PEVENT_LIB_OBJS += parse-utils.o
PEVENT_LIB_OBJS += trace-seq.o
+ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+
$(OUTPUT)%.o: %.c
$(QUIET_CC)$(CC) -g -o $@ -c $(ALL_CFLAGS) $<
diff --git a/tools/lib/trace/parse-events.c b/tools/lib/trace/parse-events.c
index 5503a18..1f95f87 100644
--- a/tools/lib/trace/parse-events.c
+++ b/tools/lib/trace/parse-events.c
@@ -24,7 +24,6 @@
* 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>
@@ -95,13 +94,7 @@ 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)
+static struct print_arg *alloc_arg(void)
{
struct print_arg *arg;
@@ -164,7 +157,7 @@ static int cmdline_init(struct pevent *pevent)
return 0;
}
-static char *find_cmdline(struct pevent *pevent, int pid)
+static const char *find_cmdline(struct pevent *pevent, int pid)
{
const struct cmdline *comm;
struct cmdline key;
diff --git a/tools/lib/trace/parse-events.h b/tools/lib/trace/parse-events.h
index 5c10208..31f46f5 100644
--- a/tools/lib/trace/parse-events.h
+++ b/tools/lib/trace/parse-events.h
@@ -24,6 +24,8 @@
#include <stdarg.h>
#include <regex.h>
+#include <lk/compiler.h>
+
#ifndef __unused
#define __unused __attribute__ ((unused))
#endif
@@ -359,9 +361,9 @@ 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, ...);
+void __vdie(const char *fmt, va_list ap);
+void __vwarning(const char *fmt, va_list ap);
+void __vpr_stat(const char *fmt, va_list ap);
static inline unsigned short
__data2host2(struct pevent *pevent, unsigned short data)
diff --git a/tools/lib/trace/parse-utils.c b/tools/lib/trace/parse-utils.c
index f023a13..ba6669b 100644
--- a/tools/lib/trace/parse-utils.c
+++ b/tools/lib/trace/parse-utils.c
@@ -3,8 +3,9 @@
#include <string.h>
#include <stdarg.h>
#include <errno.h>
+#include <lk/compiler.h>
-#define __weak __attribute__((weak))
+#include "parse-events.h"
void __vdie(const char *fmt, va_list ap)
{
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 2e18b37..9af0817 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -369,7 +369,6 @@ LIB_H += util/svghelper.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
LIB_H += util/symbol.h
-LIB_H += util/color.h
LIB_H += util/values.h
LIB_H += util/sort.h
LIB_H += util/hist.h
@@ -382,7 +381,6 @@ LIB_H += util/pstack.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
LIB_OBJS += $(OUTPUT)util/build-id.o
-LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
@@ -390,7 +388,6 @@ LIB_OBJS += $(OUTPUT)util/help.o
LIB_OBJS += $(OUTPUT)util/levenshtein.o
LIB_OBJS += $(OUTPUT)util/parse-options.o
LIB_OBJS += $(OUTPUT)util/parse-events.o
-LIB_OBJS += $(OUTPUT)util/path.o
LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
@@ -399,7 +396,6 @@ LIB_OBJS += $(OUTPUT)util/strlist.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/symbol.o
-LIB_OBJS += $(OUTPUT)util/color.o
LIB_OBJS += $(OUTPUT)util/pager.o
LIB_OBJS += $(OUTPUT)util/header.o
LIB_OBJS += $(OUTPUT)util/callchain.o
@@ -928,14 +924,12 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
'-DBINDIR="$(bindir_relative_SQ)"' \
'-DPREFIX="$(prefix_SQ)"' \
+ '-DETC_PERFCONFIG="$(ETC_PERFCONFIG_SQ)"' \
$<
$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
-$(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
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 7f9983b..c246b57 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -9,7 +9,7 @@
#include <lk/util.h>
-#include "util/color.h"
+#include <perf/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 6d5a8a7..02711d5 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -3,6 +3,8 @@
*
* Builtin help command
*/
+#include <perf/config.h>
+
#include "perf.h"
#include "util/cache.h"
#include "builtin.h"
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 76e6967..c4e50e4 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -9,7 +9,7 @@
#include <lk/util.h>
-#include "util/color.h"
+#include <perf/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index aa9e4fc..35678a5 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -16,7 +16,7 @@
#include <lk/util.h>
-#include "util/color.h"
+#include <perf/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 3727a5c..069d53c 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -20,7 +20,7 @@
#include "perf.h"
-#include "util/color.h"
+#include <perf/color.h>
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h"
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 1fa597c..5581794 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -7,6 +7,7 @@
* perf top, perf record, perf report, etc.) are started.
*/
#include <lk/debugfs.h>
+#include <perf/config.h>
#include "builtin.h"
#include "util/exec_cmd.h"
diff --git a/tools/perf/util/abspath.c b/tools/perf/util/abspath.c
index 0e76aff..6c53331 100644
--- a/tools/perf/util/abspath.c
+++ b/tools/perf/util/abspath.c
@@ -1,3 +1,4 @@
+#include <perf/config.h>
#include "cache.h"
static const char *get_pwd_cwd(void)
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c
index b8144e8..d076a15 100644
--- a/tools/perf/util/alias.c
+++ b/tools/perf/util/alias.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include <perf/config.h>
static const char *alias_key;
static char *alias_val;
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 720f2de..8a54465 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -17,19 +17,9 @@
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
-typedef int (*config_fn_t)(const char *, const char *, void *);
-extern int perf_default_config(const char *, const char *, void *);
-extern int perf_config(config_fn_t fn, void *);
-extern int perf_config_int(const char *, const char *);
-extern int perf_config_bool(const char *, const char *);
-extern int config_error_nonbool(const char *);
-extern const char *perf_config_dirname(const char *, const char *);
-
/* pager.c */
extern void setup_pager(void);
extern const char *pager_program;
-extern int pager_in_use(void);
-extern int pager_use_color;
extern int use_browser;
@@ -74,14 +64,5 @@ static inline int is_absolute_path(const char *path)
}
const char *make_nonrelative_path(const char *path);
-char *strip_path_suffix(const char *path, const char *suffix);
-
-extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-
-extern char *perf_pathdup(const char *fmt, ...)
- __attribute__((format (printf, 1, 2)));
-
-extern size_t strlcpy(char *dest, const char *src, size_t size);
#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
deleted file mode 100644
index e191eb9..0000000
--- a/tools/perf/util/color.c
+++ /dev/null
@@ -1,324 +0,0 @@
-#include "cache.h"
-#include "color.h"
-
-int perf_use_color_default = -1;
-
-static int parse_color(const char *name, int len)
-{
- static const char * const color_names[] = {
- "normal", "black", "red", "green", "yellow",
- "blue", "magenta", "cyan", "white"
- };
- char *end;
- int i;
-
- for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
- const char *str = color_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return i - 1;
- }
- i = strtol(name, &end, 10);
- if (end - name == len && i >= -1 && i <= 255)
- return i;
- return -2;
-}
-
-static int parse_attr(const char *name, int len)
-{
- static const int attr_values[] = { 1, 2, 4, 5, 7 };
- static const char * const attr_names[] = {
- "bold", "dim", "ul", "blink", "reverse"
- };
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
- const char *str = attr_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return attr_values[i];
- }
- return -1;
-}
-
-void color_parse(const char *value, const char *var, char *dst)
-{
- color_parse_mem(value, strlen(value), var, dst);
-}
-
-void color_parse_mem(const char *value, int value_len, const char *var,
- char *dst)
-{
- const char *ptr = value;
- int len = value_len;
- int attr = -1;
- int fg = -2;
- int bg = -2;
-
- if (!strncasecmp(value, "reset", len)) {
- strcpy(dst, PERF_COLOR_RESET);
- return;
- }
-
- /* [fg [bg]] [attr] */
- while (len > 0) {
- const char *word = ptr;
- int val, wordlen = 0;
-
- while (len > 0 && !isspace(word[wordlen])) {
- wordlen++;
- len--;
- }
-
- ptr = word + wordlen;
- while (len > 0 && isspace(*ptr)) {
- ptr++;
- len--;
- }
-
- val = parse_color(word, wordlen);
- if (val >= -1) {
- if (fg == -2) {
- fg = val;
- continue;
- }
- if (bg == -2) {
- bg = val;
- continue;
- }
- goto bad;
- }
- val = parse_attr(word, wordlen);
- if (val < 0 || attr != -1)
- goto bad;
- attr = val;
- }
-
- if (attr >= 0 || fg >= 0 || bg >= 0) {
- int sep = 0;
-
- *dst++ = '\033';
- *dst++ = '[';
- if (attr >= 0) {
- *dst++ = '0' + attr;
- sep++;
- }
- if (fg >= 0) {
- if (sep++)
- *dst++ = ';';
- if (fg < 8) {
- *dst++ = '3';
- *dst++ = '0' + fg;
- } else {
- dst += sprintf(dst, "38;5;%d", fg);
- }
- }
- if (bg >= 0) {
- if (sep++)
- *dst++ = ';';
- if (bg < 8) {
- *dst++ = '4';
- *dst++ = '0' + bg;
- } else {
- dst += sprintf(dst, "48;5;%d", bg);
- }
- }
- *dst++ = 'm';
- }
- *dst = 0;
- return;
-bad:
- die("bad color value '%.*s' for variable '%s'", value_len, value, var);
-}
-
-int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
-{
- if (value) {
- if (!strcasecmp(value, "never"))
- return 0;
- if (!strcasecmp(value, "always"))
- return 1;
- if (!strcasecmp(value, "auto"))
- goto auto_color;
- }
-
- /* Missing or explicit false to turn off colorization */
- if (!perf_config_bool(var, value))
- return 0;
-
- /* any normal truth value defaults to 'auto' */
- auto_color:
- if (stdout_is_tty < 0)
- stdout_is_tty = isatty(1);
- if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
- char *term = getenv("TERM");
- if (term && strcmp(term, "dumb"))
- return 1;
- }
- return 0;
-}
-
-int perf_color_default_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "color.ui")) {
- perf_use_color_default = perf_config_colorbool(var, value, -1);
- return 0;
- }
-
- return perf_default_config(var, value, cb);
-}
-
-static int __color_vsnprintf(char *bf, size_t size, const char *color,
- const char *fmt, va_list args, const char *trail)
-{
- int r = 0;
-
- /*
- * Auto-detect:
- */
- if (perf_use_color_default < 0) {
- if (isatty(1) || pager_in_use())
- perf_use_color_default = 1;
- else
- perf_use_color_default = 0;
- }
-
- if (perf_use_color_default && *color)
- r += snprintf(bf, size, "%s", color);
- r += vsnprintf(bf + r, size - r, fmt, args);
- if (perf_use_color_default && *color)
- r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
- if (trail)
- r += snprintf(bf + r, size - r, "%s", trail);
- return r;
-}
-
-static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
- va_list args, const char *trail)
-{
- int r = 0;
-
- /*
- * Auto-detect:
- */
- if (perf_use_color_default < 0) {
- if (isatty(1) || pager_in_use())
- perf_use_color_default = 1;
- else
- perf_use_color_default = 0;
- }
-
- if (perf_use_color_default && *color)
- r += fprintf(fp, "%s", color);
- r += vfprintf(fp, fmt, args);
- if (perf_use_color_default && *color)
- r += fprintf(fp, "%s", PERF_COLOR_RESET);
- if (trail)
- r += fprintf(fp, "%s", trail);
- return r;
-}
-
-int color_vsnprintf(char *bf, size_t size, const char *color,
- const char *fmt, va_list args)
-{
- return __color_vsnprintf(bf, size, color, fmt, args, NULL);
-}
-
-int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
-{
- return __color_vfprintf(fp, color, fmt, args, NULL);
-}
-
-int color_snprintf(char *bf, size_t size, const char *color,
- const char *fmt, ...)
-{
- va_list args;
- int r;
-
- va_start(args, fmt);
- r = color_vsnprintf(bf, size, color, fmt, args);
- va_end(args);
- return r;
-}
-
-int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
-{
- va_list args;
- int r;
-
- va_start(args, fmt);
- r = color_vfprintf(fp, color, fmt, args);
- va_end(args);
- return r;
-}
-
-int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
-{
- va_list args;
- int r;
- va_start(args, fmt);
- r = __color_vfprintf(fp, color, fmt, args, "\n");
- va_end(args);
- return r;
-}
-
-/*
- * This function splits the buffer by newlines and colors the lines individually.
- *
- * Returns 0 on success.
- */
-int color_fwrite_lines(FILE *fp, const char *color,
- size_t count, const char *buf)
-{
- if (!*color)
- return fwrite(buf, count, 1, fp) != 1;
-
- while (count) {
- char *p = memchr(buf, '\n', count);
-
- if (p != buf && (fputs(color, fp) < 0 ||
- fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
- fputs(PERF_COLOR_RESET, fp) < 0))
- return -1;
- if (!p)
- return 0;
- if (fputc('\n', fp) < 0)
- return -1;
- count -= p + 1 - buf;
- buf = p + 1;
- }
- return 0;
-}
-
-const char *get_percent_color(double percent)
-{
- const char *color = PERF_COLOR_NORMAL;
-
- /*
- * We color high-overhead entries in red, mid-overhead
- * entries in green - and keep the low overhead places
- * normal:
- */
- if (percent >= MIN_RED)
- color = PERF_COLOR_RED;
- else {
- if (percent > MIN_GREEN)
- color = PERF_COLOR_GREEN;
- }
- return color;
-}
-
-int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
-{
- int r;
- const char *color;
-
- color = get_percent_color(percent);
- r = color_fprintf(fp, color, fmt, percent);
-
- return r;
-}
-
-int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent)
-{
- const char *color = get_percent_color(percent);
- return color_snprintf(bf, size, color, fmt, percent);
-}
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h
deleted file mode 100644
index dea082b..0000000
--- a/tools/perf/util/color.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef __PERF_COLOR_H
-#define __PERF_COLOR_H
-
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-#define COLOR_MAXLEN 24
-
-#define PERF_COLOR_NORMAL ""
-#define PERF_COLOR_RESET "\033[m"
-#define PERF_COLOR_BOLD "\033[1m"
-#define PERF_COLOR_RED "\033[31m"
-#define PERF_COLOR_GREEN "\033[32m"
-#define PERF_COLOR_YELLOW "\033[33m"
-#define PERF_COLOR_BLUE "\033[34m"
-#define PERF_COLOR_MAGENTA "\033[35m"
-#define PERF_COLOR_CYAN "\033[36m"
-#define PERF_COLOR_BG_RED "\033[41m"
-
-#define MIN_GREEN 0.5
-#define MIN_RED 5.0
-
-/*
- * This variable stores the value of color.ui
- */
-extern int perf_use_color_default;
-
-
-/*
- * Use this instead of perf_default_config if you need the value of color.ui.
- */
-int perf_color_default_config(const char *var, const char *value, void *cb);
-
-int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
-void color_parse(const char *value, const char *var, char *dst);
-void color_parse_mem(const char *value, int len, const char *var, char *dst);
-int color_vsnprintf(char *bf, size_t size, const char *color,
- const char *fmt, va_list args);
-int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
-int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
-int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);
-int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
-int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent);
-int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
-const char *get_percent_color(double percent);
-
-#endif /* __PERF_COLOR_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
deleted file mode 100644
index bb2f5a0..0000000
--- a/tools/perf/util/config.c
+++ /dev/null
@@ -1,492 +0,0 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- * Copyright (C) Johannes Schindelin, 2005
- *
- */
-#include <lk/util.h>
-#include "cache.h"
-#include "exec_cmd.h"
-
-#define MAXNAME (256)
-
-#define DEBUG_CACHE_DIR ".debug"
-
-
-char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
-
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
-
-static const char *config_exclusive_filename;
-
-static int get_next_char(void)
-{
- int c;
- FILE *f;
-
- c = '\n';
- if ((f = config_file) != NULL) {
- c = fgetc(f);
- if (c == '\r') {
- /* DOS like systems */
- c = fgetc(f);
- if (c != '\n') {
- ungetc(c, f);
- c = '\r';
- }
- }
- if (c == '\n')
- config_linenr++;
- if (c == EOF) {
- config_file_eof = 1;
- c = '\n';
- }
- }
- return c;
-}
-
-static char *parse_value(void)
-{
- static char value[1024];
- int quote = 0, comment = 0, space = 0;
- size_t len = 0;
-
- for (;;) {
- int c = get_next_char();
-
- if (len >= sizeof(value) - 1)
- return NULL;
- if (c == '\n') {
- if (quote)
- return NULL;
- value[len] = 0;
- return value;
- }
- if (comment)
- continue;
- if (isspace(c) && !quote) {
- space = 1;
- continue;
- }
- if (!quote) {
- if (c == ';' || c == '#') {
- comment = 1;
- continue;
- }
- }
- if (space) {
- if (len)
- value[len++] = ' ';
- space = 0;
- }
- if (c == '\\') {
- c = get_next_char();
- switch (c) {
- case '\n':
- continue;
- case 't':
- c = '\t';
- break;
- case 'b':
- c = '\b';
- break;
- case 'n':
- c = '\n';
- break;
- /* Some characters escape as themselves */
- case '\\': case '"':
- break;
- /* Reject unknown escape sequences */
- default:
- return NULL;
- }
- value[len++] = c;
- continue;
- }
- if (c == '"') {
- quote = 1-quote;
- continue;
- }
- value[len++] = c;
- }
-}
-
-static inline int iskeychar(int c)
-{
- return isalnum(c) || c == '-';
-}
-
-static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
-{
- int c;
- char *value;
-
- /* Get the full name */
- for (;;) {
- c = get_next_char();
- if (config_file_eof)
- break;
- if (!iskeychar(c))
- break;
- name[len++] = c;
- if (len >= MAXNAME)
- return -1;
- }
- name[len] = 0;
- while (c == ' ' || c == '\t')
- c = get_next_char();
-
- value = NULL;
- if (c != '\n') {
- if (c != '=')
- return -1;
- value = parse_value();
- if (!value)
- return -1;
- }
- return fn(name, value, data);
-}
-
-static int get_extended_base_var(char *name, int baselen, int c)
-{
- do {
- if (c == '\n')
- return -1;
- c = get_next_char();
- } while (isspace(c));
-
- /* We require the format to be '[base "extension"]' */
- if (c != '"')
- return -1;
- name[baselen++] = '.';
-
- for (;;) {
- int ch = get_next_char();
-
- if (ch == '\n')
- return -1;
- if (ch == '"')
- break;
- if (ch == '\\') {
- ch = get_next_char();
- if (ch == '\n')
- return -1;
- }
- name[baselen++] = ch;
- if (baselen > MAXNAME / 2)
- return -1;
- }
-
- /* Final ']' */
- if (get_next_char() != ']')
- return -1;
- return baselen;
-}
-
-static int get_base_var(char *name)
-{
- int baselen = 0;
-
- for (;;) {
- int c = get_next_char();
- if (config_file_eof)
- return -1;
- if (c == ']')
- return baselen;
- if (isspace(c))
- return get_extended_base_var(name, baselen, c);
- if (!iskeychar(c) && c != '.')
- return -1;
- if (baselen > MAXNAME / 2)
- return -1;
- name[baselen++] = tolower(c);
- }
-}
-
-static int perf_parse_file(config_fn_t fn, void *data)
-{
- int comment = 0;
- int baselen = 0;
- static char var[MAXNAME];
-
- /* U+FEFF Byte Order Mark in UTF8 */
- static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
- const unsigned char *bomptr = utf8_bom;
-
- for (;;) {
- int c = get_next_char();
- if (bomptr && *bomptr) {
- /* We are at the file beginning; skip UTF8-encoded BOM
- * if present. Sane editors won't put this in on their
- * own, but e.g. Windows Notepad will do it happily. */
- if ((unsigned char) c == *bomptr) {
- bomptr++;
- continue;
- } else {
- /* Do not tolerate partial BOM. */
- if (bomptr != utf8_bom)
- break;
- /* No BOM at file beginning. Cool. */
- bomptr = NULL;
- }
- }
- if (c == '\n') {
- if (config_file_eof)
- return 0;
- comment = 0;
- continue;
- }
- if (comment || isspace(c))
- continue;
- if (c == '#' || c == ';') {
- comment = 1;
- continue;
- }
- if (c == '[') {
- baselen = get_base_var(var);
- if (baselen <= 0)
- break;
- var[baselen++] = '.';
- var[baselen] = 0;
- continue;
- }
- if (!isalpha(c))
- break;
- var[baselen] = tolower(c);
- if (get_value(fn, data, var, baselen+1) < 0)
- break;
- }
- die("bad config file line %d in %s", config_linenr, config_file_name);
-}
-
-static int parse_unit_factor(const char *end, unsigned long *val)
-{
- if (!*end)
- return 1;
- else if (!strcasecmp(end, "k")) {
- *val *= 1024;
- return 1;
- }
- else if (!strcasecmp(end, "m")) {
- *val *= 1024 * 1024;
- return 1;
- }
- else if (!strcasecmp(end, "g")) {
- *val *= 1024 * 1024 * 1024;
- return 1;
- }
- return 0;
-}
-
-static int perf_parse_long(const char *value, long *ret)
-{
- if (value && *value) {
- char *end;
- long val = strtol(value, &end, 0);
- unsigned long factor = 1;
- if (!parse_unit_factor(end, &factor))
- return 0;
- *ret = val * factor;
- return 1;
- }
- return 0;
-}
-
-static void die_bad_config(const char *name)
-{
- if (config_file_name)
- die("bad config value for '%s' in %s", name, config_file_name);
- die("bad config value for '%s'", name);
-}
-
-int perf_config_int(const char *name, const char *value)
-{
- long ret = 0;
- if (!perf_parse_long(value, &ret))
- die_bad_config(name);
- return ret;
-}
-
-static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
-{
- *is_bool = 1;
- if (!value)
- return 1;
- if (!*value)
- return 0;
- if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
- return 1;
- if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
- return 0;
- *is_bool = 0;
- return perf_config_int(name, value);
-}
-
-int perf_config_bool(const char *name, const char *value)
-{
- int discard;
- return !!perf_config_bool_or_int(name, value, &discard);
-}
-
-const char *perf_config_dirname(const char *name, const char *value)
-{
- if (!name)
- return NULL;
- return value;
-}
-
-static int perf_default_core_config(const char *var __used, const char *value __used)
-{
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-int perf_default_config(const char *var, const char *value, void *dummy __used)
-{
- if (!prefixcmp(var, "core."))
- return perf_default_core_config(var, value);
-
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
-}
-
-static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
-{
- int ret;
- FILE *f = fopen(filename, "r");
-
- ret = -1;
- if (f) {
- config_file = f;
- config_file_name = filename;
- config_linenr = 1;
- config_file_eof = 0;
- ret = perf_parse_file(fn, data);
- fclose(f);
- config_file_name = NULL;
- }
- return ret;
-}
-
-static const char *perf_etc_perfconfig(void)
-{
- static const char *system_wide;
- if (!system_wide)
- system_wide = system_path(ETC_PERFCONFIG);
- return system_wide;
-}
-
-static int perf_env_bool(const char *k, int def)
-{
- const char *v = getenv(k);
- return v ? perf_config_bool(k, v) : def;
-}
-
-static int perf_config_system(void)
-{
- return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
-}
-
-static int perf_config_global(void)
-{
- return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
-}
-
-int perf_config(config_fn_t fn, void *data)
-{
- int ret = 0, found = 0;
- char *repo_config = NULL;
- const char *home = NULL;
-
- /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
- if (config_exclusive_filename)
- return perf_config_from_file(fn, config_exclusive_filename, data);
- if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
- ret += perf_config_from_file(fn, perf_etc_perfconfig(),
- data);
- found += 1;
- }
-
- home = getenv("HOME");
- if (perf_config_global() && home) {
- char *user_config = strdup(mkpath("%s/.perfconfig", home));
- if (!access(user_config, R_OK)) {
- ret += perf_config_from_file(fn, user_config, data);
- found += 1;
- }
- free(user_config);
- }
-
- repo_config = perf_pathdup("config");
- if (!access(repo_config, R_OK)) {
- ret += perf_config_from_file(fn, repo_config, data);
- found += 1;
- }
- free(repo_config);
- if (found == 0)
- return -1;
- return ret;
-}
-
-/*
- * Call this to report error for your variable that should not
- * get a boolean value (i.e. "[my] var" means "true").
- */
-int config_error_nonbool(const char *var)
-{
- return error("Missing value for '%s'", var);
-}
-
-struct buildid_dir_config {
- char *dir;
-};
-
-static int buildid_dir_command_config(const char *var, const char *value,
- void *data)
-{
- struct buildid_dir_config *c = data;
- const char *v;
-
- /* same dir for all commands */
- if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
- v = perf_config_dirname(var, value);
- if (!v)
- return -1;
- strncpy(c->dir, v, MAXPATHLEN-1);
- c->dir[MAXPATHLEN-1] = '\0';
- }
- return 0;
-}
-
-static void check_buildid_dir_config(void)
-{
- struct buildid_dir_config c;
- c.dir = buildid_dir;
- perf_config(buildid_dir_command_config, &c);
-}
-
-void set_buildid_dir(void)
-{
- buildid_dir[0] = '\0';
-
- /* try config file */
- check_buildid_dir_config();
-
- /* default to $HOME/.debug */
- if (buildid_dir[0] == '\0') {
- char *v = getenv("HOME");
- if (v) {
- snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
- v, DEBUG_CACHE_DIR);
- } else {
- strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
- }
- buildid_dir[MAXPATHLEN-1] = '\0';
- }
- /* for communicating with external commands */
- setenv("PERF_BUILDID_DIR", buildid_dir, 1);
-}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 2b7ce22..a77dd3f 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include "cache.h"
-#include "color.h"
+#include <perf/color.h>
#include "event.h"
#include "debug.h"
#include <lk/util.h>
diff --git a/tools/perf/util/environment.c b/tools/perf/util/environment.c
index 275b0ee..9b1c819 100644
--- a/tools/perf/util/environment.c
+++ b/tools/perf/util/environment.c
@@ -6,4 +6,3 @@
#include "cache.h"
const char *pager_program;
-int pager_use_color = 1;
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c
index 67eeff5..21391fa 100644
--- a/tools/perf/util/exec_cmd.c
+++ b/tools/perf/util/exec_cmd.c
@@ -2,6 +2,8 @@
#include "exec_cmd.h"
#include "quote.h"
+#include <perf/config.h>
+
#include <string.h>
#define MAX_ARGS 32
@@ -165,3 +167,11 @@ int execl_perf_cmd(const char *cmd,...)
argv[argc] = NULL;
return execv_perf_cmd(argv);
}
+
+const char *perf_etc_perfconfig(void)
+{
+ static const char *system_wide;
+ if (!system_wide)
+ system_wide = system_path(ETC_PERFCONFIG);
+ return system_wide;
+}
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c
index 6f2975a..5098358 100644
--- a/tools/perf/util/help.c
+++ b/tools/perf/util/help.c
@@ -4,6 +4,8 @@
#include "levenshtein.h"
#include "help.h"
+#include <perf/config.h>
+
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
{
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
--git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 1915de2..f0455c3 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -2,13 +2,13 @@
#include "run-command.h"
#include "sigchain.h"
+#include <perf/config.h>
+
/*
* This is split up from the rest of git so that we can do
* something different on Windows.
*/
-static int spawned_pager;
-
static void pager_preexec(void)
{
/*
@@ -83,14 +83,3 @@ void setup_pager(void)
sigchain_push_common(wait_for_pager_signal);
atexit(wait_for_pager);
}
-
-int pager_in_use(void)
-{
- const char *env;
-
- if (spawned_pager)
- return 1;
-
- env = getenv("PERF_PAGER_IN_USE");
- return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0;
-}
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
deleted file mode 100644
index 58a470d..0000000
--- a/tools/perf/util/path.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * I'm tired of doing "vsnprintf()" etc just to open a
- * file, so here's a "return static buffer with printf"
- * interface for paths.
- *
- * It's obviously not thread-safe. Sue me. But it's quite
- * useful for doing things like
- *
- * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY);
- *
- * which is what it's designed for.
- */
-#include "cache.h"
-
-static char bad_path[] = "/bad-path/";
-/*
- * Two hacks:
- */
-
-static const char *get_perf_dir(void)
-{
- return ".";
-}
-
-size_t strlcpy(char *dest, const char *src, size_t size)
-{
- size_t ret = strlen(src);
-
- if (size) {
- size_t len = (ret >= size) ? size - 1 : ret;
- memcpy(dest, src, len);
- dest[len] = '\0';
- }
- return ret;
-}
-
-
-static char *get_pathname(void)
-{
- static char pathname_array[4][PATH_MAX];
- static int idx;
-
- return pathname_array[3 & ++idx];
-}
-
-static char *cleanup_path(char *path)
-{
- /* Clean it up */
- if (!memcmp(path, "./", 2)) {
- path += 2;
- while (*path == '/')
- path++;
- }
- return path;
-}
-
-static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
-{
- const char *perf_dir = get_perf_dir();
- size_t len;
-
- len = strlen(perf_dir);
- if (n < len + 1)
- goto bad;
- memcpy(buf, perf_dir, len);
- if (len && !is_dir_sep(perf_dir[len-1]))
- buf[len++] = '/';
- len += vsnprintf(buf + len, n - len, fmt, args);
- if (len >= n)
- goto bad;
- return cleanup_path(buf);
-bad:
- strlcpy(buf, bad_path, n);
- return buf;
-}
-
-char *perf_pathdup(const char *fmt, ...)
-{
- char path[PATH_MAX];
- va_list args;
- va_start(args, fmt);
- (void)perf_vsnpath(path, sizeof(path), fmt, args);
- va_end(args);
- return xstrdup(path);
-}
-
-char *mkpath(const char *fmt, ...)
-{
- va_list args;
- unsigned len;
- char *pathname = get_pathname();
-
- va_start(args, fmt);
- len = vsnprintf(pathname, PATH_MAX, fmt, args);
- va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
-}
-
-char *perf_path(const char *fmt, ...)
-{
- const char *perf_dir = get_perf_dir();
- char *pathname = get_pathname();
- va_list args;
- unsigned len;
-
- len = strlen(perf_dir);
- if (len > PATH_MAX-100)
- return bad_path;
- memcpy(pathname, perf_dir, len);
- if (len && perf_dir[len-1] != '/')
- pathname[len++] = '/';
- va_start(args, fmt);
- len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
- va_end(args);
- if (len >= PATH_MAX)
- return bad_path;
- return cleanup_path(pathname);
-}
-
-/* strip arbitrary amount of directory separators at end of path */
-static inline int chomp_trailing_dir_sep(const char *path, int len)
-{
- while (len && is_dir_sep(path[len - 1]))
- len--;
- return len;
-}
-
-/*
- * If path ends with suffix (complete path components), returns the
- * part before suffix (sans trailing directory separators).
- * Otherwise returns NULL.
- */
-char *strip_path_suffix(const char *path, const char *suffix)
-{
- int path_len = strlen(path), suffix_len = strlen(suffix);
-
- while (suffix_len) {
- if (!path_len)
- return NULL;
-
- if (is_dir_sep(path[path_len - 1])) {
- if (!is_dir_sep(suffix[suffix_len - 1]))
- return NULL;
- path_len = chomp_trailing_dir_sep(path, path_len);
- suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
- }
- else if (path[--path_len] != suffix[--suffix_len])
- return NULL;
- }
-
- if (path_len && !is_dir_sep(path[path_len - 1]))
- return NULL;
- return strndup(path, chomp_trailing_dir_sep(path, path_len));
-}
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 5f97d04..7377dff 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -41,7 +41,7 @@
#include "strlist.h"
#include "debug.h"
#include "cache.h"
-#include "color.h"
+#include <perf/color.h>
#include "symbol.h"
#include "thread.h"
#include "trace-event.h" /* For __unused */
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 4c015e9..b2c89b4 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -4,7 +4,7 @@
#include <lk/util.h>
-#include "color.h"
+#include <perf/color.h>
#include <linux/list.h>
#include "cache.h"
#include <lk/rbtree.h>
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
index 638a545..cd01eac 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/util/ui/browser.c
@@ -17,7 +17,7 @@
#include <sys/ttydefaults.h>
#include "browser.h"
#include "helpline.h"
-#include "../color.h"
+#include <perf/color.h>
#include "../util.h"
#if SLANG_VERSION < 20104
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 15/20] perf: Export strlist.ch
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (13 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 14/20] perf: Export color.ch and config.ch Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 16/20] perf: Export map.ch and symbol.ch Borislav Petkov
` (5 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Needed by other perf utils like map.c and symbol.c
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 2 +
tools/lib/lk/strlist.c | 200 ++++++++++++++++++++++++++++++++++++
tools/lib/lk/strlist.h | 79 ++++++++++++++
tools/perf/Makefile | 2 -
tools/perf/builtin-buildid-cache.c | 2 +-
tools/perf/builtin-probe.c | 2 +-
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/util/event.c | 2 +-
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-event.h | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/strlist.c | 200 ------------------------------------
tools/perf/util/strlist.h | 79 --------------
tools/perf/util/symbol.c | 2 +-
15 files changed, 290 insertions(+), 290 deletions(-)
create mode 100644 tools/lib/lk/strlist.c
create mode 100644 tools/lib/lk/strlist.h
delete mode 100644 tools/perf/util/strlist.c
delete mode 100644 tools/perf/util/strlist.h
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index d62e898..36395b1 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -14,6 +14,7 @@ LIB_H += bitops.h
LIB_H += bitmap.h
LIB_H += kernel.h
LIB_H += compiler.h
+LIB_H += strlist.h
LIB_OBJS += debugfs.o
LIB_OBJS += usage.o
@@ -23,6 +24,7 @@ LIB_OBJS += ctype.o
LIB_OBJS += rbtree.o
LIB_OBJS += hweight.o
LIB_OBJS += bitmap.o
+LIB_OBJS += strlist.o
LIBFILE = $(LIB_OUTPUT)lklib.a
diff --git a/tools/lib/lk/strlist.c b/tools/lib/lk/strlist.c
new file mode 100644
index 0000000..6783a20
--- /dev/null
+++ b/tools/lib/lk/strlist.c
@@ -0,0 +1,200 @@
+/*
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include "strlist.h"
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct str_node *str_node__new(const char *s, bool dupstr)
+{
+ struct str_node *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ if (dupstr) {
+ s = strdup(s);
+ if (s == NULL)
+ goto out_delete;
+ }
+ self->s = s;
+ }
+
+ return self;
+
+out_delete:
+ free(self);
+ return NULL;
+}
+
+static void str_node__delete(struct str_node *self, bool dupstr)
+{
+ if (dupstr)
+ free((void *)self->s);
+ free(self);
+}
+
+int strlist__add(struct strlist *self, const char *new_entry)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+ struct str_node *sn;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, new_entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ sn = str_node__new(new_entry, self->dupstr);
+ if (sn == NULL)
+ return -ENOMEM;
+
+ rb_link_node(&sn->rb_node, parent, p);
+ rb_insert_color(&sn->rb_node, &self->entries);
+ ++self->nr_entries;
+
+ return 0;
+}
+
+int strlist__load(struct strlist *self, const char *filename)
+{
+ char entry[1024];
+ int err;
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == NULL)
+ return errno;
+
+ while (fgets(entry, sizeof(entry), fp) != NULL) {
+ const size_t len = strlen(entry);
+
+ if (len == 0)
+ continue;
+ entry[len - 1] = '\0';
+
+ err = strlist__add(self, entry);
+ if (err != 0)
+ goto out;
+ }
+
+ err = 0;
+out:
+ fclose(fp);
+ return err;
+}
+
+void strlist__remove(struct strlist *self, struct str_node *sn)
+{
+ rb_erase(&sn->rb_node, &self->entries);
+ str_node__delete(sn, self->dupstr);
+}
+
+struct str_node *strlist__find(struct strlist *self, const char *entry)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ struct str_node *sn;
+ int rc;
+
+ parent = *p;
+ sn = rb_entry(parent, struct str_node, rb_node);
+ rc = strcmp(sn->s, entry);
+
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return sn;
+ }
+
+ return NULL;
+}
+
+static int strlist__parse_list_entry(struct strlist *self, const char *s)
+{
+ if (strncmp(s, "file://", 7) == 0)
+ return strlist__load(self, s + 7);
+
+ return strlist__add(self, s);
+}
+
+int strlist__parse_list(struct strlist *self, const char *s)
+{
+ char *sep;
+ int err;
+
+ while ((sep = strchr(s, ',')) != NULL) {
+ *sep = '\0';
+ err = strlist__parse_list_entry(self, s);
+ *sep = ',';
+ if (err != 0)
+ return err;
+ s = sep + 1;
+ }
+
+ return *s ? strlist__parse_list_entry(self, s) : 0;
+}
+
+struct strlist *strlist__new(bool dupstr, const char *slist)
+{
+ struct strlist *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->entries = RB_ROOT;
+ self->dupstr = dupstr;
+ self->nr_entries = 0;
+ if (slist && strlist__parse_list(self, slist) != 0)
+ goto out_error;
+ }
+
+ return self;
+out_error:
+ free(self);
+ return NULL;
+}
+
+void strlist__delete(struct strlist *self)
+{
+ if (self != NULL) {
+ struct str_node *pos;
+ struct rb_node *next = rb_first(&self->entries);
+
+ while (next) {
+ pos = rb_entry(next, struct str_node, rb_node);
+ next = rb_next(&pos->rb_node);
+ strlist__remove(self, pos);
+ }
+ self->entries = RB_ROOT;
+ free(self);
+ }
+}
+
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
+
+ if (!idx--)
+ return pos;
+ }
+
+ return NULL;
+}
diff --git a/tools/lib/lk/strlist.h b/tools/lib/lk/strlist.h
new file mode 100644
index 0000000..a21ba37
--- /dev/null
+++ b/tools/lib/lk/strlist.h
@@ -0,0 +1,79 @@
+#ifndef __LK_STRLIST_H
+#define __LK_STRLIST_H
+
+#include <lk/kernel.h>
+#include <lk/rbtree.h>
+#include <stdbool.h>
+
+struct str_node {
+ struct rb_node rb_node;
+ const char *s;
+};
+
+struct strlist {
+ struct rb_root entries;
+ unsigned int nr_entries;
+ bool dupstr;
+};
+
+struct strlist *strlist__new(bool dupstr, const char *slist);
+void strlist__delete(struct strlist *self);
+
+void strlist__remove(struct strlist *self, struct str_node *sn);
+int strlist__load(struct strlist *self, const char *filename);
+int strlist__add(struct strlist *self, const char *str);
+
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
+struct str_node *strlist__find(struct strlist *self, const char *entry);
+
+static inline bool strlist__has_entry(struct strlist *self, const char *entry)
+{
+ return strlist__find(self, entry) != NULL;
+}
+
+static inline bool strlist__empty(const struct strlist *self)
+{
+ return self->nr_entries == 0;
+}
+
+static inline unsigned int strlist__nr_entries(const struct strlist *self)
+{
+ return self->nr_entries;
+}
+
+/* For strlist iteration */
+static inline struct str_node *strlist__first(struct strlist *self)
+{
+ struct rb_node *rn = rb_first(&self->entries);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+static inline struct str_node *strlist__next(struct str_node *sn)
+{
+ struct rb_node *rn;
+ if (!sn)
+ return NULL;
+ rn = rb_next(&sn->rb_node);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+
+/**
+ * strlist_for_each - iterate over a strlist
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each(pos, self) \
+ for (pos = strlist__first(self); pos; pos = strlist__next(pos))
+
+/**
+ * strlist_for_each_safe - iterate over a strlist safe against removal of
+ * str_node
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @n: another &struct str_node to use as temporary storage.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each_safe(pos, n, self) \
+ for (pos = strlist__first(self), n = strlist__next(pos); pos;\
+ pos = n, n = strlist__next(n))
+
+int strlist__parse_list(struct strlist *self, const char *s);
+#endif /* __LK_STRLIST_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 9af0817..66fd9c2 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -364,7 +364,6 @@ LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
LIB_H += util/strbuf.h
-LIB_H += util/strlist.h
LIB_H += util/svghelper.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
@@ -392,7 +391,6 @@ LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
LIB_OBJS += $(OUTPUT)util/string.o
-LIB_OBJS += $(OUTPUT)util/strlist.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/symbol.o
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 29ad20e..43e4faa 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -12,7 +12,7 @@
#include "util/debug.h"
#include "util/header.h"
#include "util/parse-options.h"
-#include "util/strlist.h"
+#include <lk/strlist.h>
#include "util/symbol.h"
static char const *add_name_list_str, *remove_name_list_str;
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index a762878..9303cab 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -37,7 +37,7 @@
#include "perf.h"
#include "builtin.h"
-#include "util/strlist.h"
+#include <lk/strlist.h>
#include "util/symbol.h"
#include "util/debug.h"
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c4e50e4..2fa7635 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -15,7 +15,7 @@
#include <lk/rbtree.h>
#include "util/symbol.h"
#include "util/callchain.h"
-#include "util/strlist.h"
+#include <lk/strlist.h>
#include "util/values.h"
#include "perf.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 35678a5..c0e0dab 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -22,7 +22,7 @@
#include <lk/rbtree.h>
#include "util/symbol.h"
#include "util/callchain.h"
-#include "util/strlist.h"
+#include <lk/strlist.h>
#include "perf.h"
#include "util/header.h"
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index dab9e75..09ff091 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -4,7 +4,7 @@
#include "session.h"
#include "sort.h"
#include "string.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include "thread.h"
const char *event__name[] = {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 7377dff..a6c8d3e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -38,7 +38,7 @@
#include "event.h"
#include "string.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include "debug.h"
#include "cache.h"
#include <perf/color.h>
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af3924..2852634 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -2,7 +2,7 @@
#define _PROBE_EVENT_H
#include <stdbool.h>
-#include "strlist.h"
+#include <lk/strlist.h>
extern bool probe_event_dry_run;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index b2c89b4..e2fb61a 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -11,7 +11,7 @@
#include "symbol.h"
#include "string.h"
#include "callchain.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include "values.h"
#include "../perf.h"
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
deleted file mode 100644
index 6783a20..0000000
--- a/tools/perf/util/strlist.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Licensed under the GPLv2.
- */
-
-#include "strlist.h"
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-static struct str_node *str_node__new(const char *s, bool dupstr)
-{
- struct str_node *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- if (dupstr) {
- s = strdup(s);
- if (s == NULL)
- goto out_delete;
- }
- self->s = s;
- }
-
- return self;
-
-out_delete:
- free(self);
- return NULL;
-}
-
-static void str_node__delete(struct str_node *self, bool dupstr)
-{
- if (dupstr)
- free((void *)self->s);
- free(self);
-}
-
-int strlist__add(struct strlist *self, const char *new_entry)
-{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
- struct str_node *sn;
-
- while (*p != NULL) {
- int rc;
-
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, new_entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return -EEXIST;
- }
-
- sn = str_node__new(new_entry, self->dupstr);
- if (sn == NULL)
- return -ENOMEM;
-
- rb_link_node(&sn->rb_node, parent, p);
- rb_insert_color(&sn->rb_node, &self->entries);
- ++self->nr_entries;
-
- return 0;
-}
-
-int strlist__load(struct strlist *self, const char *filename)
-{
- char entry[1024];
- int err;
- FILE *fp = fopen(filename, "r");
-
- if (fp == NULL)
- return errno;
-
- while (fgets(entry, sizeof(entry), fp) != NULL) {
- const size_t len = strlen(entry);
-
- if (len == 0)
- continue;
- entry[len - 1] = '\0';
-
- err = strlist__add(self, entry);
- if (err != 0)
- goto out;
- }
-
- err = 0;
-out:
- fclose(fp);
- return err;
-}
-
-void strlist__remove(struct strlist *self, struct str_node *sn)
-{
- rb_erase(&sn->rb_node, &self->entries);
- str_node__delete(sn, self->dupstr);
-}
-
-struct str_node *strlist__find(struct strlist *self, const char *entry)
-{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
-
- while (*p != NULL) {
- struct str_node *sn;
- int rc;
-
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return sn;
- }
-
- return NULL;
-}
-
-static int strlist__parse_list_entry(struct strlist *self, const char *s)
-{
- if (strncmp(s, "file://", 7) == 0)
- return strlist__load(self, s + 7);
-
- return strlist__add(self, s);
-}
-
-int strlist__parse_list(struct strlist *self, const char *s)
-{
- char *sep;
- int err;
-
- while ((sep = strchr(s, ',')) != NULL) {
- *sep = '\0';
- err = strlist__parse_list_entry(self, s);
- *sep = ',';
- if (err != 0)
- return err;
- s = sep + 1;
- }
-
- return *s ? strlist__parse_list_entry(self, s) : 0;
-}
-
-struct strlist *strlist__new(bool dupstr, const char *slist)
-{
- struct strlist *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- self->entries = RB_ROOT;
- self->dupstr = dupstr;
- self->nr_entries = 0;
- if (slist && strlist__parse_list(self, slist) != 0)
- goto out_error;
- }
-
- return self;
-out_error:
- free(self);
- return NULL;
-}
-
-void strlist__delete(struct strlist *self)
-{
- if (self != NULL) {
- struct str_node *pos;
- struct rb_node *next = rb_first(&self->entries);
-
- while (next) {
- pos = rb_entry(next, struct str_node, rb_node);
- next = rb_next(&pos->rb_node);
- strlist__remove(self, pos);
- }
- self->entries = RB_ROOT;
- free(self);
- }
-}
-
-struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
-{
- struct rb_node *nd;
-
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
-
- if (!idx--)
- return pos;
- }
-
- return NULL;
-}
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
deleted file mode 100644
index 964da20..0000000
--- a/tools/perf/util/strlist.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef __PERF_STRLIST_H
-#define __PERF_STRLIST_H
-
-#include <lk/kernel.h>
-#include <lk/rbtree.h>
-#include <stdbool.h>
-
-struct str_node {
- struct rb_node rb_node;
- const char *s;
-};
-
-struct strlist {
- struct rb_root entries;
- unsigned int nr_entries;
- bool dupstr;
-};
-
-struct strlist *strlist__new(bool dupstr, const char *slist);
-void strlist__delete(struct strlist *self);
-
-void strlist__remove(struct strlist *self, struct str_node *sn);
-int strlist__load(struct strlist *self, const char *filename);
-int strlist__add(struct strlist *self, const char *str);
-
-struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
-struct str_node *strlist__find(struct strlist *self, const char *entry);
-
-static inline bool strlist__has_entry(struct strlist *self, const char *entry)
-{
- return strlist__find(self, entry) != NULL;
-}
-
-static inline bool strlist__empty(const struct strlist *self)
-{
- return self->nr_entries == 0;
-}
-
-static inline unsigned int strlist__nr_entries(const struct strlist *self)
-{
- return self->nr_entries;
-}
-
-/* For strlist iteration */
-static inline struct str_node *strlist__first(struct strlist *self)
-{
- struct rb_node *rn = rb_first(&self->entries);
- return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
-}
-static inline struct str_node *strlist__next(struct str_node *sn)
-{
- struct rb_node *rn;
- if (!sn)
- return NULL;
- rn = rb_next(&sn->rb_node);
- return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
-}
-
-/**
- * strlist_for_each - iterate over a strlist
- * @pos: the &struct str_node to use as a loop cursor.
- * @self: the &struct strlist for loop.
- */
-#define strlist__for_each(pos, self) \
- for (pos = strlist__first(self); pos; pos = strlist__next(pos))
-
-/**
- * strlist_for_each_safe - iterate over a strlist safe against removal of
- * str_node
- * @pos: the &struct str_node to use as a loop cursor.
- * @n: another &struct str_node to use as temporary storage.
- * @self: the &struct strlist for loop.
- */
-#define strlist__for_each_safe(pos, n, self) \
- for (pos = strlist__first(self), n = strlist__next(pos); pos;\
- pos = n, n = strlist__next(n))
-
-int strlist__parse_list(struct strlist *self, const char *s);
-#endif /* __PERF_STRLIST_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b2f5ae9..0319325 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -14,7 +14,7 @@
#include "build-id.h"
#include "debug.h"
#include "symbol.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include <libelf.h>
#include <gelf.h>
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 16/20] perf: Export map.ch and symbol.ch
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (14 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 15/20] perf: Export strlist.ch Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 17/20] perf: Export trace parsing utils Borislav Petkov
` (4 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Those suck in a bunch of other stuff. Put objects shared between
perf/util/ and lib/perf/ in lib/perf/config.ch for now, will go in
lib/perf/shared.ch later.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/util.h | 2 +
tools/lib/perf/Makefile | 4 +
tools/lib/perf/config.c | 3 +
tools/lib/perf/config.h | 1 +
tools/lib/perf/map.c | 682 +++++++++
tools/lib/perf/map.h | 229 +++
tools/lib/perf/symbol.c | 2466 ++++++++++++++++++++++++++++++++
tools/lib/perf/symbol.h | 233 +++
tools/perf/Makefile | 4 -
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-buildid-cache.c | 3 +-
tools/perf/builtin-buildid-list.c | 2 +-
tools/perf/builtin-diff.c | 2 +-
tools/perf/builtin-inject.c | 2 +-
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-kvm.c | 2 +-
tools/perf/builtin-lock.c | 2 +-
tools/perf/builtin-probe.c | 3 +-
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-sched.c | 2 +-
tools/perf/builtin-stat.c | 1 +
tools/perf/builtin-test.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 2 +-
tools/perf/util/build-id.c | 2 +-
tools/perf/util/build-id.h | 2 -
tools/perf/util/callchain.h | 2 +-
tools/perf/util/debug.c | 2 +-
tools/perf/util/debug.h | 1 -
tools/perf/util/event.h | 2 +-
| 2 +-
tools/perf/util/hist.h | 1 +
tools/perf/util/map.c | 682 ---------
tools/perf/util/map.h | 229 ---
tools/perf/util/parse-events.c | 2 +-
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-finder.c | 2 +-
tools/perf/util/session.h | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/symbol.c | 2459 -------------------------------
tools/perf/util/symbol.h | 231 ---
tools/perf/util/thread.c | 1 +
tools/perf/util/thread.h | 2 +-
tools/perf/util/ui/browsers/annotate.c | 2 +-
tools/perf/util/ui/browsers/hists.c | 2 +-
tools/perf/util/ui/browsers/map.c | 4 +-
48 files changed, 3656 insertions(+), 3639 deletions(-)
create mode 100644 tools/lib/perf/map.c
create mode 100644 tools/lib/perf/map.h
create mode 100644 tools/lib/perf/symbol.c
create mode 100644 tools/lib/perf/symbol.h
delete mode 100644 tools/perf/util/map.c
delete mode 100644 tools/perf/util/map.h
delete mode 100644 tools/perf/util/symbol.c
delete mode 100644 tools/perf/util/symbol.h
diff --git a/tools/lib/lk/util.h b/tools/lib/lk/util.h
index f380fed..23f3e61 100644
--- a/tools/lib/lk/util.h
+++ b/tools/lib/lk/util.h
@@ -40,7 +40,9 @@
#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
#define _ALL_SOURCE 1
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
+#endif
#define _BSD_SOURCE 1
#define HAS_BOOL
diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
index 7709a46..8b4d9c0 100644
--- a/tools/lib/perf/Makefile
+++ b/tools/lib/perf/Makefile
@@ -8,11 +8,15 @@ LIB_H += mmap.h
LIB_H += util.h
LIB_H += color.h
LIB_H += config.h
+LIB_H += map.h
+LIB_H += symbol.h
LIB_OBJS += mmap.o
LIB_OBJS += color.o
LIB_OBJS += config.o
LIB_OBJS += path.o
+LIB_OBJS += map.o
+LIB_OBJS += symbol.o
LIBFILE = $(LIB_OUTPUT)lkperflib.a
diff --git a/tools/lib/perf/config.c b/tools/lib/perf/config.c
index bb72a55..f56646f 100644
--- a/tools/lib/perf/config.c
+++ b/tools/lib/perf/config.c
@@ -18,6 +18,9 @@ int pager_use_color = 1;
char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
+/* debug.c */
+int verbose = 0;
+
static FILE *config_file;
static const char *config_file_name;
static int config_linenr;
diff --git a/tools/lib/perf/config.h b/tools/lib/perf/config.h
index 5ef9c1d..edcb8ee 100644
--- a/tools/lib/perf/config.h
+++ b/tools/lib/perf/config.h
@@ -7,6 +7,7 @@
#include <lk/util.h>
extern int spawned_pager;
+extern int verbose;
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *);
diff --git a/tools/lib/perf/map.c b/tools/lib/perf/map.c
new file mode 100644
index 0000000..3a7eb6e
--- /dev/null
+++ b/tools/lib/perf/map.c
@@ -0,0 +1,682 @@
+#include "symbol.h"
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "map.h"
+
+const char *map_type__name[MAP__NR_TYPES] = {
+ [MAP__FUNCTION] = "Functions",
+ [MAP__VARIABLE] = "Variables",
+};
+
+static inline int is_anon_memory(const char *filename)
+{
+ return strcmp(filename, "//anon") == 0;
+}
+
+void map__init(struct map *self, enum map_type type,
+ u64 start, u64 end, u64 pgoff, struct dso *dso)
+{
+ self->type = type;
+ self->start = start;
+ self->end = end;
+ self->pgoff = pgoff;
+ self->dso = dso;
+ self->map_ip = map__map_ip;
+ self->unmap_ip = map__unmap_ip;
+ RB_CLEAR_NODE(&self->rb_node);
+ self->groups = NULL;
+ self->referenced = false;
+}
+
+struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
+ u64 pgoff, u32 pid, char *filename,
+ enum map_type type)
+{
+ struct map *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ char newfilename[PATH_MAX];
+ struct dso *dso;
+ int anon;
+
+ anon = is_anon_memory(filename);
+
+ if (anon) {
+ snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
+ filename = newfilename;
+ }
+
+ dso = __dsos__findnew(dsos__list, filename);
+ if (dso == NULL)
+ goto out_delete;
+
+ map__init(self, type, start, start + len, pgoff, dso);
+
+ if (anon) {
+set_identity:
+ self->map_ip = self->unmap_ip = identity__map_ip;
+ } else if (strcmp(filename, "[vdso]") == 0) {
+ dso__set_loaded(dso, self->type);
+ goto set_identity;
+ }
+ }
+ return self;
+out_delete:
+ free(self);
+ return NULL;
+}
+
+void map__delete(struct map *self)
+{
+ free(self);
+}
+
+void map__fixup_start(struct map *self)
+{
+ struct rb_root *symbols = &self->dso->symbols[self->type];
+ struct rb_node *nd = rb_first(symbols);
+ if (nd != NULL) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ self->start = sym->start;
+ }
+}
+
+void map__fixup_end(struct map *self)
+{
+ struct rb_root *symbols = &self->dso->symbols[self->type];
+ struct rb_node *nd = rb_last(symbols);
+ if (nd != NULL) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ self->end = sym->end;
+ }
+}
+
+#define DSO__DELETED "(deleted)"
+
+int map__load(struct map *self, symbol_filter_t filter)
+{
+ const char *name = self->dso->long_name;
+ int nr;
+
+ if (dso__loaded(self->dso, self->type))
+ return 0;
+
+ nr = dso__load(self->dso, self, filter);
+ if (nr < 0) {
+ if (self->dso->has_build_id) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->dso->build_id,
+ sizeof(self->dso->build_id),
+ sbuild_id);
+ pr_warning("%s with build id %s not found",
+ name, sbuild_id);
+ } else
+ pr_warning("Failed to open %s", name);
+
+ pr_warning(", continuing without symbols\n");
+ return -1;
+ } else if (nr == 0) {
+ const size_t len = strlen(name);
+ const size_t real_len = len - sizeof(DSO__DELETED);
+
+ if (len > sizeof(DSO__DELETED) &&
+ strcmp(name + real_len + 1, DSO__DELETED) == 0) {
+ pr_warning("%.*s was updated, restart the long "
+ "running apps that use it!\n",
+ (int)real_len, name);
+ } else {
+ pr_warning("no symbols found in %s, maybe install "
+ "a debug package?\n", name);
+ }
+
+ return -1;
+ }
+ /*
+ * Only applies to the kernel, as its symtabs aren't relative like the
+ * module ones.
+ */
+ if (self->dso->kernel)
+ map__reloc_vmlinux(self);
+
+ return 0;
+}
+
+struct symbol *map__find_symbol(struct map *self, u64 addr,
+ symbol_filter_t filter)
+{
+ if (map__load(self, filter) < 0)
+ return NULL;
+
+ return dso__find_symbol(self->dso, self->type, addr);
+}
+
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ symbol_filter_t filter)
+{
+ if (map__load(self, filter) < 0)
+ return NULL;
+
+ if (!dso__sorted_by_name(self->dso, self->type))
+ dso__sort_by_name(self->dso, self->type);
+
+ return dso__find_symbol_by_name(self->dso, self->type, name);
+}
+
+struct map *map__clone(struct map *self)
+{
+ struct map *map = malloc(sizeof(*self));
+
+ if (!map)
+ return NULL;
+
+ memcpy(map, self, sizeof(*self));
+
+ return map;
+}
+
+int map__overlap(struct map *l, struct map *r)
+{
+ if (l->start > r->start) {
+ struct map *t = l;
+ l = r;
+ r = t;
+ }
+
+ if (l->end > r->start)
+ return 1;
+
+ return 0;
+}
+
+size_t map__fprintf(struct map *self, FILE *fp)
+{
+ return fprintf(fp, " %Lx-%Lx %Lx %s\n",
+ self->start, self->end, self->pgoff, self->dso->name);
+}
+
+/*
+ * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
+ * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
+ */
+u64 map__rip_2objdump(struct map *map, u64 rip)
+{
+ u64 addr = map->dso->adjust_symbols ?
+ map->unmap_ip(map, rip) : /* RIP -> IP */
+ rip;
+ return addr;
+}
+
+u64 map__objdump_2ip(struct map *map, u64 addr)
+{
+ u64 ip = map->dso->adjust_symbols ?
+ addr :
+ map->unmap_ip(map, addr); /* RIP -> IP */
+ return ip;
+}
+
+void map_groups__init(struct map_groups *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ self->maps[i] = RB_ROOT;
+ INIT_LIST_HEAD(&self->removed_maps[i]);
+ }
+ self->machine = NULL;
+}
+
+static void maps__delete(struct rb_root *self)
+{
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ map__delete(pos);
+ }
+}
+
+static void maps__delete_removed(struct list_head *self)
+{
+ struct map *pos, *n;
+
+ list_for_each_entry_safe(pos, n, self, node) {
+ list_del(&pos->node);
+ map__delete(pos);
+ }
+}
+
+void map_groups__exit(struct map_groups *self)
+{
+ int i;
+
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ maps__delete(&self->maps[i]);
+ maps__delete_removed(&self->removed_maps[i]);
+ }
+}
+
+void map_groups__flush(struct map_groups *self)
+{
+ int type;
+
+ for (type = 0; type < MAP__NR_TYPES; type++) {
+ struct rb_root *root = &self->maps[type];
+ struct rb_node *next = rb_first(root);
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, root);
+ /*
+ * We may have references to this map, for
+ * instance in some hist_entry instances, so
+ * just move them to a separate list.
+ */
+ list_add_tail(&pos->node, &self->removed_maps[pos->type]);
+ }
+ }
+}
+
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ struct map *map = map_groups__find(self, type, addr);
+
+ if (map != NULL) {
+ if (mapp != NULL)
+ *mapp = map;
+ return map__find_symbol(map, map->map_ip(map, addr), filter);
+ }
+
+ return NULL;
+}
+
+struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
+ enum map_type type,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+ struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
+
+ if (sym == NULL)
+ continue;
+ if (mapp != NULL)
+ *mapp = pos;
+ return sym;
+ }
+
+ return NULL;
+}
+
+size_t __map_groups__fprintf_maps(struct map_groups *self,
+ enum map_type type, int verbose, FILE *fp)
+{
+ size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+ printed += fprintf(fp, "Map:");
+ printed += map__fprintf(pos, fp);
+ if (verbose > 2) {
+ printed += dso__fprintf(pos->dso, type, fp);
+ printed += fprintf(fp, "--\n");
+ }
+ }
+
+ return printed;
+}
+
+size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp)
+{
+ size_t printed = 0, i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ printed += __map_groups__fprintf_maps(self, i, verbose, fp);
+ return printed;
+}
+
+static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
+ enum map_type type,
+ int verbose, FILE *fp)
+{
+ struct map *pos;
+ size_t printed = 0;
+
+ list_for_each_entry(pos, &self->removed_maps[type], node) {
+ printed += fprintf(fp, "Map:");
+ printed += map__fprintf(pos, fp);
+ if (verbose > 1) {
+ printed += dso__fprintf(pos->dso, type, fp);
+ printed += fprintf(fp, "--\n");
+ }
+ }
+ return printed;
+}
+
+static size_t map_groups__fprintf_removed_maps(struct map_groups *self,
+ int verbose, FILE *fp)
+{
+ size_t printed = 0, i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp);
+ return printed;
+}
+
+size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp)
+{
+ size_t printed = map_groups__fprintf_maps(self, verbose, fp);
+ printed += fprintf(fp, "Removed maps:\n");
+ return printed + map_groups__fprintf_removed_maps(self, verbose, fp);
+}
+
+int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
+ int verbose, FILE *fp)
+{
+ struct rb_root *root = &self->maps[map->type];
+ struct rb_node *next = rb_first(root);
+ int err = 0;
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ if (!map__overlap(pos, map))
+ continue;
+
+ if (verbose >= 2) {
+ fputs("overlapping maps:\n", fp);
+ map__fprintf(map, fp);
+ map__fprintf(pos, fp);
+ }
+
+ rb_erase(&pos->rb_node, root);
+ /*
+ * Now check if we need to create new maps for areas not
+ * overlapped by the new map:
+ */
+ if (map->start > pos->start) {
+ struct map *before = map__clone(pos);
+
+ if (before == NULL) {
+ err = -ENOMEM;
+ goto move_map;
+ }
+
+ before->end = map->start - 1;
+ map_groups__insert(self, before);
+ if (verbose >= 2)
+ map__fprintf(before, fp);
+ }
+
+ if (map->end < pos->end) {
+ struct map *after = map__clone(pos);
+
+ if (after == NULL) {
+ err = -ENOMEM;
+ goto move_map;
+ }
+
+ after->start = map->end + 1;
+ map_groups__insert(self, after);
+ if (verbose >= 2)
+ map__fprintf(after, fp);
+ }
+move_map:
+ /*
+ * If we have references, just move them to a separate list.
+ */
+ if (pos->referenced)
+ list_add_tail(&pos->node, &self->removed_maps[map->type]);
+ else
+ map__delete(pos);
+
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * XXX This should not really _copy_ te maps, but refcount them.
+ */
+int map_groups__clone(struct map_groups *self,
+ struct map_groups *parent, enum map_type type)
+{
+ struct rb_node *nd;
+ for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
+ struct map *new = map__clone(map);
+ if (new == NULL)
+ return -ENOMEM;
+ map_groups__insert(self, new);
+ }
+ return 0;
+}
+
+static u64 map__reloc_map_ip(struct map *map, u64 ip)
+{
+ return ip + (s64)map->pgoff;
+}
+
+static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
+{
+ return ip - (s64)map->pgoff;
+}
+
+void map__reloc_vmlinux(struct map *self)
+{
+ struct kmap *kmap = map__kmap(self);
+ s64 reloc;
+
+ if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
+ return;
+
+ reloc = (kmap->ref_reloc_sym->unrelocated_addr -
+ kmap->ref_reloc_sym->addr);
+
+ if (!reloc)
+ return;
+
+ self->map_ip = map__reloc_map_ip;
+ self->unmap_ip = map__reloc_unmap_ip;
+ self->pgoff = reloc;
+}
+
+void maps__insert(struct rb_root *maps, struct map *map)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = map->start;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&map->rb_node, parent, p);
+ rb_insert_color(&map->rb_node, maps);
+}
+
+void maps__remove(struct rb_root *self, struct map *map)
+{
+ rb_erase(&map->rb_node, self);
+}
+
+struct map *maps__find(struct rb_root *maps, u64 ip)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else if (ip > m->end)
+ p = &(*p)->rb_right;
+ else
+ return m;
+ }
+
+ return NULL;
+}
+
+int machine__init(struct machine *self, const char *root_dir, pid_t pid)
+{
+ map_groups__init(&self->kmaps);
+ RB_CLEAR_NODE(&self->rb_node);
+ INIT_LIST_HEAD(&self->user_dsos);
+ INIT_LIST_HEAD(&self->kernel_dsos);
+
+ self->kmaps.machine = self;
+ self->pid = pid;
+ self->root_dir = strdup(root_dir);
+ return self->root_dir == NULL ? -ENOMEM : 0;
+}
+
+static void dsos__delete(struct list_head *self)
+{
+ struct dso *pos, *n;
+
+ list_for_each_entry_safe(pos, n, self, node) {
+ list_del(&pos->node);
+ dso__delete(pos);
+ }
+}
+
+void machine__exit(struct machine *self)
+{
+ map_groups__exit(&self->kmaps);
+ dsos__delete(&self->user_dsos);
+ dsos__delete(&self->kernel_dsos);
+ free(self->root_dir);
+ self->root_dir = NULL;
+}
+
+void machine__delete(struct machine *self)
+{
+ machine__exit(self);
+ free(self);
+}
+
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct machine *pos, *machine = malloc(sizeof(*machine));
+
+ if (!machine)
+ return NULL;
+
+ if (machine__init(machine, root_dir, pid) != 0) {
+ free(machine);
+ return NULL;
+ }
+
+ while (*p != NULL) {
+ parent = *p;
+ pos = rb_entry(parent, struct machine, rb_node);
+ if (pid < pos->pid)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&machine->rb_node, parent, p);
+ rb_insert_color(&machine->rb_node, self);
+
+ return machine;
+}
+
+struct machine *machines__find(struct rb_root *self, pid_t pid)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct machine *machine;
+ struct machine *default_machine = NULL;
+
+ while (*p != NULL) {
+ parent = *p;
+ machine = rb_entry(parent, struct machine, rb_node);
+ if (pid < machine->pid)
+ p = &(*p)->rb_left;
+ else if (pid > machine->pid)
+ p = &(*p)->rb_right;
+ else
+ return machine;
+ if (!machine->pid)
+ default_machine = machine;
+ }
+
+ return default_machine;
+}
+
+struct machine *machines__findnew(struct rb_root *self, pid_t pid)
+{
+ char path[PATH_MAX];
+ const char *root_dir;
+ struct machine *machine = machines__find(self, pid);
+
+ if (!machine || machine->pid != pid) {
+ if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
+ root_dir = "";
+ else {
+ if (!symbol_conf.guestmount)
+ goto out;
+ sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
+ if (access(path, R_OK)) {
+ pr_err("Can't access file %s\n", path);
+ goto out;
+ }
+ root_dir = path;
+ }
+ machine = machines__add(self, pid, root_dir);
+ }
+
+out:
+ return machine;
+}
+
+void machines__process(struct rb_root *self, machine__process_t process, void *data)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ process(pos, data);
+ }
+}
+
+char *machine__mmap_name(struct machine *self, char *bf, size_t size)
+{
+ if (machine__is_host(self))
+ snprintf(bf, size, "[%s]", "kernel.kallsyms");
+ else if (machine__is_default_guest(self))
+ snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
+ else
+ snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
+
+ return bf;
+}
diff --git a/tools/lib/perf/map.h b/tools/lib/perf/map.h
new file mode 100644
index 0000000..efbff5b
--- /dev/null
+++ b/tools/lib/perf/map.h
@@ -0,0 +1,229 @@
+#ifndef __PERF_MAP_H
+#define __PERF_MAP_H
+
+#include <lk/compiler.h>
+#include <linux/list.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <lk/types.h>
+#include <lk/rbtree.h>
+#include <lk/kernel.h>
+
+enum map_type {
+ MAP__FUNCTION = 0,
+ MAP__VARIABLE,
+};
+
+#define MAP__NR_TYPES (MAP__VARIABLE + 1)
+
+extern const char *map_type__name[MAP__NR_TYPES];
+
+struct dso;
+struct ref_reloc_sym;
+struct map_groups;
+struct machine;
+
+struct map {
+ union {
+ struct rb_node rb_node;
+ struct list_head node;
+ };
+ u64 start;
+ u64 end;
+ u8 /* enum map_type */ type;
+ bool referenced;
+ u32 priv;
+ u64 pgoff;
+
+ /* ip -> dso rip */
+ u64 (*map_ip)(struct map *, u64);
+ /* dso rip -> ip */
+ u64 (*unmap_ip)(struct map *, u64);
+
+ struct dso *dso;
+ struct map_groups *groups;
+};
+
+struct kmap {
+ struct ref_reloc_sym *ref_reloc_sym;
+ struct map_groups *kmaps;
+};
+
+struct map_groups {
+ struct rb_root maps[MAP__NR_TYPES];
+ struct list_head removed_maps[MAP__NR_TYPES];
+ struct machine *machine;
+};
+
+/* Native host kernel uses -1 as pid index in machine */
+#define HOST_KERNEL_ID (-1)
+#define DEFAULT_GUEST_KERNEL_ID (0)
+
+struct machine {
+ struct rb_node rb_node;
+ pid_t pid;
+ char *root_dir;
+ struct list_head user_dsos;
+ struct list_head kernel_dsos;
+ struct map_groups kmaps;
+ struct map *vmlinux_maps[MAP__NR_TYPES];
+};
+
+static inline
+struct map *machine__kernel_map(struct machine *self, enum map_type type)
+{
+ return self->vmlinux_maps[type];
+}
+
+static inline struct kmap *map__kmap(struct map *self)
+{
+ return (struct kmap *)(self + 1);
+}
+
+static inline u64 map__map_ip(struct map *map, u64 ip)
+{
+ return ip - map->start + map->pgoff;
+}
+
+static inline u64 map__unmap_ip(struct map *map, u64 ip)
+{
+ return ip + map->start - map->pgoff;
+}
+
+static inline u64 identity__map_ip(struct map *map __used, u64 ip)
+{
+ return ip;
+}
+
+
+/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
+u64 map__rip_2objdump(struct map *map, u64 rip);
+u64 map__objdump_2ip(struct map *map, u64 addr);
+
+struct symbol;
+
+typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
+
+void map__init(struct map *self, enum map_type type,
+ u64 start, u64 end, u64 pgoff, struct dso *dso);
+struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
+ u64 pgoff, u32 pid, char *filename,
+ enum map_type type);
+void map__delete(struct map *self);
+struct map *map__clone(struct map *self);
+int map__overlap(struct map *l, struct map *r);
+size_t map__fprintf(struct map *self, FILE *fp);
+
+int map__load(struct map *self, symbol_filter_t filter);
+struct symbol *map__find_symbol(struct map *self,
+ u64 addr, symbol_filter_t filter);
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ symbol_filter_t filter);
+void map__fixup_start(struct map *self);
+void map__fixup_end(struct map *self);
+
+void map__reloc_vmlinux(struct map *self);
+
+size_t __map_groups__fprintf_maps(struct map_groups *self,
+ enum map_type type, int verbose, FILE *fp);
+void maps__insert(struct rb_root *maps, struct map *map);
+void maps__remove(struct rb_root *self, struct map *map);
+struct map *maps__find(struct rb_root *maps, u64 addr);
+void map_groups__init(struct map_groups *self);
+void map_groups__exit(struct map_groups *self);
+int map_groups__clone(struct map_groups *self,
+ struct map_groups *parent, enum map_type type);
+size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
+size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
+
+typedef void (*machine__process_t)(struct machine *self, void *data);
+
+void machines__process(struct rb_root *self, machine__process_t process, void *data);
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir);
+struct machine *machines__find_host(struct rb_root *self);
+struct machine *machines__find(struct rb_root *self, pid_t pid);
+struct machine *machines__findnew(struct rb_root *self, pid_t pid);
+char *machine__mmap_name(struct machine *self, char *bf, size_t size);
+int machine__init(struct machine *self, const char *root_dir, pid_t pid);
+void machine__exit(struct machine *self);
+void machine__delete(struct machine *self);
+
+/*
+ * Default guest kernel is defined by parameter --guestkallsyms
+ * and --guestmodules
+ */
+static inline bool machine__is_default_guest(struct machine *self)
+{
+ return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
+}
+
+static inline bool machine__is_host(struct machine *self)
+{
+ return self ? self->pid == HOST_KERNEL_ID : false;
+}
+
+static inline void map_groups__insert(struct map_groups *self, struct map *map)
+{
+ maps__insert(&self->maps[map->type], map);
+ map->groups = self;
+}
+
+static inline void map_groups__remove(struct map_groups *self, struct map *map)
+{
+ maps__remove(&self->maps[map->type], map);
+}
+
+static inline struct map *map_groups__find(struct map_groups *self,
+ enum map_type type, u64 addr)
+{
+ return maps__find(&self->maps[type], addr);
+}
+
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter);
+
+struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
+ enum map_type type,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter);
+
+static inline
+struct symbol *machine__find_kernel_symbol(struct machine *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
+}
+
+static inline
+struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
+}
+
+static inline
+struct symbol *map_groups__find_function_by_name(struct map_groups *self,
+ const char *name, struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
+}
+
+int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
+ int verbose, FILE *fp);
+
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name);
+struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
+
+void map_groups__flush(struct map_groups *self);
+
+#endif /* __PERF_MAP_H */
diff --git a/tools/lib/perf/symbol.c b/tools/lib/perf/symbol.c
new file mode 100644
index 0000000..e47d086
--- /dev/null
+++ b/tools/lib/perf/symbol.c
@@ -0,0 +1,2466 @@
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "symbol.h"
+#include <lk/strlist.h>
+#include <perf/config.h>
+#include <trace/parse-events.h>
+
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <limits.h>
+#include <sys/utsname.h>
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
+static int elf_read_build_id(Elf *elf, void *bf, size_t size);
+static void dsos__add(struct list_head *head, struct dso *dso);
+static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
+static int dso__load_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+static int vmlinux_path__nr_entries;
+static char **vmlinux_path;
+
+struct symbol_conf symbol_conf = {
+ .exclude_other = true,
+ .use_modules = true,
+ .try_vmlinux_path = true,
+};
+
+int dso__name_len(const struct dso *self)
+{
+ if (verbose)
+ return self->long_name_len;
+
+ return self->short_name_len;
+}
+
+bool dso__loaded(const struct dso *self, enum map_type type)
+{
+ return self->loaded & (1 << type);
+}
+
+bool dso__sorted_by_name(const struct dso *self, enum map_type type)
+{
+ return self->sorted_by_name & (1 << type);
+}
+
+static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
+{
+ self->sorted_by_name |= (1 << type);
+}
+
+bool symbol_type__is_a(char symbol_type, enum map_type map_type)
+{
+ switch (map_type) {
+ case MAP__FUNCTION:
+ return symbol_type == 'T' || symbol_type == 'W';
+ case MAP__VARIABLE:
+ return symbol_type == 'D' || symbol_type == 'd';
+ default:
+ return false;
+ }
+}
+
+static void symbols__fixup_end(struct rb_root *self)
+{
+ struct rb_node *nd, *prevnd = rb_first(self);
+ struct symbol *curr, *prev;
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct symbol, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct symbol, rb_node);
+
+ if (prev->end == prev->start)
+ prev->end = curr->start - 1;
+ }
+
+ /* Last entry */
+ if (curr->end == curr->start)
+ curr->end = roundup(curr->start, 4096);
+}
+
+static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
+{
+ struct map *prev, *curr;
+ struct rb_node *nd, *prevnd = rb_first(&self->maps[type]);
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct map, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct map, rb_node);
+ prev->end = curr->start - 1;
+ }
+
+ /*
+ * We still haven't the actual symbols, so guess the
+ * last map final address.
+ */
+ curr->end = ~0UL;
+}
+
+static void map_groups__fixup_end(struct map_groups *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ __map_groups__fixup_end(self, i);
+}
+
+static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
+ const char *name)
+{
+ size_t namelen = strlen(name) + 1;
+ struct symbol *self = calloc(1, (symbol_conf.priv_size +
+ sizeof(*self) + namelen));
+ if (self == NULL)
+ return NULL;
+
+ if (symbol_conf.priv_size)
+ self = ((void *)self) + symbol_conf.priv_size;
+
+ self->start = start;
+ self->end = len ? start + len - 1 : start;
+ self->binding = binding;
+ self->namelen = namelen - 1;
+
+ pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
+
+ memcpy(self->name, name, namelen);
+
+ return self;
+}
+
+void symbol__delete(struct symbol *self)
+{
+ free(((void *)self) - symbol_conf.priv_size);
+}
+
+static size_t symbol__fprintf(struct symbol *self, FILE *fp)
+{
+ return fprintf(fp, " %llx-%llx %c %s\n",
+ self->start, self->end,
+ self->binding == STB_GLOBAL ? 'g' :
+ self->binding == STB_LOCAL ? 'l' : 'w',
+ self->name);
+}
+
+void dso__set_long_name(struct dso *self, char *name)
+{
+ if (name == NULL)
+ return;
+ self->long_name = name;
+ self->long_name_len = strlen(name);
+}
+
+static void dso__set_short_name(struct dso *self, const char *name)
+{
+ if (name == NULL)
+ return;
+ self->short_name = name;
+ self->short_name_len = strlen(name);
+}
+
+static void dso__set_basename(struct dso *self)
+{
+ dso__set_short_name(self, basename(self->long_name));
+}
+
+struct dso *dso__new(const char *name)
+{
+ struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1);
+
+ if (self != NULL) {
+ int i;
+ strcpy(self->name, name);
+ dso__set_long_name(self, self->name);
+ dso__set_short_name(self, self->name);
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ self->symbols[i] = self->symbol_names[i] = RB_ROOT;
+ self->slen_calculated = 0;
+ self->origin = DSO__ORIG_NOT_FOUND;
+ self->loaded = 0;
+ self->sorted_by_name = 0;
+ self->has_build_id = 0;
+ self->kernel = DSO_TYPE_USER;
+ INIT_LIST_HEAD(&self->node);
+ }
+
+ return self;
+}
+
+static void symbols__delete(struct rb_root *self)
+{
+ struct symbol *pos;
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ symbol__delete(pos);
+ }
+}
+
+void dso__delete(struct dso *self)
+{
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ symbols__delete(&self->symbols[i]);
+ if (self->sname_alloc)
+ free((char *)self->short_name);
+ if (self->lname_alloc)
+ free(self->long_name);
+ free(self);
+}
+
+void dso__set_build_id(struct dso *self, void *build_id)
+{
+ memcpy(self->build_id, build_id, sizeof(self->build_id));
+ self->has_build_id = 1;
+}
+
+static void symbols__insert(struct rb_root *self, struct symbol *sym)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = sym->start;
+ struct symbol *s;
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct symbol, rb_node);
+ if (ip < s->start)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&sym->rb_node, parent, p);
+ rb_insert_color(&sym->rb_node, self);
+}
+
+static struct symbol *symbols__find(struct rb_root *self, u64 ip)
+{
+ struct rb_node *n;
+
+ if (self == NULL)
+ return NULL;
+
+ n = self->rb_node;
+
+ while (n) {
+ struct symbol *s = rb_entry(n, struct symbol, rb_node);
+
+ if (ip < s->start)
+ n = n->rb_left;
+ else if (ip > s->end)
+ n = n->rb_right;
+ else
+ return s;
+ }
+
+ return NULL;
+}
+
+struct symbol_name_rb_node {
+ struct rb_node rb_node;
+ struct symbol sym;
+};
+
+static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s;
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
+ if (strcmp(sym->name, s->sym.name) < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&symn->rb_node, parent, p);
+ rb_insert_color(&symn->rb_node, self);
+}
+
+static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(source); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ symbols__insert_by_name(self, pos);
+ }
+}
+
+static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name)
+{
+ struct rb_node *n;
+
+ if (self == NULL)
+ return NULL;
+
+ n = self->rb_node;
+
+ while (n) {
+ struct symbol_name_rb_node *s;
+ int cmp;
+
+ s = rb_entry(n, struct symbol_name_rb_node, rb_node);
+ cmp = strcmp(name, s->sym.name);
+
+ if (cmp < 0)
+ n = n->rb_left;
+ else if (cmp > 0)
+ n = n->rb_right;
+ else
+ return &s->sym;
+ }
+
+ return NULL;
+}
+
+struct symbol *dso__find_symbol(struct dso *self,
+ enum map_type type, u64 addr)
+{
+ return symbols__find(&self->symbols[type], addr);
+}
+
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name)
+{
+ return symbols__find_by_name(&self->symbol_names[type], name);
+}
+
+void dso__sort_by_name(struct dso *self, enum map_type type)
+{
+ dso__set_sorted_by_name(self, type);
+ return symbols__sort_by_name(&self->symbol_names[type],
+ &self->symbols[type]);
+}
+
+int build_id__sprintf(const u8 *self, int len, char *bf)
+{
+ char *bid = bf;
+ const u8 *raw = self;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+
+ return raw - self;
+}
+
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
+ return fprintf(fp, "%s", sbuild_id);
+}
+
+size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
+{
+ struct rb_node *nd;
+ size_t ret = fprintf(fp, "dso: %s (", self->short_name);
+
+ if (self->short_name != self->long_name)
+ ret += fprintf(fp, "%s, ", self->long_name);
+ ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
+ self->loaded ? "" : "NOT ");
+ ret += dso__fprintf_buildid(self, fp);
+ ret += fprintf(fp, ")\n");
+ for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ ret += symbol__fprintf(pos, fp);
+ }
+
+ return ret;
+}
+
+int kallsyms__parse(const char *filename, void *arg,
+ int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start))
+{
+ char *line = NULL;
+ size_t n;
+ int err = 0;
+ FILE *file = fopen(filename, "r");
+
+ if (file == NULL)
+ goto out_failure;
+
+ while (!feof(file)) {
+ u64 start;
+ int line_len, len;
+ char symbol_type;
+ char *symbol_name;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0 || !line)
+ break;
+
+ line[--line_len] = '\0'; /* \n */
+
+ len = hex2u64(line, &start);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ symbol_type = toupper(line[len]);
+ symbol_name = line + len + 2;
+
+ err = process_symbol(arg, symbol_name, symbol_type, start);
+ if (err)
+ break;
+ }
+
+ free(line);
+ fclose(file);
+ return err;
+
+out_failure:
+ return -1;
+}
+
+struct process_kallsyms_args {
+ struct map *map;
+ struct dso *dso;
+};
+
+static u8 kallsyms2elf_type(char type)
+{
+ if (type == 'W')
+ return STB_WEAK;
+
+ return isupper(type) ? STB_GLOBAL : STB_LOCAL;
+}
+
+static int map__process_kallsym_symbol(void *arg, const char *name,
+ char type, u64 start)
+{
+ struct symbol *sym;
+ struct process_kallsyms_args *a = arg;
+ struct rb_root *root = &a->dso->symbols[a->map->type];
+
+ if (!symbol_type__is_a(type, a->map->type))
+ return 0;
+
+ /*
+ * Will fix up the end later, when we have all symbols sorted.
+ */
+ sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
+
+ if (sym == NULL)
+ return -ENOMEM;
+ /*
+ * We will pass the symbols to the filter later, in
+ * map__split_kallsyms, when we have split the maps per module
+ */
+ symbols__insert(root, sym);
+
+ return 0;
+}
+
+/*
+ * Loads the function entries in /proc/kallsyms into kernel_map->dso,
+ * so that we can in the next step set the symbol ->end address and then
+ * call kernel_maps__split_kallsyms.
+ */
+static int dso__load_all_kallsyms(struct dso *self, const char *filename,
+ struct map *map)
+{
+ struct process_kallsyms_args args = { .map = map, .dso = self, };
+ return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
+}
+
+/*
+ * Split the symbols into maps, making sure there are no overlaps, i.e. the
+ * kernel range is broken in several maps, named [kernel].N, as we don't have
+ * the original ELF section names vmlinux have.
+ */
+static int dso__split_kallsyms(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ struct map_groups *kmaps = map__kmap(map)->kmaps;
+ struct machine *machine = kmaps->machine;
+ struct map *curr_map = map;
+ struct symbol *pos;
+ int count = 0;
+ struct rb_root *root = &self->symbols[map->type];
+ struct rb_node *next = rb_first(root);
+ int kernel_range = 0;
+
+ while (next) {
+ char *module;
+
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ module = strchr(pos->name, '\t');
+ if (module) {
+ if (!symbol_conf.use_modules)
+ goto discard_symbol;
+
+ *module++ = '\0';
+
+ if (strcmp(curr_map->dso->short_name, module)) {
+ if (curr_map != map &&
+ self->kernel == DSO_TYPE_GUEST_KERNEL &&
+ machine__is_default_guest(machine)) {
+ /*
+ * We assume all symbols of a module are
+ * continuous in * kallsyms, so curr_map
+ * points to a module and all its
+ * symbols are in its kmap. Mark it as
+ * loaded.
+ */
+ dso__set_loaded(curr_map->dso,
+ curr_map->type);
+ }
+
+ curr_map = map_groups__find_by_name(kmaps,
+ map->type, module);
+ if (curr_map == NULL) {
+ pr_debug("%s/proc/{kallsyms,modules} "
+ "inconsistency while looking "
+ "for \"%s\" module!\n",
+ machine->root_dir, module);
+ curr_map = map;
+ goto discard_symbol;
+ }
+
+ if (curr_map->dso->loaded &&
+ !machine__is_default_guest(machine))
+ goto discard_symbol;
+ }
+ /*
+ * So that we look just like we get from .ko files,
+ * i.e. not prelinked, relative to map->start.
+ */
+ pos->start = curr_map->map_ip(curr_map, pos->start);
+ pos->end = curr_map->map_ip(curr_map, pos->end);
+ } else if (curr_map != map) {
+ char dso_name[PATH_MAX];
+ struct dso *dso;
+
+ if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ snprintf(dso_name, sizeof(dso_name),
+ "[guest.kernel].%d",
+ kernel_range++);
+ else
+ snprintf(dso_name, sizeof(dso_name),
+ "[kernel].%d",
+ kernel_range++);
+
+ dso = dso__new(dso_name);
+ if (dso == NULL)
+ return -1;
+
+ dso->kernel = self->kernel;
+
+ curr_map = map__new2(pos->start, dso, map->type);
+ if (curr_map == NULL) {
+ dso__delete(dso);
+ return -1;
+ }
+
+ curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
+ map_groups__insert(kmaps, curr_map);
+ ++kernel_range;
+ }
+
+ if (filter && filter(curr_map, pos)) {
+discard_symbol: rb_erase(&pos->rb_node, root);
+ symbol__delete(pos);
+ } else {
+ if (curr_map != map) {
+ rb_erase(&pos->rb_node, root);
+ symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
+ }
+ count++;
+ }
+ }
+
+ if (curr_map != map &&
+ self->kernel == DSO_TYPE_GUEST_KERNEL &&
+ machine__is_default_guest(kmaps->machine)) {
+ dso__set_loaded(curr_map->dso, curr_map->type);
+ }
+
+ return count;
+}
+
+int dso__load_kallsyms(struct dso *self, const char *filename,
+ struct map *map, symbol_filter_t filter)
+{
+ if (dso__load_all_kallsyms(self, filename, map) < 0)
+ return -1;
+
+ symbols__fixup_end(&self->symbols[map->type]);
+ if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ self->origin = DSO__ORIG_GUEST_KERNEL;
+ else
+ self->origin = DSO__ORIG_KERNEL;
+
+ return dso__split_kallsyms(self, map, filter);
+}
+
+static int dso__load_perf_map(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ char *line = NULL;
+ size_t n;
+ FILE *file;
+ int nr_syms = 0;
+
+ file = fopen(self->long_name, "r");
+ if (file == NULL)
+ goto out_failure;
+
+ while (!feof(file)) {
+ u64 start, size;
+ struct symbol *sym;
+ int line_len, len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ len = hex2u64(line, &start);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ len += hex2u64(line + len, &size);
+
+ len++;
+ if (len + 2 >= line_len)
+ continue;
+
+ sym = symbol__new(start, size, STB_GLOBAL, line + len);
+
+ if (sym == NULL)
+ goto out_delete_line;
+
+ if (filter && filter(map, sym))
+ symbol__delete(sym);
+ else {
+ symbols__insert(&self->symbols[map->type], sym);
+ nr_syms++;
+ }
+ }
+
+ free(line);
+ fclose(file);
+
+ return nr_syms;
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
+}
+
+/**
+ * elf_symtab__for_each_symbol - iterate thru all the symbols
+ *
+ * @self: struct elf_symtab instance to iterate
+ * @idx: uint32_t idx
+ * @sym: GElf_Sym iterator
+ */
+#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
+ for (idx = 0, gelf_getsym(syms, idx, &sym);\
+ idx < nr_syms; \
+ idx++, gelf_getsym(syms, idx, &sym))
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static inline int elf_sym__is_function(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_FUNC &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_OBJECT &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+ const Elf_Data *symstrs)
+{
+ return symstrs->d_buf + sym->st_name;
+}
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name,
+ size_t *idx)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (idx)
+ *idx = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+
+#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+
+/*
+ * We need to check if we have a .dynsym, so that we can handle the
+ * .plt, synthesizing its symbols, that aren't on the symtabs (be it
+ * .dynsym or .symtab).
+ * And always look at the original dso, not at debuginfo packages, that
+ * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
+ */
+static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ uint32_t nr_rel_entries, idx;
+ GElf_Sym sym;
+ u64 plt_offset;
+ GElf_Shdr shdr_plt;
+ struct symbol *f;
+ GElf_Shdr shdr_rel_plt, shdr_dynsym;
+ Elf_Data *reldata, *syms, *symstrs;
+ Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
+ size_t dynsym_idx;
+ GElf_Ehdr ehdr;
+ char sympltname[1024];
+ Elf *elf;
+ int nr = 0, symidx, fd, err = 0;
+
+ fd = open(self->long_name, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ goto out_close;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_elf_end;
+
+ scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
+ ".dynsym", &dynsym_idx);
+ if (scn_dynsym == NULL)
+ goto out_elf_end;
+
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rela.plt", NULL);
+ if (scn_plt_rel == NULL) {
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rel.plt", NULL);
+ if (scn_plt_rel == NULL)
+ goto out_elf_end;
+ }
+
+ err = -1;
+
+ if (shdr_rel_plt.sh_link != dynsym_idx)
+ goto out_elf_end;
+
+ if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ goto out_elf_end;
+
+ /*
+ * Fetch the relocation section to find the idxes to the GOT
+ * and the symbols in the .dynsym they refer to.
+ */
+ reldata = elf_getdata(scn_plt_rel, NULL);
+ if (reldata == NULL)
+ goto out_elf_end;
+
+ syms = elf_getdata(scn_dynsym, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
+ if (scn_symstrs == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(scn_symstrs, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
+ plt_offset = shdr_plt.sh_offset;
+
+ if (shdr_rel_plt.sh_type == SHT_RELA) {
+ GElf_Rela pos_mem, *pos;
+
+ elf_section__for_each_rela(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&self->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ } else if (shdr_rel_plt.sh_type == SHT_REL) {
+ GElf_Rel pos_mem, *pos;
+ elf_section__for_each_rel(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&self->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ }
+
+ err = 0;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+
+ if (err == 0)
+ return nr;
+out:
+ pr_debug("%s: problems reading %s PLT info.\n",
+ __func__, self->long_name);
+ return 0;
+}
+
+static bool elf_sym__is_a(GElf_Sym *self, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sym__is_function(self);
+ case MAP__VARIABLE:
+ return elf_sym__is_object(self);
+ default:
+ return false;
+ }
+}
+
+static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sec__is_text(self, secstrs);
+ case MAP__VARIABLE:
+ return elf_sec__is_data(self, secstrs);
+ default:
+ return false;
+ }
+}
+
+static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
+{
+ Elf_Scn *sec = NULL;
+ GElf_Shdr shdr;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ gelf_getshdr(sec, &shdr);
+
+ if ((addr >= shdr.sh_addr) &&
+ (addr < (shdr.sh_addr + shdr.sh_size)))
+ return cnt;
+
+ ++cnt;
+ }
+
+ return -1;
+}
+
+static int dso__load_sym(struct dso *self, struct map *map, const char *name,
+ int fd, symbol_filter_t filter, int kmodule,
+ int want_symtab)
+{
+ struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
+ struct map *curr_map = map;
+ struct dso *curr_dso = self;
+ Elf_Data *symstrs, *secstrs;
+ uint32_t nr_syms;
+ int err = -1;
+ uint32_t idx;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr, opdshdr;
+ Elf_Data *syms, *opddata = NULL;
+ GElf_Sym sym;
+ Elf_Scn *sec, *sec_strndx, *opdsec;
+ Elf *elf;
+ int nr = 0;
+ size_t opdidx = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_debug("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ /* Always reject images with a mismatched build-id: */
+ if (self->has_build_id) {
+ u8 build_id[BUILD_ID_SIZE];
+
+ if (elf_read_build_id(elf, build_id,
+ BUILD_ID_SIZE) != BUILD_ID_SIZE)
+ goto out_elf_end;
+
+ if (!dso__build_id_equal(self, build_id))
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
+ if (sec == NULL) {
+ if (want_symtab)
+ goto out_elf_end;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
+ if (sec == NULL)
+ goto out_elf_end;
+ }
+
+ opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
+ if (opdsec)
+ opddata = elf_rawdata(opdsec, NULL);
+
+ syms = elf_getdata(sec, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ sec = elf_getscn(elf, shdr.sh_link);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(sec, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ if (sec_strndx == NULL)
+ goto out_elf_end;
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (secstrs == NULL)
+ goto out_elf_end;
+
+ nr_syms = shdr.sh_size / shdr.sh_entsize;
+
+ memset(&sym, 0, sizeof(sym));
+ if (self->kernel == DSO_TYPE_USER) {
+ self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+ elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu.prelink_undo",
+ NULL) != NULL);
+ } else self->adjust_symbols = 0;
+
+ elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
+ struct symbol *f;
+ const char *elf_name = elf_sym__name(&sym, symstrs);
+ char *demangled = NULL;
+ int is_label = elf_sym__is_label(&sym);
+ const char *section_name;
+
+ if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
+ strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
+ kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
+
+ if (!is_label && !elf_sym__is_a(&sym, map->type))
+ continue;
+
+ /* Reject ARM ELF "mapping symbols": these aren't unique and
+ * don't identify functions, so will confuse the profile
+ * output: */
+ if (ehdr.e_machine == EM_ARM) {
+ if (!strcmp(elf_name, "$a") ||
+ !strcmp(elf_name, "$d") ||
+ !strcmp(elf_name, "$t"))
+ continue;
+ }
+
+ if (opdsec && sym.st_shndx == opdidx) {
+ u32 offset = sym.st_value - opdshdr.sh_addr;
+ u64 *opd = opddata->d_buf + offset;
+ sym.st_value = *opd;
+ sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
+ }
+
+ sec = elf_getscn(elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+
+ if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
+ continue;
+
+ section_name = elf_sec__name(&shdr, secstrs);
+
+ if (self->kernel != DSO_TYPE_USER || kmodule) {
+ char dso_name[PATH_MAX];
+
+ if (strcmp(section_name,
+ (curr_dso->short_name +
+ self->short_name_len)) == 0)
+ goto new_symbol;
+
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = self;
+ goto new_symbol;
+ }
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", self->short_name, section_name);
+
+ curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_dso->kernel = self->kernel;
+ curr_map = map__new2(start, curr_dso,
+ map->type);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = identity__map_ip;
+ curr_map->unmap_ip = identity__map_ip;
+ curr_dso->origin = self->origin;
+ map_groups__insert(kmap->kmaps, curr_map);
+ dsos__add(&self->node, curr_dso);
+ dso__set_loaded(curr_dso, map->type);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
+ }
+
+ if (curr_dso->adjust_symbols) {
+ pr_debug4("%s: adjusting symbol: st_value: %#Lx "
+ "sh_addr: %#Lx sh_offset: %#Lx\n", __func__,
+ (u64)sym.st_value, (u64)shdr.sh_addr,
+ (u64)shdr.sh_offset);
+ sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ }
+ /*
+ * We need to figure out if the object was created from C++ sources
+ * DWARF DW_compile_unit has this, but we don't always have access
+ * to it...
+ */
+ demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
+ if (demangled != NULL)
+ elf_name = demangled;
+new_symbol:
+ f = symbol__new(sym.st_value, sym.st_size,
+ GELF_ST_BIND(sym.st_info), elf_name);
+ free(demangled);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(curr_map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&curr_dso->symbols[curr_map->type], f);
+ nr++;
+ }
+ }
+
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ symbols__fixup_end(&self->symbols[map->type]);
+ if (kmap) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ __map_groups__fixup_end(kmap->kmaps, map->type);
+ }
+ }
+ err = nr;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ return err;
+}
+
+static bool dso__build_id_equal(const struct dso *self, u8 *build_id)
+{
+ return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;
+}
+
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
+{
+ bool have_build_id = false;
+ struct dso *pos;
+
+ list_for_each_entry(pos, head, node) {
+ if (with_hits && !pos->hit)
+ continue;
+ if (pos->has_build_id) {
+ have_build_id = true;
+ continue;
+ }
+ if (filename__read_build_id(pos->long_name, pos->build_id,
+ sizeof(pos->build_id)) > 0) {
+ have_build_id = true;
+ pos->has_build_id = true;
+ }
+ }
+
+ return have_build_id;
+}
+
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
+ if (sec == NULL) {
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".notes", NULL);
+ if (sec == NULL)
+ goto out;
+ }
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out;
+
+ ptr = data->d_buf;
+ while (ptr < (data->d_buf + data->d_size)) {
+ GElf_Nhdr *nhdr = ptr;
+ int namesz = NOTE_ALIGN(nhdr->n_namesz),
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+ const char *name;
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ memcpy(bf, ptr, BUILD_ID_SIZE);
+ err = BUILD_ID_SIZE;
+ break;
+ }
+ }
+ ptr += descsz;
+ }
+
+out:
+ return err;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ err = elf_read_build_id(elf, bf, size);
+
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd, err = -1;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ while (1) {
+ char bf[BUFSIZ];
+ GElf_Nhdr nhdr;
+ int namesz, descsz;
+
+ if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
+ break;
+
+ namesz = NOTE_ALIGN(nhdr.n_namesz);
+ descsz = NOTE_ALIGN(nhdr.n_descsz);
+ if (nhdr.n_type == NT_GNU_BUILD_ID &&
+ nhdr.n_namesz == sizeof("GNU")) {
+ if (read(fd, bf, namesz) != namesz)
+ break;
+ if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
+ if (read(fd, build_id,
+ BUILD_ID_SIZE) == BUILD_ID_SIZE) {
+ err = 0;
+ break;
+ }
+ } else if (read(fd, bf, descsz) != descsz)
+ break;
+ } else {
+ int n = namesz + descsz;
+ if (read(fd, bf, n) != n)
+ break;
+ }
+ }
+ close(fd);
+out:
+ return err;
+}
+
+char dso__symtab_origin(const struct dso *self)
+{
+ static const char origin[] = {
+ [DSO__ORIG_KERNEL] = 'k',
+ [DSO__ORIG_JAVA_JIT] = 'j',
+ [DSO__ORIG_BUILD_ID_CACHE] = 'B',
+ [DSO__ORIG_FEDORA] = 'f',
+ [DSO__ORIG_UBUNTU] = 'u',
+ [DSO__ORIG_BUILDID] = 'b',
+ [DSO__ORIG_DSO] = 'd',
+ [DSO__ORIG_KMODULE] = 'K',
+ [DSO__ORIG_GUEST_KERNEL] = 'g',
+ [DSO__ORIG_GUEST_KMODULE] = 'G',
+ };
+
+ if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
+ return '!';
+ return origin[self->origin];
+}
+
+/* overridden by perf/util/build-id.c */
+char * __weak dso__build_id_filename(struct dso *self __unused,
+ char *bf __unused, size_t size __unused)
+{
+ return NULL;
+}
+
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
+{
+ int size = PATH_MAX;
+ char *name;
+ int ret = -1;
+ int fd;
+ struct machine *machine;
+ const char *root_dir;
+ int want_symtab;
+
+ dso__set_loaded(self, map->type);
+
+ if (self->kernel == DSO_TYPE_KERNEL)
+ return dso__load_kernel_sym(self, map, filter);
+ else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
+ return dso__load_guest_kernel_sym(self, map, filter);
+
+ if (map->groups && map->groups->machine)
+ machine = map->groups->machine;
+ else
+ machine = NULL;
+
+ name = malloc(size);
+ if (!name)
+ return -1;
+
+ self->adjust_symbols = 0;
+
+ if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
+ ret = dso__load_perf_map(self, map, filter);
+ self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
+ DSO__ORIG_NOT_FOUND;
+ return ret;
+ }
+
+ /* Iterate over candidate debug images.
+ * On the first pass, only load images if they have a full symtab.
+ * Failing that, do a second pass where we accept .dynsym also
+ */
+ for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
+ self->origin != DSO__ORIG_NOT_FOUND;
+ self->origin++) {
+ switch (self->origin) {
+ case DSO__ORIG_BUILD_ID_CACHE:
+ if (dso__build_id_filename(self, name, size) == NULL)
+ continue;
+ break;
+ case DSO__ORIG_FEDORA:
+ snprintf(name, size, "/usr/lib/debug%s.debug",
+ self->long_name);
+ break;
+ case DSO__ORIG_UBUNTU:
+ snprintf(name, size, "/usr/lib/debug%s",
+ self->long_name);
+ break;
+ case DSO__ORIG_BUILDID: {
+ char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+
+ if (!self->has_build_id)
+ continue;
+
+ build_id__sprintf(self->build_id,
+ sizeof(self->build_id),
+ build_id_hex);
+ snprintf(name, size,
+ "/usr/lib/debug/.build-id/%.2s/%s.debug",
+ build_id_hex, build_id_hex + 2);
+ }
+ break;
+ case DSO__ORIG_DSO:
+ snprintf(name, size, "%s", self->long_name);
+ break;
+ case DSO__ORIG_GUEST_KMODULE:
+ if (map->groups && map->groups->machine)
+ root_dir = map->groups->machine->root_dir;
+ else
+ root_dir = "";
+ snprintf(name, size, "%s%s", root_dir, self->long_name);
+ break;
+
+ default:
+ /*
+ * If we wanted a full symtab but no image had one,
+ * relax our requirements and repeat the search.
+ */
+ if (want_symtab) {
+ want_symtab = 0;
+ self->origin = DSO__ORIG_BUILD_ID_CACHE;
+ } else
+ continue;
+ }
+
+ /* Name is now the name of the next image to try */
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ ret = dso__load_sym(self, map, name, fd, filter, 0,
+ want_symtab);
+ close(fd);
+
+ /*
+ * Some people seem to have debuginfo files _WITHOUT_ debug
+ * info!?!?
+ */
+ if (!ret)
+ continue;
+
+ if (ret > 0) {
+ int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
+ if (nr_plt > 0)
+ ret += nr_plt;
+ break;
+ }
+ }
+
+ free(name);
+ if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
+ return 0;
+ return ret;
+}
+
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
+
+ if (map->dso && strcmp(map->dso->short_name, name) == 0)
+ return map;
+ }
+
+ return NULL;
+}
+
+static int dso__kernel_module_get_build_id(struct dso *self,
+ const char *root_dir)
+{
+ char filename[PATH_MAX];
+ /*
+ * kernel module short names are of the form "[module]" and
+ * we need just "module" here.
+ */
+ const char *name = self->short_name + 1;
+
+ snprintf(filename, sizeof(filename),
+ "%s/sys/module/%.*s/notes/.note.gnu.build-id",
+ root_dir, (int)strlen(name) - 1, name);
+
+ if (sysfs__read_build_id(filename, self->build_id,
+ sizeof(self->build_id)) == 0)
+ self->has_build_id = true;
+
+ return 0;
+}
+
+static int map_groups__set_modules_path_dir(struct map_groups *self,
+ const char *dir_name)
+{
+ struct dirent *dent;
+ DIR *dir = opendir(dir_name);
+ int ret = 0;
+
+ if (!dir) {
+ pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
+ return -1;
+ }
+
+ while ((dent = readdir(dir)) != NULL) {
+ char path[PATH_MAX];
+ struct stat st;
+
+ /*sshfs might return bad dent->d_type, so we have to stat*/
+ sprintf(path, "%s/%s", dir_name, dent->d_name);
+ if (stat(path, &st))
+ continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!strcmp(dent->d_name, ".") ||
+ !strcmp(dent->d_name, ".."))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dir_name, dent->d_name);
+ ret = map_groups__set_modules_path_dir(self, path);
+ if (ret < 0)
+ goto out;
+ } else {
+ char *dot = strrchr(dent->d_name, '.'),
+ dso_name[PATH_MAX];
+ struct map *map;
+ char *long_name;
+
+ if (dot == NULL || strcmp(dot, ".ko"))
+ continue;
+ snprintf(dso_name, sizeof(dso_name), "[%.*s]",
+ (int)(dot - dent->d_name), dent->d_name);
+
+ strxfrchar(dso_name, '-', '_');
+ map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name);
+ if (map == NULL)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dir_name, dent->d_name);
+
+ long_name = strdup(path);
+ if (long_name == NULL) {
+ ret = -1;
+ goto out;
+ }
+ dso__set_long_name(map->dso, long_name);
+ map->dso->lname_alloc = 1;
+ dso__kernel_module_get_build_id(map->dso, "");
+ }
+ }
+
+out:
+ closedir(dir);
+ return ret;
+}
+
+static char *get_kernel_version(const char *root_dir)
+{
+ char version[PATH_MAX];
+ FILE *file;
+ char *name, *tmp;
+ const char *prefix = "Linux version ";
+
+ sprintf(version, "%s/proc/version", root_dir);
+ file = fopen(version, "r");
+ if (!file)
+ return NULL;
+
+ version[0] = '\0';
+ tmp = fgets(version, sizeof(version), file);
+ fclose(file);
+
+ name = strstr(version, prefix);
+ if (!name)
+ return NULL;
+ name += strlen(prefix);
+ tmp = strchr(name, ' ');
+ if (tmp)
+ *tmp = '\0';
+
+ return strdup(name);
+}
+
+static int machine__set_modules_path(struct machine *self)
+{
+ char *version;
+ char modules_path[PATH_MAX];
+
+ version = get_kernel_version(self->root_dir);
+ if (!version)
+ return -1;
+
+ snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
+ self->root_dir, version);
+ free(version);
+
+ return map_groups__set_modules_path_dir(&self->kmaps, modules_path);
+}
+
+/*
+ * Constructor variant for modules (where we know from /proc/modules where
+ * they are loaded) and for vmlinux, where only after we load all the
+ * symbols we'll know where it starts and ends.
+ */
+static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
+{
+ struct map *self = calloc(1, (sizeof(*self) +
+ (dso->kernel ? sizeof(struct kmap) : 0)));
+ if (self != NULL) {
+ /*
+ * ->end will be filled after we load all the symbols
+ */
+ map__init(self, type, start, 0, 0, dso);
+ }
+
+ return self;
+}
+
+struct map *machine__new_module(struct machine *self, u64 start,
+ const char *filename)
+{
+ struct map *map;
+ struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename);
+
+ if (dso == NULL)
+ return NULL;
+
+ map = map__new2(start, dso, MAP__FUNCTION);
+ if (map == NULL)
+ return NULL;
+
+ if (machine__is_host(self))
+ dso->origin = DSO__ORIG_KMODULE;
+ else
+ dso->origin = DSO__ORIG_GUEST_KMODULE;
+ map_groups__insert(&self->kmaps, map);
+ return map;
+}
+
+static int machine__create_modules(struct machine *self)
+{
+ char *line = NULL;
+ size_t n;
+ FILE *file;
+ struct map *map;
+ const char *modules;
+ char path[PATH_MAX];
+
+ if (machine__is_default_guest(self))
+ modules = symbol_conf.default_guest_modules;
+ else {
+ sprintf(path, "%s/proc/modules", self->root_dir);
+ modules = path;
+ }
+
+ file = fopen(modules, "r");
+ if (file == NULL)
+ return -1;
+
+ while (!feof(file)) {
+ char name[PATH_MAX];
+ u64 start;
+ char *sep;
+ int line_len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ sep = strrchr(line, 'x');
+ if (sep == NULL)
+ continue;
+
+ hex2u64(sep + 1, &start);
+
+ sep = strchr(line, ' ');
+ if (sep == NULL)
+ continue;
+
+ *sep = '\0';
+
+ snprintf(name, sizeof(name), "[%s]", line);
+ map = machine__new_module(self, start, name);
+ if (map == NULL)
+ goto out_delete_line;
+ dso__kernel_module_get_build_id(map->dso, self->root_dir);
+ }
+
+ free(line);
+ fclose(file);
+
+ return machine__set_modules_path(self);
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
+}
+
+static int dso__load_vmlinux(struct dso *self, struct map *map,
+ const char *vmlinux, symbol_filter_t filter)
+{
+ int err = -1, fd;
+
+ fd = open(vmlinux, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ dso__set_loaded(self, map->type);
+ err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
+ close(fd);
+
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", vmlinux);
+
+ return err;
+}
+
+int dso__load_vmlinux_path(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int i, err = 0;
+ char *filename;
+
+ pr_debug("Looking at the vmlinux_path (%d entries long)\n",
+ vmlinux_path__nr_entries + 1);
+
+ filename = dso__build_id_filename(self, NULL, 0);
+ if (filename != NULL) {
+ err = dso__load_vmlinux(self, map, filename, filter);
+ if (err > 0) {
+ dso__set_long_name(self, filename);
+ goto out;
+ }
+ free(filename);
+ }
+
+ for (i = 0; i < vmlinux_path__nr_entries; ++i) {
+ err = dso__load_vmlinux(self, map, vmlinux_path[i], filter);
+ if (err > 0) {
+ dso__set_long_name(self, strdup(vmlinux_path[i]));
+ break;
+ }
+ }
+out:
+ return err;
+}
+
+static int dso__load_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int err;
+ const char *kallsyms_filename = NULL;
+ char *kallsyms_allocated_filename = NULL;
+ /*
+ * Step 1: if the user specified a vmlinux filename, use it and only
+ * it, reporting errors to the user if it cannot be used.
+ *
+ * For instance, try to analyse an ARM perf.data file _without_ a
+ * build-id, or if the user specifies the wrong path to the right
+ * vmlinux file, obviously we can't fallback to another vmlinux (a
+ * x86_86 one, on the machine where analysis is being performed, say),
+ * or worse, /proc/kallsyms.
+ *
+ * If the specified file _has_ a build-id and there is a build-id
+ * section in the perf.data file, we will still do the expected
+ * validation in dso__load_vmlinux and will bail out if they don't
+ * match.
+ */
+ if (symbol_conf.vmlinux_name != NULL) {
+ err = dso__load_vmlinux(self, map,
+ symbol_conf.vmlinux_name, filter);
+ if (err > 0) {
+ dso__set_long_name(self,
+ strdup(symbol_conf.vmlinux_name));
+ goto out_fixup;
+ }
+ return err;
+ }
+
+ if (vmlinux_path != NULL) {
+ err = dso__load_vmlinux_path(self, map, filter);
+ if (err > 0)
+ goto out_fixup;
+ }
+
+ /*
+ * Say the kernel DSO was created when processing the build-id header table,
+ * we have a build-id, so check if it is the same as the running kernel,
+ * using it if it is.
+ */
+ if (self->has_build_id) {
+ u8 kallsyms_build_id[BUILD_ID_SIZE];
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
+ sizeof(kallsyms_build_id)) == 0) {
+ if (dso__build_id_equal(self, kallsyms_build_id)) {
+ kallsyms_filename = "/proc/kallsyms";
+ goto do_kallsyms;
+ }
+ }
+ /*
+ * Now look if we have it on the build-id cache in
+ * $HOME/.debug/[kernel.kallsyms].
+ */
+ build_id__sprintf(self->build_id, sizeof(self->build_id),
+ sbuild_id);
+
+ if (asprintf(&kallsyms_allocated_filename,
+ "%s/.debug/[kernel.kallsyms]/%s",
+ getenv("HOME"), sbuild_id) == -1) {
+ pr_err("Not enough memory for kallsyms file lookup\n");
+ return -1;
+ }
+
+ kallsyms_filename = kallsyms_allocated_filename;
+
+ if (access(kallsyms_filename, F_OK)) {
+ pr_err("No kallsyms or vmlinux with build-id %s "
+ "was found\n", sbuild_id);
+ free(kallsyms_allocated_filename);
+ return -1;
+ }
+ } else {
+ /*
+ * Last resort, if we don't have a build-id and couldn't find
+ * any vmlinux file, try the running kernel kallsyms table.
+ */
+ kallsyms_filename = "/proc/kallsyms";
+ }
+
+do_kallsyms:
+ err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", kallsyms_filename);
+ free(kallsyms_allocated_filename);
+
+ if (err > 0) {
+out_fixup:
+ if (kallsyms_filename != NULL)
+ dso__set_long_name(self, strdup("[kernel.kallsyms]"));
+ map__fixup_start(map);
+ map__fixup_end(map);
+ }
+
+ return err;
+}
+
+static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int err;
+ const char *kallsyms_filename = NULL;
+ struct machine *machine;
+ char path[PATH_MAX];
+
+ if (!map->groups) {
+ pr_debug("Guest kernel map hasn't the point to groups\n");
+ return -1;
+ }
+ machine = map->groups->machine;
+
+ if (machine__is_default_guest(machine)) {
+ /*
+ * if the user specified a vmlinux filename, use it and only
+ * it, reporting errors to the user if it cannot be used.
+ * Or use file guest_kallsyms inputted by user on commandline
+ */
+ if (symbol_conf.default_guest_vmlinux_name != NULL) {
+ err = dso__load_vmlinux(self, map,
+ symbol_conf.default_guest_vmlinux_name, filter);
+ goto out_try_fixup;
+ }
+
+ kallsyms_filename = symbol_conf.default_guest_kallsyms;
+ if (!kallsyms_filename)
+ return -1;
+ } else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ kallsyms_filename = path;
+ }
+
+ err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
+ if (err > 0)
+ pr_debug("Using %s for symbols\n", kallsyms_filename);
+
+out_try_fixup:
+ if (err > 0) {
+ if (kallsyms_filename != NULL) {
+ machine__mmap_name(machine, path, sizeof(path));
+ dso__set_long_name(self, strdup(path));
+ }
+ map__fixup_start(map);
+ map__fixup_end(map);
+ }
+
+ return err;
+}
+
+static void dsos__add(struct list_head *head, struct dso *dso)
+{
+ list_add_tail(&dso->node, head);
+}
+
+static struct dso *dsos__find(struct list_head *head, const char *name)
+{
+ struct dso *pos;
+
+ list_for_each_entry(pos, head, node)
+ if (strcmp(pos->long_name, name) == 0)
+ return pos;
+ return NULL;
+}
+
+struct dso *__dsos__findnew(struct list_head *head, const char *name)
+{
+ struct dso *dso = dsos__find(head, name);
+
+ if (!dso) {
+ dso = dso__new(name);
+ if (dso != NULL) {
+ dsos__add(head, dso);
+ dso__set_basename(dso);
+ }
+ }
+
+ return dso;
+}
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ int i;
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ ret += dso__fprintf(pos, i, fp);
+ }
+
+ return ret;
+}
+
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp)
+{
+ struct rb_node *nd;
+ size_t ret = 0;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += __dsos__fprintf(&pos->kernel_dsos, fp);
+ ret += __dsos__fprintf(&pos->user_dsos, fp);
+ }
+
+ return ret;
+}
+
+static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
+ bool with_hits)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ if (with_hits && !pos->hit)
+ continue;
+ ret += dso__fprintf_buildid(pos, fp);
+ ret += fprintf(fp, " %s\n", pos->long_name);
+ }
+ return ret;
+}
+
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits)
+{
+ return __dsos__fprintf_buildid(&self->kernel_dsos, fp, with_hits) +
+ __dsos__fprintf_buildid(&self->user_dsos, fp, with_hits);
+}
+
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
+{
+ struct rb_node *nd;
+ size_t ret = 0;
+
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += machine__fprintf_dsos_buildid(pos, fp, with_hits);
+ }
+ return ret;
+}
+
+struct dso *dso__new_kernel(const char *name)
+{
+ struct dso *self = dso__new(name ?: "[kernel.kallsyms]");
+
+ if (self != NULL) {
+ dso__set_short_name(self, "[kernel]");
+ self->kernel = DSO_TYPE_KERNEL;
+ }
+
+ return self;
+}
+
+static struct dso *dso__new_guest_kernel(struct machine *machine,
+ const char *name)
+{
+ char bf[PATH_MAX];
+ struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf)));
+
+ if (self != NULL) {
+ dso__set_short_name(self, "[guest.kernel]");
+ self->kernel = DSO_TYPE_GUEST_KERNEL;
+ }
+
+ return self;
+}
+
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine)
+{
+ char path[PATH_MAX];
+
+ if (machine__is_default_guest(machine))
+ return;
+ sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
+ if (sysfs__read_build_id(path, self->build_id,
+ sizeof(self->build_id)) == 0)
+ self->has_build_id = true;
+}
+
+static struct dso *machine__create_kernel(struct machine *self)
+{
+ const char *vmlinux_name = NULL;
+ struct dso *kernel;
+
+ if (machine__is_host(self)) {
+ vmlinux_name = symbol_conf.vmlinux_name;
+ kernel = dso__new_kernel(vmlinux_name);
+ } else {
+ if (machine__is_default_guest(self))
+ vmlinux_name = symbol_conf.default_guest_vmlinux_name;
+ kernel = dso__new_guest_kernel(self, vmlinux_name);
+ }
+
+ if (kernel != NULL) {
+ dso__read_running_kernel_build_id(kernel, self);
+ dsos__add(&self->kernel_dsos, kernel);
+ }
+ return kernel;
+}
+
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
+{
+ enum map_type type;
+
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ struct kmap *kmap;
+
+ self->vmlinux_maps[type] = map__new2(0, kernel, type);
+ if (self->vmlinux_maps[type] == NULL)
+ return -1;
+
+ self->vmlinux_maps[type]->map_ip =
+ self->vmlinux_maps[type]->unmap_ip = identity__map_ip;
+
+ kmap = map__kmap(self->vmlinux_maps[type]);
+ kmap->kmaps = &self->kmaps;
+ map_groups__insert(&self->kmaps, self->vmlinux_maps[type]);
+ }
+
+ return 0;
+}
+
+void machine__destroy_kernel_maps(struct machine *self)
+{
+ enum map_type type;
+
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ struct kmap *kmap;
+
+ if (self->vmlinux_maps[type] == NULL)
+ continue;
+
+ kmap = map__kmap(self->vmlinux_maps[type]);
+ map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
+ if (kmap->ref_reloc_sym) {
+ /*
+ * ref_reloc_sym is shared among all maps, so free just
+ * on one of them.
+ */
+ if (type == MAP__FUNCTION) {
+ free((char *)kmap->ref_reloc_sym->name);
+ kmap->ref_reloc_sym->name = NULL;
+ free(kmap->ref_reloc_sym);
+ }
+ kmap->ref_reloc_sym = NULL;
+ }
+
+ map__delete(self->vmlinux_maps[type]);
+ self->vmlinux_maps[type] = NULL;
+ }
+}
+
+int machine__create_kernel_maps(struct machine *self)
+{
+ struct dso *kernel = machine__create_kernel(self);
+
+ if (kernel == NULL ||
+ __machine__create_kernel_maps(self, kernel) < 0)
+ return -1;
+
+ if (symbol_conf.use_modules && machine__create_modules(self) < 0)
+ pr_debug("Problems creating module maps, continuing anyway...\n");
+ /*
+ * Now that we have all the maps created, just set the ->end of them:
+ */
+ map_groups__fixup_end(&self->kmaps);
+ return 0;
+}
+
+static void vmlinux_path__exit(void)
+{
+ while (--vmlinux_path__nr_entries >= 0) {
+ free(vmlinux_path[vmlinux_path__nr_entries]);
+ vmlinux_path[vmlinux_path__nr_entries] = NULL;
+ }
+
+ free(vmlinux_path);
+ vmlinux_path = NULL;
+}
+
+static int vmlinux_path__init(void)
+{
+ struct utsname uts;
+ char bf[PATH_MAX];
+
+ if (uname(&uts) < 0)
+ return -1;
+
+ vmlinux_path = malloc(sizeof(char *) * 5);
+ if (vmlinux_path == NULL)
+ return -1;
+
+ vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
+ uts.release);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+
+ return 0;
+
+out_fail:
+ vmlinux_path__exit();
+ return -1;
+}
+
+size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp)
+{
+ int i;
+ size_t printed = 0;
+ struct dso *kdso = self->vmlinux_maps[MAP__FUNCTION]->dso;
+
+ if (kdso->has_build_id) {
+ char filename[PATH_MAX];
+ if (dso__build_id_filename(kdso, filename, sizeof(filename)))
+ printed += fprintf(fp, "[0] %s\n", filename);
+ }
+
+ for (i = 0; i < vmlinux_path__nr_entries; ++i)
+ printed += fprintf(fp, "[%d] %s\n",
+ i + kdso->has_build_id, vmlinux_path[i]);
+
+ return printed;
+}
+
+static int setup_list(struct strlist **list, const char *list_str,
+ const char *list_name)
+{
+ if (list_str == NULL)
+ return 0;
+
+ *list = strlist__new(true, list_str);
+ if (!*list) {
+ pr_err("problems parsing %s list\n", list_name);
+ return -1;
+ }
+ return 0;
+}
+
+int symbol__init(void)
+{
+ if (symbol_conf.initialized)
+ return 0;
+
+ elf_version(EV_CURRENT);
+ if (symbol_conf.sort_by_name)
+ symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
+ sizeof(struct symbol));
+
+ if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
+ return -1;
+
+ if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
+ pr_err("'.' is the only non valid --field-separator argument\n");
+ return -1;
+ }
+
+ if (setup_list(&symbol_conf.dso_list,
+ symbol_conf.dso_list_str, "dso") < 0)
+ return -1;
+
+ if (setup_list(&symbol_conf.comm_list,
+ symbol_conf.comm_list_str, "comm") < 0)
+ goto out_free_dso_list;
+
+ if (setup_list(&symbol_conf.sym_list,
+ symbol_conf.sym_list_str, "symbol") < 0)
+ goto out_free_comm_list;
+
+ symbol_conf.initialized = true;
+ return 0;
+
+out_free_dso_list:
+ strlist__delete(symbol_conf.dso_list);
+out_free_comm_list:
+ strlist__delete(symbol_conf.comm_list);
+ return -1;
+}
+
+void symbol__exit(void)
+{
+ if (!symbol_conf.initialized)
+ return;
+ strlist__delete(symbol_conf.sym_list);
+ strlist__delete(symbol_conf.dso_list);
+ strlist__delete(symbol_conf.comm_list);
+ vmlinux_path__exit();
+ symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
+ symbol_conf.initialized = false;
+}
+
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
+{
+ struct machine *machine = machines__findnew(self, pid);
+
+ if (machine == NULL)
+ return -1;
+
+ return machine__create_kernel_maps(machine);
+}
+
+static int hex(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ if ((ch >= 'A') && (ch <= 'F'))
+ return ch - 'A' + 10;
+ return -1;
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int hex2u64(const char *ptr, u64 *long_val)
+{
+ const char *p = ptr;
+ *long_val = 0;
+
+ while (*p) {
+ const int hex_val = hex(*p);
+
+ if (hex_val < 0)
+ break;
+
+ *long_val = (*long_val << 4) | hex_val;
+ p++;
+ }
+
+ return p - ptr;
+}
+
+char *strxfrchar(char *s, char from, char to)
+{
+ char *p = s;
+
+ while ((p = strchr(p, from)) != NULL)
+ *p++ = to;
+
+ return s;
+}
+
+int machines__create_guest_kernel_maps(struct rb_root *self)
+{
+ int ret = 0;
+ struct dirent **namelist = NULL;
+ int i, items = 0;
+ char path[PATH_MAX];
+ pid_t pid;
+
+ if (symbol_conf.default_guest_vmlinux_name ||
+ symbol_conf.default_guest_modules ||
+ symbol_conf.default_guest_kallsyms) {
+ machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID);
+ }
+
+ if (symbol_conf.guestmount) {
+ items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL);
+ if (items <= 0)
+ return -ENOENT;
+ for (i = 0; i < items; i++) {
+ if (!isdigit(namelist[i]->d_name[0])) {
+ /* Filter out . and .. */
+ continue;
+ }
+ pid = atoi(namelist[i]->d_name);
+ sprintf(path, "%s/%s/proc/kallsyms",
+ symbol_conf.guestmount,
+ namelist[i]->d_name);
+ ret = access(path, R_OK);
+ if (ret) {
+ pr_debug("Can't access file %s\n", path);
+ goto failure;
+ }
+ machines__create_kernel_maps(self, pid);
+ }
+failure:
+ free(namelist);
+ }
+
+ return ret;
+}
+
+void machines__destroy_guest_kernel_maps(struct rb_root *self)
+{
+ struct rb_node *next = rb_first(self);
+
+ while (next) {
+ struct machine *pos = rb_entry(next, struct machine, rb_node);
+
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, self);
+ machine__delete(pos);
+ }
+}
+
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_kallsyms(map->dso, filename, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ /*
+ * Since /proc/kallsyms will have multiple sessions for the
+ * kernel, with modules between them, fixup the end of all
+ * sections.
+ */
+ __map_groups__fixup_end(&self->kmaps, type);
+ }
+
+ return ret;
+}
+
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_vmlinux_path(map->dso, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ map__reloc_vmlinux(map);
+ }
+
+ return ret;
+}
diff --git a/tools/lib/perf/symbol.h b/tools/lib/perf/symbol.h
new file mode 100644
index 0000000..c8cf8c6
--- /dev/null
+++ b/tools/lib/perf/symbol.h
@@ -0,0 +1,233 @@
+#ifndef __PERF_SYMBOL_H
+#define __PERF_SYMBOL_H
+
+#include <linux/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "map.h"
+#include <linux/list.h>
+#include <lk/rbtree.h>
+#include <stdio.h>
+
+#ifdef HAVE_CPLUS_DEMANGLE
+extern char *cplus_demangle(const char *, int);
+
+static inline char *bfd_demangle(void __used *v, const char *c, int i)
+{
+ return cplus_demangle(c, i);
+}
+#else
+#ifdef NO_DEMANGLE
+static inline char *bfd_demangle(void __used *v, const char __used *c,
+ int __used i)
+{
+ return NULL;
+}
+#else
+#include <bfd.h>
+#endif
+#endif
+
+int hex2u64(const char *ptr, u64 *val);
+char *strxfrchar(char *s, char from, char to);
+
+/*
+ * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
+ * for newer versions we can use mmap to reduce memory usage:
+ */
+#ifdef LIBELF_NO_MMAP
+# define PERF_ELF_C_READ_MMAP ELF_C_READ
+#else
+# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#endif
+
+#ifndef DMGL_PARAMS
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
+
+#define BUILD_ID_SIZE 20
+
+struct symbol {
+ struct rb_node rb_node;
+ u64 start;
+ u64 end;
+ u16 namelen;
+ u8 binding;
+ char name[0];
+};
+
+void symbol__delete(struct symbol *self);
+
+struct strlist;
+
+struct symbol_conf {
+ unsigned short priv_size;
+ bool try_vmlinux_path,
+ use_modules,
+ sort_by_name,
+ show_nr_samples,
+ use_callchain,
+ exclude_other,
+ show_cpu_utilization,
+ initialized;
+ const char *vmlinux_name,
+ *source_prefix,
+ *field_sep;
+ const char *default_guest_vmlinux_name,
+ *default_guest_kallsyms,
+ *default_guest_modules;
+ const char *guestmount;
+ const char *dso_list_str,
+ *comm_list_str,
+ *sym_list_str,
+ *col_width_list_str;
+ struct strlist *dso_list,
+ *comm_list,
+ *sym_list;
+};
+
+extern struct symbol_conf symbol_conf;
+
+static inline void *symbol__priv(struct symbol *self)
+{
+ return ((void *)self) - symbol_conf.priv_size;
+}
+
+struct ref_reloc_sym {
+ const char *name;
+ u64 addr;
+ u64 unrelocated_addr;
+};
+
+struct map_symbol {
+ struct map *map;
+ struct symbol *sym;
+ bool unfolded;
+ bool has_children;
+};
+
+struct addr_location {
+ struct thread *thread;
+ struct map *map;
+ struct symbol *sym;
+ u64 addr;
+ char level;
+ bool filtered;
+ u8 cpumode;
+ s32 cpu;
+};
+
+enum dso_kernel_type {
+ DSO_TYPE_USER = 0,
+ DSO_TYPE_KERNEL,
+ DSO_TYPE_GUEST_KERNEL
+};
+
+struct dso {
+ struct list_head node;
+ struct rb_root symbols[MAP__NR_TYPES];
+ struct rb_root symbol_names[MAP__NR_TYPES];
+ enum dso_kernel_type kernel;
+ u8 adjust_symbols:1;
+ u8 slen_calculated:1;
+ u8 has_build_id:1;
+ u8 hit:1;
+ u8 annotate_warned:1;
+ u8 sname_alloc:1;
+ u8 lname_alloc:1;
+ unsigned char origin;
+ u8 sorted_by_name;
+ u8 loaded;
+ u8 build_id[BUILD_ID_SIZE];
+ const char *short_name;
+ char *long_name;
+ u16 long_name_len;
+ u16 short_name_len;
+ char name[0];
+};
+
+struct dso *dso__new(const char *name);
+struct dso *dso__new_kernel(const char *name);
+void dso__delete(struct dso *self);
+
+int dso__name_len(const struct dso *self);
+
+bool dso__loaded(const struct dso *self, enum map_type type);
+bool dso__sorted_by_name(const struct dso *self, enum map_type type);
+
+static inline void dso__set_loaded(struct dso *self, enum map_type type)
+{
+ self->loaded |= (1 << type);
+}
+
+void dso__sort_by_name(struct dso *self, enum map_type type);
+
+struct dso *__dsos__findnew(struct list_head *head, const char *name);
+
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
+int dso__load_vmlinux_path(struct dso *self, struct map *map,
+ symbol_filter_t filter);
+int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
+ symbol_filter_t filter);
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter);
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter);
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp);
+
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits);
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
+
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
+size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
+
+enum dso_origin {
+ DSO__ORIG_KERNEL = 0,
+ DSO__ORIG_GUEST_KERNEL,
+ DSO__ORIG_JAVA_JIT,
+ DSO__ORIG_BUILD_ID_CACHE,
+ DSO__ORIG_FEDORA,
+ DSO__ORIG_UBUNTU,
+ DSO__ORIG_BUILDID,
+ DSO__ORIG_DSO,
+ DSO__ORIG_GUEST_KMODULE,
+ DSO__ORIG_KMODULE,
+ DSO__ORIG_NOT_FOUND,
+};
+
+char dso__symtab_origin(const struct dso *self);
+void dso__set_long_name(struct dso *self, char *name);
+void dso__set_build_id(struct dso *self, void *build_id);
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
+struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name);
+
+int filename__read_build_id(const char *filename, void *bf, size_t size);
+int sysfs__read_build_id(const char *filename, void *bf, size_t size);
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
+int build_id__sprintf(const u8 *self, int len, char *bf);
+int kallsyms__parse(const char *filename, void *arg,
+ int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start));
+
+void machine__destroy_kernel_maps(struct machine *self);
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
+int machine__create_kernel_maps(struct machine *self);
+
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
+int machines__create_guest_kernel_maps(struct rb_root *self);
+void machines__destroy_guest_kernel_maps(struct rb_root *self);
+
+int symbol__init(void);
+void symbol__exit(void);
+bool symbol_type__is_a(char symbol_type, enum map_type map_type);
+
+size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
+
+char * __weak dso__build_id_filename(struct dso *self, char *bf, size_t size);
+
+#endif /* __PERF_SYMBOL_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 66fd9c2..98cdd20 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -356,7 +356,6 @@ LIB_H += util/debug.h
LIB_H += util/event.h
LIB_H += util/exec_cmd.h
LIB_H += util/levenshtein.h
-LIB_H += util/map.h
LIB_H += util/parse-options.h
LIB_H += util/parse-events.h
LIB_H += util/quote.h
@@ -367,7 +366,6 @@ LIB_H += util/strbuf.h
LIB_H += util/svghelper.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
-LIB_H += util/symbol.h
LIB_H += util/values.h
LIB_H += util/sort.h
LIB_H += util/hist.h
@@ -393,13 +391,11 @@ LIB_OBJS += $(OUTPUT)util/strbuf.o
LIB_OBJS += $(OUTPUT)util/string.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
-LIB_OBJS += $(OUTPUT)util/symbol.o
LIB_OBJS += $(OUTPUT)util/pager.o
LIB_OBJS += $(OUTPUT)util/header.o
LIB_OBJS += $(OUTPUT)util/callchain.o
LIB_OBJS += $(OUTPUT)util/values.o
LIB_OBJS += $(OUTPUT)util/debug.o
-LIB_OBJS += $(OUTPUT)util/map.o
LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index c246b57..774a6c4 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -13,7 +13,7 @@
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
-#include "util/symbol.h"
+#include <perf/config.h>
#include "perf.h"
#include "util/debug.h"
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 43e4faa..96b84f7 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -13,7 +13,8 @@
#include "util/header.h"
#include "util/parse-options.h"
#include <lk/strlist.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
+#include <perf/config.h>
static char const *add_name_list_str, *remove_name_list_str;
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 44a47e1..d2bcbfb 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -13,7 +13,7 @@
#include "util/debug.h"
#include "util/parse-options.h"
#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/config.h>
static char const *input_name = "perf.data";
static bool force;
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 83c0a89..939f52d 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -11,7 +11,7 @@
#include "util/hist.h"
#include "util/session.h"
#include "util/sort.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include <lk/util.h>
#include <stdlib.h>
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 8e3e47b..0de5e1a 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -10,8 +10,8 @@
#include "perf.h"
#include "util/session.h"
#include "util/debug.h"
-
#include "util/parse-options.h"
+#include <perf/config.h>
static char const *input_name = "-";
static bool inject_build_ids;
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index f157a9c..703cc93 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -3,7 +3,7 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 3e5d0fc..b849f99e 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -3,7 +3,7 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 76253d7..6257d26 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -3,7 +3,7 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/thread.h"
#include "util/header.h"
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 9303cab..8099157 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -38,7 +38,8 @@
#include "perf.h"
#include "builtin.h"
#include <lk/strlist.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
+#include <perf/config.h>
#include "util/debug.h"
#include "util/parse-options.h"
#include "util/probe-finder.h"
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 99a71c5..ffde577 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -16,7 +16,7 @@
#include "util/event.h"
#include "util/debug.h"
#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include <lk/cpumap.h>
#include <lk/util.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 2fa7635..a58efba 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -13,7 +13,7 @@
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/callchain.h"
#include <lk/strlist.h>
#include "util/values.h"
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index a9ec0c3..c881900 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -3,7 +3,7 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 1f60239..fc4841d 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -47,6 +47,7 @@
#include "util/header.h"
#include <lk/cpumap.h>
#include "util/thread.h"
+#include <perf/config.h>
#include <sys/prctl.h>
#include <math.h>
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 035b9fa..cda894c 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -9,7 +9,7 @@
#include "util/debug.h"
#include "util/parse-options.h"
#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/thread.h"
static long page_size;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index c0e0dab..43cfc18 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -20,7 +20,7 @@
#include <linux/list.h>
#include "util/cache.h"
#include <lk/rbtree.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/callchain.h"
#include <lk/strlist.h>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 069d53c..bbdced1 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -22,7 +22,7 @@
#include <perf/color.h>
#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/thread.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index beebd43..a6c41c6 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -7,7 +7,7 @@
#include "util/header.h"
#include "util/parse-options.h"
#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/config.h>
#include "util/thread.h"
#include "util/trace-event.h"
#include <lk/util.h>
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index ce4c7d3..8a5d50f 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -10,7 +10,7 @@
#include <stdio.h>
#include "build-id.h"
#include "event.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#include <lk/kernel.h>
#include "debug.h"
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 5dafb00..1d981d6 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -5,6 +5,4 @@
extern struct perf_event_ops build_id__mark_dso_hit_ops;
-char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
-
#endif
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index e0d0dfb..0cd00dd 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -5,7 +5,7 @@
#include <linux/list.h>
#include <lk/rbtree.h>
#include "event.h"
-#include "symbol.h"
+#include <perf/symbol.h>
enum chain_mode {
CHAIN_NONE,
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index a77dd3f..f348bf7 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -8,11 +8,11 @@
#include "cache.h"
#include <perf/color.h>
+#include <perf/config.h>
#include "event.h"
#include "debug.h"
#include <lk/util.h>
-int verbose = 0;
bool dump_trace = false;
int eprintf(int level, const char *fmt, ...)
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 7a17ee0..40def5c 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -5,7 +5,6 @@
#include <stdbool.h>
#include "event.h"
-extern int verbose;
extern bool dump_trace;
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 05587ce..f1e44f6 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -5,7 +5,7 @@
#include <lk/kernel.h>
#include "../perf.h"
-#include "map.h"
+#include <perf/map.h>
/*
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index c64df23..f233545 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -13,7 +13,7 @@
#include "../perf.h"
#include "trace-event.h"
#include "session.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#include "debug.h"
static bool no_buildid_cache = false;
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 587d375..8e4105b 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include "callchain.h"
+#include <perf/config.h>
extern struct callchain_param callchain_param;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
deleted file mode 100644
index 3a7eb6e..0000000
--- a/tools/perf/util/map.c
+++ /dev/null
@@ -1,682 +0,0 @@
-#include "symbol.h"
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include "map.h"
-
-const char *map_type__name[MAP__NR_TYPES] = {
- [MAP__FUNCTION] = "Functions",
- [MAP__VARIABLE] = "Variables",
-};
-
-static inline int is_anon_memory(const char *filename)
-{
- return strcmp(filename, "//anon") == 0;
-}
-
-void map__init(struct map *self, enum map_type type,
- u64 start, u64 end, u64 pgoff, struct dso *dso)
-{
- self->type = type;
- self->start = start;
- self->end = end;
- self->pgoff = pgoff;
- self->dso = dso;
- self->map_ip = map__map_ip;
- self->unmap_ip = map__unmap_ip;
- RB_CLEAR_NODE(&self->rb_node);
- self->groups = NULL;
- self->referenced = false;
-}
-
-struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
- u64 pgoff, u32 pid, char *filename,
- enum map_type type)
-{
- struct map *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- char newfilename[PATH_MAX];
- struct dso *dso;
- int anon;
-
- anon = is_anon_memory(filename);
-
- if (anon) {
- snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
- filename = newfilename;
- }
-
- dso = __dsos__findnew(dsos__list, filename);
- if (dso == NULL)
- goto out_delete;
-
- map__init(self, type, start, start + len, pgoff, dso);
-
- if (anon) {
-set_identity:
- self->map_ip = self->unmap_ip = identity__map_ip;
- } else if (strcmp(filename, "[vdso]") == 0) {
- dso__set_loaded(dso, self->type);
- goto set_identity;
- }
- }
- return self;
-out_delete:
- free(self);
- return NULL;
-}
-
-void map__delete(struct map *self)
-{
- free(self);
-}
-
-void map__fixup_start(struct map *self)
-{
- struct rb_root *symbols = &self->dso->symbols[self->type];
- struct rb_node *nd = rb_first(symbols);
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- self->start = sym->start;
- }
-}
-
-void map__fixup_end(struct map *self)
-{
- struct rb_root *symbols = &self->dso->symbols[self->type];
- struct rb_node *nd = rb_last(symbols);
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- self->end = sym->end;
- }
-}
-
-#define DSO__DELETED "(deleted)"
-
-int map__load(struct map *self, symbol_filter_t filter)
-{
- const char *name = self->dso->long_name;
- int nr;
-
- if (dso__loaded(self->dso, self->type))
- return 0;
-
- nr = dso__load(self->dso, self, filter);
- if (nr < 0) {
- if (self->dso->has_build_id) {
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(self->dso->build_id,
- sizeof(self->dso->build_id),
- sbuild_id);
- pr_warning("%s with build id %s not found",
- name, sbuild_id);
- } else
- pr_warning("Failed to open %s", name);
-
- pr_warning(", continuing without symbols\n");
- return -1;
- } else if (nr == 0) {
- const size_t len = strlen(name);
- const size_t real_len = len - sizeof(DSO__DELETED);
-
- if (len > sizeof(DSO__DELETED) &&
- strcmp(name + real_len + 1, DSO__DELETED) == 0) {
- pr_warning("%.*s was updated, restart the long "
- "running apps that use it!\n",
- (int)real_len, name);
- } else {
- pr_warning("no symbols found in %s, maybe install "
- "a debug package?\n", name);
- }
-
- return -1;
- }
- /*
- * Only applies to the kernel, as its symtabs aren't relative like the
- * module ones.
- */
- if (self->dso->kernel)
- map__reloc_vmlinux(self);
-
- return 0;
-}
-
-struct symbol *map__find_symbol(struct map *self, u64 addr,
- symbol_filter_t filter)
-{
- if (map__load(self, filter) < 0)
- return NULL;
-
- return dso__find_symbol(self->dso, self->type, addr);
-}
-
-struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
- symbol_filter_t filter)
-{
- if (map__load(self, filter) < 0)
- return NULL;
-
- if (!dso__sorted_by_name(self->dso, self->type))
- dso__sort_by_name(self->dso, self->type);
-
- return dso__find_symbol_by_name(self->dso, self->type, name);
-}
-
-struct map *map__clone(struct map *self)
-{
- struct map *map = malloc(sizeof(*self));
-
- if (!map)
- return NULL;
-
- memcpy(map, self, sizeof(*self));
-
- return map;
-}
-
-int map__overlap(struct map *l, struct map *r)
-{
- if (l->start > r->start) {
- struct map *t = l;
- l = r;
- r = t;
- }
-
- if (l->end > r->start)
- return 1;
-
- return 0;
-}
-
-size_t map__fprintf(struct map *self, FILE *fp)
-{
- return fprintf(fp, " %Lx-%Lx %Lx %s\n",
- self->start, self->end, self->pgoff, self->dso->name);
-}
-
-/*
- * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
- * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
- */
-u64 map__rip_2objdump(struct map *map, u64 rip)
-{
- u64 addr = map->dso->adjust_symbols ?
- map->unmap_ip(map, rip) : /* RIP -> IP */
- rip;
- return addr;
-}
-
-u64 map__objdump_2ip(struct map *map, u64 addr)
-{
- u64 ip = map->dso->adjust_symbols ?
- addr :
- map->unmap_ip(map, addr); /* RIP -> IP */
- return ip;
-}
-
-void map_groups__init(struct map_groups *self)
-{
- int i;
- for (i = 0; i < MAP__NR_TYPES; ++i) {
- self->maps[i] = RB_ROOT;
- INIT_LIST_HEAD(&self->removed_maps[i]);
- }
- self->machine = NULL;
-}
-
-static void maps__delete(struct rb_root *self)
-{
- struct rb_node *next = rb_first(self);
-
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
-
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, self);
- map__delete(pos);
- }
-}
-
-static void maps__delete_removed(struct list_head *self)
-{
- struct map *pos, *n;
-
- list_for_each_entry_safe(pos, n, self, node) {
- list_del(&pos->node);
- map__delete(pos);
- }
-}
-
-void map_groups__exit(struct map_groups *self)
-{
- int i;
-
- for (i = 0; i < MAP__NR_TYPES; ++i) {
- maps__delete(&self->maps[i]);
- maps__delete_removed(&self->removed_maps[i]);
- }
-}
-
-void map_groups__flush(struct map_groups *self)
-{
- int type;
-
- for (type = 0; type < MAP__NR_TYPES; type++) {
- struct rb_root *root = &self->maps[type];
- struct rb_node *next = rb_first(root);
-
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, root);
- /*
- * We may have references to this map, for
- * instance in some hist_entry instances, so
- * just move them to a separate list.
- */
- list_add_tail(&pos->node, &self->removed_maps[pos->type]);
- }
- }
-}
-
-struct symbol *map_groups__find_symbol(struct map_groups *self,
- enum map_type type, u64 addr,
- struct map **mapp,
- symbol_filter_t filter)
-{
- struct map *map = map_groups__find(self, type, addr);
-
- if (map != NULL) {
- if (mapp != NULL)
- *mapp = map;
- return map__find_symbol(map, map->map_ip(map, addr), filter);
- }
-
- return NULL;
-}
-
-struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
- enum map_type type,
- const char *name,
- struct map **mapp,
- symbol_filter_t filter)
-{
- struct rb_node *nd;
-
- for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
- struct map *pos = rb_entry(nd, struct map, rb_node);
- struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
-
- if (sym == NULL)
- continue;
- if (mapp != NULL)
- *mapp = pos;
- return sym;
- }
-
- return NULL;
-}
-
-size_t __map_groups__fprintf_maps(struct map_groups *self,
- enum map_type type, int verbose, FILE *fp)
-{
- size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
- struct rb_node *nd;
-
- for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
- struct map *pos = rb_entry(nd, struct map, rb_node);
- printed += fprintf(fp, "Map:");
- printed += map__fprintf(pos, fp);
- if (verbose > 2) {
- printed += dso__fprintf(pos->dso, type, fp);
- printed += fprintf(fp, "--\n");
- }
- }
-
- return printed;
-}
-
-size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp)
-{
- size_t printed = 0, i;
- for (i = 0; i < MAP__NR_TYPES; ++i)
- printed += __map_groups__fprintf_maps(self, i, verbose, fp);
- return printed;
-}
-
-static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
- enum map_type type,
- int verbose, FILE *fp)
-{
- struct map *pos;
- size_t printed = 0;
-
- list_for_each_entry(pos, &self->removed_maps[type], node) {
- printed += fprintf(fp, "Map:");
- printed += map__fprintf(pos, fp);
- if (verbose > 1) {
- printed += dso__fprintf(pos->dso, type, fp);
- printed += fprintf(fp, "--\n");
- }
- }
- return printed;
-}
-
-static size_t map_groups__fprintf_removed_maps(struct map_groups *self,
- int verbose, FILE *fp)
-{
- size_t printed = 0, i;
- for (i = 0; i < MAP__NR_TYPES; ++i)
- printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp);
- return printed;
-}
-
-size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp)
-{
- size_t printed = map_groups__fprintf_maps(self, verbose, fp);
- printed += fprintf(fp, "Removed maps:\n");
- return printed + map_groups__fprintf_removed_maps(self, verbose, fp);
-}
-
-int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
- int verbose, FILE *fp)
-{
- struct rb_root *root = &self->maps[map->type];
- struct rb_node *next = rb_first(root);
- int err = 0;
-
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
- next = rb_next(&pos->rb_node);
-
- if (!map__overlap(pos, map))
- continue;
-
- if (verbose >= 2) {
- fputs("overlapping maps:\n", fp);
- map__fprintf(map, fp);
- map__fprintf(pos, fp);
- }
-
- rb_erase(&pos->rb_node, root);
- /*
- * Now check if we need to create new maps for areas not
- * overlapped by the new map:
- */
- if (map->start > pos->start) {
- struct map *before = map__clone(pos);
-
- if (before == NULL) {
- err = -ENOMEM;
- goto move_map;
- }
-
- before->end = map->start - 1;
- map_groups__insert(self, before);
- if (verbose >= 2)
- map__fprintf(before, fp);
- }
-
- if (map->end < pos->end) {
- struct map *after = map__clone(pos);
-
- if (after == NULL) {
- err = -ENOMEM;
- goto move_map;
- }
-
- after->start = map->end + 1;
- map_groups__insert(self, after);
- if (verbose >= 2)
- map__fprintf(after, fp);
- }
-move_map:
- /*
- * If we have references, just move them to a separate list.
- */
- if (pos->referenced)
- list_add_tail(&pos->node, &self->removed_maps[map->type]);
- else
- map__delete(pos);
-
- if (err)
- return err;
- }
-
- return 0;
-}
-
-/*
- * XXX This should not really _copy_ te maps, but refcount them.
- */
-int map_groups__clone(struct map_groups *self,
- struct map_groups *parent, enum map_type type)
-{
- struct rb_node *nd;
- for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
- struct map *map = rb_entry(nd, struct map, rb_node);
- struct map *new = map__clone(map);
- if (new == NULL)
- return -ENOMEM;
- map_groups__insert(self, new);
- }
- return 0;
-}
-
-static u64 map__reloc_map_ip(struct map *map, u64 ip)
-{
- return ip + (s64)map->pgoff;
-}
-
-static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
-{
- return ip - (s64)map->pgoff;
-}
-
-void map__reloc_vmlinux(struct map *self)
-{
- struct kmap *kmap = map__kmap(self);
- s64 reloc;
-
- if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
- return;
-
- reloc = (kmap->ref_reloc_sym->unrelocated_addr -
- kmap->ref_reloc_sym->addr);
-
- if (!reloc)
- return;
-
- self->map_ip = map__reloc_map_ip;
- self->unmap_ip = map__reloc_unmap_ip;
- self->pgoff = reloc;
-}
-
-void maps__insert(struct rb_root *maps, struct map *map)
-{
- struct rb_node **p = &maps->rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = map->start;
- struct map *m;
-
- while (*p != NULL) {
- parent = *p;
- m = rb_entry(parent, struct map, rb_node);
- if (ip < m->start)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&map->rb_node, parent, p);
- rb_insert_color(&map->rb_node, maps);
-}
-
-void maps__remove(struct rb_root *self, struct map *map)
-{
- rb_erase(&map->rb_node, self);
-}
-
-struct map *maps__find(struct rb_root *maps, u64 ip)
-{
- struct rb_node **p = &maps->rb_node;
- struct rb_node *parent = NULL;
- struct map *m;
-
- while (*p != NULL) {
- parent = *p;
- m = rb_entry(parent, struct map, rb_node);
- if (ip < m->start)
- p = &(*p)->rb_left;
- else if (ip > m->end)
- p = &(*p)->rb_right;
- else
- return m;
- }
-
- return NULL;
-}
-
-int machine__init(struct machine *self, const char *root_dir, pid_t pid)
-{
- map_groups__init(&self->kmaps);
- RB_CLEAR_NODE(&self->rb_node);
- INIT_LIST_HEAD(&self->user_dsos);
- INIT_LIST_HEAD(&self->kernel_dsos);
-
- self->kmaps.machine = self;
- self->pid = pid;
- self->root_dir = strdup(root_dir);
- return self->root_dir == NULL ? -ENOMEM : 0;
-}
-
-static void dsos__delete(struct list_head *self)
-{
- struct dso *pos, *n;
-
- list_for_each_entry_safe(pos, n, self, node) {
- list_del(&pos->node);
- dso__delete(pos);
- }
-}
-
-void machine__exit(struct machine *self)
-{
- map_groups__exit(&self->kmaps);
- dsos__delete(&self->user_dsos);
- dsos__delete(&self->kernel_dsos);
- free(self->root_dir);
- self->root_dir = NULL;
-}
-
-void machine__delete(struct machine *self)
-{
- machine__exit(self);
- free(self);
-}
-
-struct machine *machines__add(struct rb_root *self, pid_t pid,
- const char *root_dir)
-{
- struct rb_node **p = &self->rb_node;
- struct rb_node *parent = NULL;
- struct machine *pos, *machine = malloc(sizeof(*machine));
-
- if (!machine)
- return NULL;
-
- if (machine__init(machine, root_dir, pid) != 0) {
- free(machine);
- return NULL;
- }
-
- while (*p != NULL) {
- parent = *p;
- pos = rb_entry(parent, struct machine, rb_node);
- if (pid < pos->pid)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&machine->rb_node, parent, p);
- rb_insert_color(&machine->rb_node, self);
-
- return machine;
-}
-
-struct machine *machines__find(struct rb_root *self, pid_t pid)
-{
- struct rb_node **p = &self->rb_node;
- struct rb_node *parent = NULL;
- struct machine *machine;
- struct machine *default_machine = NULL;
-
- while (*p != NULL) {
- parent = *p;
- machine = rb_entry(parent, struct machine, rb_node);
- if (pid < machine->pid)
- p = &(*p)->rb_left;
- else if (pid > machine->pid)
- p = &(*p)->rb_right;
- else
- return machine;
- if (!machine->pid)
- default_machine = machine;
- }
-
- return default_machine;
-}
-
-struct machine *machines__findnew(struct rb_root *self, pid_t pid)
-{
- char path[PATH_MAX];
- const char *root_dir;
- struct machine *machine = machines__find(self, pid);
-
- if (!machine || machine->pid != pid) {
- if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
- root_dir = "";
- else {
- if (!symbol_conf.guestmount)
- goto out;
- sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
- if (access(path, R_OK)) {
- pr_err("Can't access file %s\n", path);
- goto out;
- }
- root_dir = path;
- }
- machine = machines__add(self, pid, root_dir);
- }
-
-out:
- return machine;
-}
-
-void machines__process(struct rb_root *self, machine__process_t process, void *data)
-{
- struct rb_node *nd;
-
- for (nd = rb_first(self); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- process(pos, data);
- }
-}
-
-char *machine__mmap_name(struct machine *self, char *bf, size_t size)
-{
- if (machine__is_host(self))
- snprintf(bf, size, "[%s]", "kernel.kallsyms");
- else if (machine__is_default_guest(self))
- snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
- else
- snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
-
- return bf;
-}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
deleted file mode 100644
index efbff5b..0000000
--- a/tools/perf/util/map.h
+++ /dev/null
@@ -1,229 +0,0 @@
-#ifndef __PERF_MAP_H
-#define __PERF_MAP_H
-
-#include <lk/compiler.h>
-#include <linux/list.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <sys/types.h>
-#include <lk/types.h>
-#include <lk/rbtree.h>
-#include <lk/kernel.h>
-
-enum map_type {
- MAP__FUNCTION = 0,
- MAP__VARIABLE,
-};
-
-#define MAP__NR_TYPES (MAP__VARIABLE + 1)
-
-extern const char *map_type__name[MAP__NR_TYPES];
-
-struct dso;
-struct ref_reloc_sym;
-struct map_groups;
-struct machine;
-
-struct map {
- union {
- struct rb_node rb_node;
- struct list_head node;
- };
- u64 start;
- u64 end;
- u8 /* enum map_type */ type;
- bool referenced;
- u32 priv;
- u64 pgoff;
-
- /* ip -> dso rip */
- u64 (*map_ip)(struct map *, u64);
- /* dso rip -> ip */
- u64 (*unmap_ip)(struct map *, u64);
-
- struct dso *dso;
- struct map_groups *groups;
-};
-
-struct kmap {
- struct ref_reloc_sym *ref_reloc_sym;
- struct map_groups *kmaps;
-};
-
-struct map_groups {
- struct rb_root maps[MAP__NR_TYPES];
- struct list_head removed_maps[MAP__NR_TYPES];
- struct machine *machine;
-};
-
-/* Native host kernel uses -1 as pid index in machine */
-#define HOST_KERNEL_ID (-1)
-#define DEFAULT_GUEST_KERNEL_ID (0)
-
-struct machine {
- struct rb_node rb_node;
- pid_t pid;
- char *root_dir;
- struct list_head user_dsos;
- struct list_head kernel_dsos;
- struct map_groups kmaps;
- struct map *vmlinux_maps[MAP__NR_TYPES];
-};
-
-static inline
-struct map *machine__kernel_map(struct machine *self, enum map_type type)
-{
- return self->vmlinux_maps[type];
-}
-
-static inline struct kmap *map__kmap(struct map *self)
-{
- return (struct kmap *)(self + 1);
-}
-
-static inline u64 map__map_ip(struct map *map, u64 ip)
-{
- return ip - map->start + map->pgoff;
-}
-
-static inline u64 map__unmap_ip(struct map *map, u64 ip)
-{
- return ip + map->start - map->pgoff;
-}
-
-static inline u64 identity__map_ip(struct map *map __used, u64 ip)
-{
- return ip;
-}
-
-
-/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
-u64 map__rip_2objdump(struct map *map, u64 rip);
-u64 map__objdump_2ip(struct map *map, u64 addr);
-
-struct symbol;
-
-typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
-
-void map__init(struct map *self, enum map_type type,
- u64 start, u64 end, u64 pgoff, struct dso *dso);
-struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
- u64 pgoff, u32 pid, char *filename,
- enum map_type type);
-void map__delete(struct map *self);
-struct map *map__clone(struct map *self);
-int map__overlap(struct map *l, struct map *r);
-size_t map__fprintf(struct map *self, FILE *fp);
-
-int map__load(struct map *self, symbol_filter_t filter);
-struct symbol *map__find_symbol(struct map *self,
- u64 addr, symbol_filter_t filter);
-struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
- symbol_filter_t filter);
-void map__fixup_start(struct map *self);
-void map__fixup_end(struct map *self);
-
-void map__reloc_vmlinux(struct map *self);
-
-size_t __map_groups__fprintf_maps(struct map_groups *self,
- enum map_type type, int verbose, FILE *fp);
-void maps__insert(struct rb_root *maps, struct map *map);
-void maps__remove(struct rb_root *self, struct map *map);
-struct map *maps__find(struct rb_root *maps, u64 addr);
-void map_groups__init(struct map_groups *self);
-void map_groups__exit(struct map_groups *self);
-int map_groups__clone(struct map_groups *self,
- struct map_groups *parent, enum map_type type);
-size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
-size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
-
-typedef void (*machine__process_t)(struct machine *self, void *data);
-
-void machines__process(struct rb_root *self, machine__process_t process, void *data);
-struct machine *machines__add(struct rb_root *self, pid_t pid,
- const char *root_dir);
-struct machine *machines__find_host(struct rb_root *self);
-struct machine *machines__find(struct rb_root *self, pid_t pid);
-struct machine *machines__findnew(struct rb_root *self, pid_t pid);
-char *machine__mmap_name(struct machine *self, char *bf, size_t size);
-int machine__init(struct machine *self, const char *root_dir, pid_t pid);
-void machine__exit(struct machine *self);
-void machine__delete(struct machine *self);
-
-/*
- * Default guest kernel is defined by parameter --guestkallsyms
- * and --guestmodules
- */
-static inline bool machine__is_default_guest(struct machine *self)
-{
- return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
-}
-
-static inline bool machine__is_host(struct machine *self)
-{
- return self ? self->pid == HOST_KERNEL_ID : false;
-}
-
-static inline void map_groups__insert(struct map_groups *self, struct map *map)
-{
- maps__insert(&self->maps[map->type], map);
- map->groups = self;
-}
-
-static inline void map_groups__remove(struct map_groups *self, struct map *map)
-{
- maps__remove(&self->maps[map->type], map);
-}
-
-static inline struct map *map_groups__find(struct map_groups *self,
- enum map_type type, u64 addr)
-{
- return maps__find(&self->maps[type], addr);
-}
-
-struct symbol *map_groups__find_symbol(struct map_groups *self,
- enum map_type type, u64 addr,
- struct map **mapp,
- symbol_filter_t filter);
-
-struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
- enum map_type type,
- const char *name,
- struct map **mapp,
- symbol_filter_t filter);
-
-static inline
-struct symbol *machine__find_kernel_symbol(struct machine *self,
- enum map_type type, u64 addr,
- struct map **mapp,
- symbol_filter_t filter)
-{
- return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
-}
-
-static inline
-struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
- struct map **mapp,
- symbol_filter_t filter)
-{
- return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
-}
-
-static inline
-struct symbol *map_groups__find_function_by_name(struct map_groups *self,
- const char *name, struct map **mapp,
- symbol_filter_t filter)
-{
- return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
-}
-
-int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
- int verbose, FILE *fp);
-
-struct map *map_groups__find_by_name(struct map_groups *self,
- enum map_type type, const char *name);
-struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
-
-void map_groups__flush(struct map_groups *self);
-
-#endif /* __PERF_MAP_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 01686b5..ad36237 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -7,7 +7,7 @@
#include "parse-events.h"
#include "exec_cmd.h"
#include "string.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#include "cache.h"
#include "header.h"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a6c8d3e..577944c 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -42,7 +42,7 @@
#include "debug.h"
#include "cache.h"
#include <perf/color.h>
-#include "symbol.h"
+#include <perf/symbol.h>
#include "thread.h"
#include "trace-event.h" /* For __unused */
#include "probe-event.h"
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c7a8bdb..1c23a9d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -36,7 +36,7 @@
#include "event.h"
#include "debug.h"
#include <lk/util.h>
-#include "symbol.h"
+#include <perf/symbol.h>
#include "probe-finder.h"
/* Kprobe tracer basic type is up to u64 */
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 6f2af58..950cea2 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -4,7 +4,7 @@
#include "hist.h"
#include "event.h"
#include "header.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#include "thread.h"
#include <lk/rbtree.h>
#include "../../../include/linux/perf_event.h"
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index e2fb61a..cc461d6 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -8,7 +8,7 @@
#include <linux/list.h>
#include "cache.h"
#include <lk/rbtree.h>
-#include "symbol.h"
+#include <perf/symbol.h>
#include "string.h"
#include "callchain.h"
#include <lk/strlist.h>
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
deleted file mode 100644
index 0319325..0000000
--- a/tools/perf/util/symbol.c
+++ /dev/null
@@ -1,2459 +0,0 @@
-#define _GNU_SOURCE
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <libgen.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include "build-id.h"
-#include "debug.h"
-#include "symbol.h"
-#include <lk/strlist.h>
-
-#include <libelf.h>
-#include <gelf.h>
-#include <elf.h>
-#include <limits.h>
-#include <sys/utsname.h>
-
-#ifndef NT_GNU_BUILD_ID
-#define NT_GNU_BUILD_ID 3
-#endif
-
-static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
-static int elf_read_build_id(Elf *elf, void *bf, size_t size);
-static void dsos__add(struct list_head *head, struct dso *dso);
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
-static int dso__load_kernel_sym(struct dso *self, struct map *map,
- symbol_filter_t filter);
-static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
- symbol_filter_t filter);
-static int vmlinux_path__nr_entries;
-static char **vmlinux_path;
-
-struct symbol_conf symbol_conf = {
- .exclude_other = true,
- .use_modules = true,
- .try_vmlinux_path = true,
-};
-
-int dso__name_len(const struct dso *self)
-{
- if (verbose)
- return self->long_name_len;
-
- return self->short_name_len;
-}
-
-bool dso__loaded(const struct dso *self, enum map_type type)
-{
- return self->loaded & (1 << type);
-}
-
-bool dso__sorted_by_name(const struct dso *self, enum map_type type)
-{
- return self->sorted_by_name & (1 << type);
-}
-
-static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
-{
- self->sorted_by_name |= (1 << type);
-}
-
-bool symbol_type__is_a(char symbol_type, enum map_type map_type)
-{
- switch (map_type) {
- case MAP__FUNCTION:
- return symbol_type == 'T' || symbol_type == 'W';
- case MAP__VARIABLE:
- return symbol_type == 'D' || symbol_type == 'd';
- default:
- return false;
- }
-}
-
-static void symbols__fixup_end(struct rb_root *self)
-{
- struct rb_node *nd, *prevnd = rb_first(self);
- struct symbol *curr, *prev;
-
- if (prevnd == NULL)
- return;
-
- curr = rb_entry(prevnd, struct symbol, rb_node);
-
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
- prev = curr;
- curr = rb_entry(nd, struct symbol, rb_node);
-
- if (prev->end == prev->start)
- prev->end = curr->start - 1;
- }
-
- /* Last entry */
- if (curr->end == curr->start)
- curr->end = roundup(curr->start, 4096);
-}
-
-static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
-{
- struct map *prev, *curr;
- struct rb_node *nd, *prevnd = rb_first(&self->maps[type]);
-
- if (prevnd == NULL)
- return;
-
- curr = rb_entry(prevnd, struct map, rb_node);
-
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
- prev = curr;
- curr = rb_entry(nd, struct map, rb_node);
- prev->end = curr->start - 1;
- }
-
- /*
- * We still haven't the actual symbols, so guess the
- * last map final address.
- */
- curr->end = ~0UL;
-}
-
-static void map_groups__fixup_end(struct map_groups *self)
-{
- int i;
- for (i = 0; i < MAP__NR_TYPES; ++i)
- __map_groups__fixup_end(self, i);
-}
-
-static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
- const char *name)
-{
- size_t namelen = strlen(name) + 1;
- struct symbol *self = calloc(1, (symbol_conf.priv_size +
- sizeof(*self) + namelen));
- if (self == NULL)
- return NULL;
-
- if (symbol_conf.priv_size)
- self = ((void *)self) + symbol_conf.priv_size;
-
- self->start = start;
- self->end = len ? start + len - 1 : start;
- self->binding = binding;
- self->namelen = namelen - 1;
-
- pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
-
- memcpy(self->name, name, namelen);
-
- return self;
-}
-
-void symbol__delete(struct symbol *self)
-{
- free(((void *)self) - symbol_conf.priv_size);
-}
-
-static size_t symbol__fprintf(struct symbol *self, FILE *fp)
-{
- return fprintf(fp, " %llx-%llx %c %s\n",
- self->start, self->end,
- self->binding == STB_GLOBAL ? 'g' :
- self->binding == STB_LOCAL ? 'l' : 'w',
- self->name);
-}
-
-void dso__set_long_name(struct dso *self, char *name)
-{
- if (name == NULL)
- return;
- self->long_name = name;
- self->long_name_len = strlen(name);
-}
-
-static void dso__set_short_name(struct dso *self, const char *name)
-{
- if (name == NULL)
- return;
- self->short_name = name;
- self->short_name_len = strlen(name);
-}
-
-static void dso__set_basename(struct dso *self)
-{
- dso__set_short_name(self, basename(self->long_name));
-}
-
-struct dso *dso__new(const char *name)
-{
- struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1);
-
- if (self != NULL) {
- int i;
- strcpy(self->name, name);
- dso__set_long_name(self, self->name);
- dso__set_short_name(self, self->name);
- for (i = 0; i < MAP__NR_TYPES; ++i)
- self->symbols[i] = self->symbol_names[i] = RB_ROOT;
- self->slen_calculated = 0;
- self->origin = DSO__ORIG_NOT_FOUND;
- self->loaded = 0;
- self->sorted_by_name = 0;
- self->has_build_id = 0;
- self->kernel = DSO_TYPE_USER;
- INIT_LIST_HEAD(&self->node);
- }
-
- return self;
-}
-
-static void symbols__delete(struct rb_root *self)
-{
- struct symbol *pos;
- struct rb_node *next = rb_first(self);
-
- while (next) {
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, self);
- symbol__delete(pos);
- }
-}
-
-void dso__delete(struct dso *self)
-{
- int i;
- for (i = 0; i < MAP__NR_TYPES; ++i)
- symbols__delete(&self->symbols[i]);
- if (self->sname_alloc)
- free((char *)self->short_name);
- if (self->lname_alloc)
- free(self->long_name);
- free(self);
-}
-
-void dso__set_build_id(struct dso *self, void *build_id)
-{
- memcpy(self->build_id, build_id, sizeof(self->build_id));
- self->has_build_id = 1;
-}
-
-static void symbols__insert(struct rb_root *self, struct symbol *sym)
-{
- struct rb_node **p = &self->rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = sym->start;
- struct symbol *s;
-
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol, rb_node);
- if (ip < s->start)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&sym->rb_node, parent, p);
- rb_insert_color(&sym->rb_node, self);
-}
-
-static struct symbol *symbols__find(struct rb_root *self, u64 ip)
-{
- struct rb_node *n;
-
- if (self == NULL)
- return NULL;
-
- n = self->rb_node;
-
- while (n) {
- struct symbol *s = rb_entry(n, struct symbol, rb_node);
-
- if (ip < s->start)
- n = n->rb_left;
- else if (ip > s->end)
- n = n->rb_right;
- else
- return s;
- }
-
- return NULL;
-}
-
-struct symbol_name_rb_node {
- struct rb_node rb_node;
- struct symbol sym;
-};
-
-static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
-{
- struct rb_node **p = &self->rb_node;
- struct rb_node *parent = NULL;
- struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s;
-
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
- if (strcmp(sym->name, s->sym.name) < 0)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&symn->rb_node, parent, p);
- rb_insert_color(&symn->rb_node, self);
-}
-
-static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source)
-{
- struct rb_node *nd;
-
- for (nd = rb_first(source); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- symbols__insert_by_name(self, pos);
- }
-}
-
-static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name)
-{
- struct rb_node *n;
-
- if (self == NULL)
- return NULL;
-
- n = self->rb_node;
-
- while (n) {
- struct symbol_name_rb_node *s;
- int cmp;
-
- s = rb_entry(n, struct symbol_name_rb_node, rb_node);
- cmp = strcmp(name, s->sym.name);
-
- if (cmp < 0)
- n = n->rb_left;
- else if (cmp > 0)
- n = n->rb_right;
- else
- return &s->sym;
- }
-
- return NULL;
-}
-
-struct symbol *dso__find_symbol(struct dso *self,
- enum map_type type, u64 addr)
-{
- return symbols__find(&self->symbols[type], addr);
-}
-
-struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
- const char *name)
-{
- return symbols__find_by_name(&self->symbol_names[type], name);
-}
-
-void dso__sort_by_name(struct dso *self, enum map_type type)
-{
- dso__set_sorted_by_name(self, type);
- return symbols__sort_by_name(&self->symbol_names[type],
- &self->symbols[type]);
-}
-
-int build_id__sprintf(const u8 *self, int len, char *bf)
-{
- char *bid = bf;
- const u8 *raw = self;
- int i;
-
- for (i = 0; i < len; ++i) {
- sprintf(bid, "%02x", *raw);
- ++raw;
- bid += 2;
- }
-
- return raw - self;
-}
-
-size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
-{
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
- return fprintf(fp, "%s", sbuild_id);
-}
-
-size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
-{
- struct rb_node *nd;
- size_t ret = fprintf(fp, "dso: %s (", self->short_name);
-
- if (self->short_name != self->long_name)
- ret += fprintf(fp, "%s, ", self->long_name);
- ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
- self->loaded ? "" : "NOT ");
- ret += dso__fprintf_buildid(self, fp);
- ret += fprintf(fp, ")\n");
- for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- ret += symbol__fprintf(pos, fp);
- }
-
- return ret;
-}
-
-int kallsyms__parse(const char *filename, void *arg,
- int (*process_symbol)(void *arg, const char *name,
- char type, u64 start))
-{
- char *line = NULL;
- size_t n;
- int err = 0;
- FILE *file = fopen(filename, "r");
-
- if (file == NULL)
- goto out_failure;
-
- while (!feof(file)) {
- u64 start;
- int line_len, len;
- char symbol_type;
- char *symbol_name;
-
- line_len = getline(&line, &n, file);
- if (line_len < 0 || !line)
- break;
-
- line[--line_len] = '\0'; /* \n */
-
- len = hex2u64(line, &start);
-
- len++;
- if (len + 2 >= line_len)
- continue;
-
- symbol_type = toupper(line[len]);
- symbol_name = line + len + 2;
-
- err = process_symbol(arg, symbol_name, symbol_type, start);
- if (err)
- break;
- }
-
- free(line);
- fclose(file);
- return err;
-
-out_failure:
- return -1;
-}
-
-struct process_kallsyms_args {
- struct map *map;
- struct dso *dso;
-};
-
-static u8 kallsyms2elf_type(char type)
-{
- if (type == 'W')
- return STB_WEAK;
-
- return isupper(type) ? STB_GLOBAL : STB_LOCAL;
-}
-
-static int map__process_kallsym_symbol(void *arg, const char *name,
- char type, u64 start)
-{
- struct symbol *sym;
- struct process_kallsyms_args *a = arg;
- struct rb_root *root = &a->dso->symbols[a->map->type];
-
- if (!symbol_type__is_a(type, a->map->type))
- return 0;
-
- /*
- * Will fix up the end later, when we have all symbols sorted.
- */
- sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
-
- if (sym == NULL)
- return -ENOMEM;
- /*
- * We will pass the symbols to the filter later, in
- * map__split_kallsyms, when we have split the maps per module
- */
- symbols__insert(root, sym);
-
- return 0;
-}
-
-/*
- * Loads the function entries in /proc/kallsyms into kernel_map->dso,
- * so that we can in the next step set the symbol ->end address and then
- * call kernel_maps__split_kallsyms.
- */
-static int dso__load_all_kallsyms(struct dso *self, const char *filename,
- struct map *map)
-{
- struct process_kallsyms_args args = { .map = map, .dso = self, };
- return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
-}
-
-/*
- * Split the symbols into maps, making sure there are no overlaps, i.e. the
- * kernel range is broken in several maps, named [kernel].N, as we don't have
- * the original ELF section names vmlinux have.
- */
-static int dso__split_kallsyms(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- struct map_groups *kmaps = map__kmap(map)->kmaps;
- struct machine *machine = kmaps->machine;
- struct map *curr_map = map;
- struct symbol *pos;
- int count = 0;
- struct rb_root *root = &self->symbols[map->type];
- struct rb_node *next = rb_first(root);
- int kernel_range = 0;
-
- while (next) {
- char *module;
-
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
-
- module = strchr(pos->name, '\t');
- if (module) {
- if (!symbol_conf.use_modules)
- goto discard_symbol;
-
- *module++ = '\0';
-
- if (strcmp(curr_map->dso->short_name, module)) {
- if (curr_map != map &&
- self->kernel == DSO_TYPE_GUEST_KERNEL &&
- machine__is_default_guest(machine)) {
- /*
- * We assume all symbols of a module are
- * continuous in * kallsyms, so curr_map
- * points to a module and all its
- * symbols are in its kmap. Mark it as
- * loaded.
- */
- dso__set_loaded(curr_map->dso,
- curr_map->type);
- }
-
- curr_map = map_groups__find_by_name(kmaps,
- map->type, module);
- if (curr_map == NULL) {
- pr_debug("%s/proc/{kallsyms,modules} "
- "inconsistency while looking "
- "for \"%s\" module!\n",
- machine->root_dir, module);
- curr_map = map;
- goto discard_symbol;
- }
-
- if (curr_map->dso->loaded &&
- !machine__is_default_guest(machine))
- goto discard_symbol;
- }
- /*
- * So that we look just like we get from .ko files,
- * i.e. not prelinked, relative to map->start.
- */
- pos->start = curr_map->map_ip(curr_map, pos->start);
- pos->end = curr_map->map_ip(curr_map, pos->end);
- } else if (curr_map != map) {
- char dso_name[PATH_MAX];
- struct dso *dso;
-
- if (self->kernel == DSO_TYPE_GUEST_KERNEL)
- snprintf(dso_name, sizeof(dso_name),
- "[guest.kernel].%d",
- kernel_range++);
- else
- snprintf(dso_name, sizeof(dso_name),
- "[kernel].%d",
- kernel_range++);
-
- dso = dso__new(dso_name);
- if (dso == NULL)
- return -1;
-
- dso->kernel = self->kernel;
-
- curr_map = map__new2(pos->start, dso, map->type);
- if (curr_map == NULL) {
- dso__delete(dso);
- return -1;
- }
-
- curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
- map_groups__insert(kmaps, curr_map);
- ++kernel_range;
- }
-
- if (filter && filter(curr_map, pos)) {
-discard_symbol: rb_erase(&pos->rb_node, root);
- symbol__delete(pos);
- } else {
- if (curr_map != map) {
- rb_erase(&pos->rb_node, root);
- symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
- }
- count++;
- }
- }
-
- if (curr_map != map &&
- self->kernel == DSO_TYPE_GUEST_KERNEL &&
- machine__is_default_guest(kmaps->machine)) {
- dso__set_loaded(curr_map->dso, curr_map->type);
- }
-
- return count;
-}
-
-int dso__load_kallsyms(struct dso *self, const char *filename,
- struct map *map, symbol_filter_t filter)
-{
- if (dso__load_all_kallsyms(self, filename, map) < 0)
- return -1;
-
- symbols__fixup_end(&self->symbols[map->type]);
- if (self->kernel == DSO_TYPE_GUEST_KERNEL)
- self->origin = DSO__ORIG_GUEST_KERNEL;
- else
- self->origin = DSO__ORIG_KERNEL;
-
- return dso__split_kallsyms(self, map, filter);
-}
-
-static int dso__load_perf_map(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- char *line = NULL;
- size_t n;
- FILE *file;
- int nr_syms = 0;
-
- file = fopen(self->long_name, "r");
- if (file == NULL)
- goto out_failure;
-
- while (!feof(file)) {
- u64 start, size;
- struct symbol *sym;
- int line_len, len;
-
- line_len = getline(&line, &n, file);
- if (line_len < 0)
- break;
-
- if (!line)
- goto out_failure;
-
- line[--line_len] = '\0'; /* \n */
-
- len = hex2u64(line, &start);
-
- len++;
- if (len + 2 >= line_len)
- continue;
-
- len += hex2u64(line + len, &size);
-
- len++;
- if (len + 2 >= line_len)
- continue;
-
- sym = symbol__new(start, size, STB_GLOBAL, line + len);
-
- if (sym == NULL)
- goto out_delete_line;
-
- if (filter && filter(map, sym))
- symbol__delete(sym);
- else {
- symbols__insert(&self->symbols[map->type], sym);
- nr_syms++;
- }
- }
-
- free(line);
- fclose(file);
-
- return nr_syms;
-
-out_delete_line:
- free(line);
-out_failure:
- return -1;
-}
-
-/**
- * elf_symtab__for_each_symbol - iterate thru all the symbols
- *
- * @self: struct elf_symtab instance to iterate
- * @idx: uint32_t idx
- * @sym: GElf_Sym iterator
- */
-#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
- for (idx = 0, gelf_getsym(syms, idx, &sym);\
- idx < nr_syms; \
- idx++, gelf_getsym(syms, idx, &sym))
-
-static inline uint8_t elf_sym__type(const GElf_Sym *sym)
-{
- return GELF_ST_TYPE(sym->st_info);
-}
-
-static inline int elf_sym__is_function(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_FUNC &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline bool elf_sym__is_object(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_OBJECT &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline int elf_sym__is_label(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_NOTYPE &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF &&
- sym->st_shndx != SHN_ABS;
-}
-
-static inline const char *elf_sec__name(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return secstrs->d_buf + shdr->sh_name;
-}
-
-static inline int elf_sec__is_text(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
-}
-
-static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
-}
-
-static inline const char *elf_sym__name(const GElf_Sym *sym,
- const Elf_Data *symstrs)
-{
- return symstrs->d_buf + sym->st_name;
-}
-
-static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
- GElf_Shdr *shp, const char *name,
- size_t *idx)
-{
- Elf_Scn *sec = NULL;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- char *str;
-
- gelf_getshdr(sec, shp);
- str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
- if (!strcmp(name, str)) {
- if (idx)
- *idx = cnt;
- break;
- }
- ++cnt;
- }
-
- return sec;
-}
-
-#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
-
-#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
-
-/*
- * We need to check if we have a .dynsym, so that we can handle the
- * .plt, synthesizing its symbols, that aren't on the symtabs (be it
- * .dynsym or .symtab).
- * And always look at the original dso, not at debuginfo packages, that
- * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
- */
-static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- uint32_t nr_rel_entries, idx;
- GElf_Sym sym;
- u64 plt_offset;
- GElf_Shdr shdr_plt;
- struct symbol *f;
- GElf_Shdr shdr_rel_plt, shdr_dynsym;
- Elf_Data *reldata, *syms, *symstrs;
- Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
- size_t dynsym_idx;
- GElf_Ehdr ehdr;
- char sympltname[1024];
- Elf *elf;
- int nr = 0, symidx, fd, err = 0;
-
- fd = open(self->long_name, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- goto out_close;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_elf_end;
-
- scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
- ".dynsym", &dynsym_idx);
- if (scn_dynsym == NULL)
- goto out_elf_end;
-
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rela.plt", NULL);
- if (scn_plt_rel == NULL) {
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rel.plt", NULL);
- if (scn_plt_rel == NULL)
- goto out_elf_end;
- }
-
- err = -1;
-
- if (shdr_rel_plt.sh_link != dynsym_idx)
- goto out_elf_end;
-
- if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
- goto out_elf_end;
-
- /*
- * Fetch the relocation section to find the idxes to the GOT
- * and the symbols in the .dynsym they refer to.
- */
- reldata = elf_getdata(scn_plt_rel, NULL);
- if (reldata == NULL)
- goto out_elf_end;
-
- syms = elf_getdata(scn_dynsym, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
- if (scn_symstrs == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(scn_symstrs, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- plt_offset = shdr_plt.sh_offset;
-
- if (shdr_rel_plt.sh_type == SHT_RELA) {
- GElf_Rela pos_mem, *pos;
-
- elf_section__for_each_rela(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&self->symbols[map->type], f);
- ++nr;
- }
- }
- } else if (shdr_rel_plt.sh_type == SHT_REL) {
- GElf_Rel pos_mem, *pos;
- elf_section__for_each_rel(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&self->symbols[map->type], f);
- ++nr;
- }
- }
- }
-
- err = 0;
-out_elf_end:
- elf_end(elf);
-out_close:
- close(fd);
-
- if (err == 0)
- return nr;
-out:
- pr_debug("%s: problems reading %s PLT info.\n",
- __func__, self->long_name);
- return 0;
-}
-
-static bool elf_sym__is_a(GElf_Sym *self, enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sym__is_function(self);
- case MAP__VARIABLE:
- return elf_sym__is_object(self);
- default:
- return false;
- }
-}
-
-static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sec__is_text(self, secstrs);
- case MAP__VARIABLE:
- return elf_sec__is_data(self, secstrs);
- default:
- return false;
- }
-}
-
-static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
-{
- Elf_Scn *sec = NULL;
- GElf_Shdr shdr;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- gelf_getshdr(sec, &shdr);
-
- if ((addr >= shdr.sh_addr) &&
- (addr < (shdr.sh_addr + shdr.sh_size)))
- return cnt;
-
- ++cnt;
- }
-
- return -1;
-}
-
-static int dso__load_sym(struct dso *self, struct map *map, const char *name,
- int fd, symbol_filter_t filter, int kmodule,
- int want_symtab)
-{
- struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
- struct map *curr_map = map;
- struct dso *curr_dso = self;
- Elf_Data *symstrs, *secstrs;
- uint32_t nr_syms;
- int err = -1;
- uint32_t idx;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr, opdshdr;
- Elf_Data *syms, *opddata = NULL;
- GElf_Sym sym;
- Elf_Scn *sec, *sec_strndx, *opdsec;
- Elf *elf;
- int nr = 0;
- size_t opdidx = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
- goto out_close;
- }
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_debug("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
- }
-
- /* Always reject images with a mismatched build-id: */
- if (self->has_build_id) {
- u8 build_id[BUILD_ID_SIZE];
-
- if (elf_read_build_id(elf, build_id,
- BUILD_ID_SIZE) != BUILD_ID_SIZE)
- goto out_elf_end;
-
- if (!dso__build_id_equal(self, build_id))
- goto out_elf_end;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
- if (sec == NULL) {
- if (want_symtab)
- goto out_elf_end;
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
- if (sec == NULL)
- goto out_elf_end;
- }
-
- opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
- if (opdsec)
- opddata = elf_rawdata(opdsec, NULL);
-
- syms = elf_getdata(sec, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- sec = elf_getscn(elf, shdr.sh_link);
- if (sec == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(sec, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
- if (sec_strndx == NULL)
- goto out_elf_end;
-
- secstrs = elf_getdata(sec_strndx, NULL);
- if (secstrs == NULL)
- goto out_elf_end;
-
- nr_syms = shdr.sh_size / shdr.sh_entsize;
-
- memset(&sym, 0, sizeof(sym));
- if (self->kernel == DSO_TYPE_USER) {
- self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
- elf_section_by_name(elf, &ehdr, &shdr,
- ".gnu.prelink_undo",
- NULL) != NULL);
- } else self->adjust_symbols = 0;
-
- elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
- struct symbol *f;
- const char *elf_name = elf_sym__name(&sym, symstrs);
- char *demangled = NULL;
- int is_label = elf_sym__is_label(&sym);
- const char *section_name;
-
- if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
- strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
- kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
-
- if (!is_label && !elf_sym__is_a(&sym, map->type))
- continue;
-
- /* Reject ARM ELF "mapping symbols": these aren't unique and
- * don't identify functions, so will confuse the profile
- * output: */
- if (ehdr.e_machine == EM_ARM) {
- if (!strcmp(elf_name, "$a") ||
- !strcmp(elf_name, "$d") ||
- !strcmp(elf_name, "$t"))
- continue;
- }
-
- if (opdsec && sym.st_shndx == opdidx) {
- u32 offset = sym.st_value - opdshdr.sh_addr;
- u64 *opd = opddata->d_buf + offset;
- sym.st_value = *opd;
- sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
- }
-
- sec = elf_getscn(elf, sym.st_shndx);
- if (!sec)
- goto out_elf_end;
-
- gelf_getshdr(sec, &shdr);
-
- if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
- continue;
-
- section_name = elf_sec__name(&shdr, secstrs);
-
- if (self->kernel != DSO_TYPE_USER || kmodule) {
- char dso_name[PATH_MAX];
-
- if (strcmp(section_name,
- (curr_dso->short_name +
- self->short_name_len)) == 0)
- goto new_symbol;
-
- if (strcmp(section_name, ".text") == 0) {
- curr_map = map;
- curr_dso = self;
- goto new_symbol;
- }
-
- snprintf(dso_name, sizeof(dso_name),
- "%s%s", self->short_name, section_name);
-
- curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
- if (curr_map == NULL) {
- u64 start = sym.st_value;
-
- if (kmodule)
- start += map->start + shdr.sh_offset;
-
- curr_dso = dso__new(dso_name);
- if (curr_dso == NULL)
- goto out_elf_end;
- curr_dso->kernel = self->kernel;
- curr_map = map__new2(start, curr_dso,
- map->type);
- if (curr_map == NULL) {
- dso__delete(curr_dso);
- goto out_elf_end;
- }
- curr_map->map_ip = identity__map_ip;
- curr_map->unmap_ip = identity__map_ip;
- curr_dso->origin = self->origin;
- map_groups__insert(kmap->kmaps, curr_map);
- dsos__add(&self->node, curr_dso);
- dso__set_loaded(curr_dso, map->type);
- } else
- curr_dso = curr_map->dso;
-
- goto new_symbol;
- }
-
- if (curr_dso->adjust_symbols) {
- pr_debug4("%s: adjusting symbol: st_value: %#Lx "
- "sh_addr: %#Lx sh_offset: %#Lx\n", __func__,
- (u64)sym.st_value, (u64)shdr.sh_addr,
- (u64)shdr.sh_offset);
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
- }
- /*
- * We need to figure out if the object was created from C++ sources
- * DWARF DW_compile_unit has this, but we don't always have access
- * to it...
- */
- demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
- if (demangled != NULL)
- elf_name = demangled;
-new_symbol:
- f = symbol__new(sym.st_value, sym.st_size,
- GELF_ST_BIND(sym.st_info), elf_name);
- free(demangled);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(curr_map, f))
- symbol__delete(f);
- else {
- symbols__insert(&curr_dso->symbols[curr_map->type], f);
- nr++;
- }
- }
-
- /*
- * For misannotated, zeroed, ASM function sizes.
- */
- if (nr > 0) {
- symbols__fixup_end(&self->symbols[map->type]);
- if (kmap) {
- /*
- * We need to fixup this here too because we create new
- * maps here, for things like vsyscall sections.
- */
- __map_groups__fixup_end(kmap->kmaps, map->type);
- }
- }
- err = nr;
-out_elf_end:
- elf_end(elf);
-out_close:
- return err;
-}
-
-static bool dso__build_id_equal(const struct dso *self, u8 *build_id)
-{
- return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;
-}
-
-bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
-{
- bool have_build_id = false;
- struct dso *pos;
-
- list_for_each_entry(pos, head, node) {
- if (with_hits && !pos->hit)
- continue;
- if (pos->has_build_id) {
- have_build_id = true;
- continue;
- }
- if (filename__read_build_id(pos->long_name, pos->build_id,
- sizeof(pos->build_id)) > 0) {
- have_build_id = true;
- pos->has_build_id = true;
- }
- }
-
- return have_build_id;
-}
-
-/*
- * Align offset to 4 bytes as needed for note name and descriptor data.
- */
-#define NOTE_ALIGN(n) (((n) + 3) & -4U)
-
-static int elf_read_build_id(Elf *elf, void *bf, size_t size)
-{
- int err = -1;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- Elf_Data *data;
- Elf_Scn *sec;
- Elf_Kind ek;
- void *ptr;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- ek = elf_kind(elf);
- if (ek != ELF_K_ELF)
- goto out;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".note.gnu.build-id", NULL);
- if (sec == NULL) {
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".notes", NULL);
- if (sec == NULL)
- goto out;
- }
-
- data = elf_getdata(sec, NULL);
- if (data == NULL)
- goto out;
-
- ptr = data->d_buf;
- while (ptr < (data->d_buf + data->d_size)) {
- GElf_Nhdr *nhdr = ptr;
- int namesz = NOTE_ALIGN(nhdr->n_namesz),
- descsz = NOTE_ALIGN(nhdr->n_descsz);
- const char *name;
-
- ptr += sizeof(*nhdr);
- name = ptr;
- ptr += namesz;
- if (nhdr->n_type == NT_GNU_BUILD_ID &&
- nhdr->n_namesz == sizeof("GNU")) {
- if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
- memcpy(bf, ptr, BUILD_ID_SIZE);
- err = BUILD_ID_SIZE;
- break;
- }
- }
- ptr += descsz;
- }
-
-out:
- return err;
-}
-
-int filename__read_build_id(const char *filename, void *bf, size_t size)
-{
- int fd, err = -1;
- Elf *elf;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
- goto out_close;
- }
-
- err = elf_read_build_id(elf, bf, size);
-
- elf_end(elf);
-out_close:
- close(fd);
-out:
- return err;
-}
-
-int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
-{
- int fd, err = -1;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- while (1) {
- char bf[BUFSIZ];
- GElf_Nhdr nhdr;
- int namesz, descsz;
-
- if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
- break;
-
- namesz = NOTE_ALIGN(nhdr.n_namesz);
- descsz = NOTE_ALIGN(nhdr.n_descsz);
- if (nhdr.n_type == NT_GNU_BUILD_ID &&
- nhdr.n_namesz == sizeof("GNU")) {
- if (read(fd, bf, namesz) != namesz)
- break;
- if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
- if (read(fd, build_id,
- BUILD_ID_SIZE) == BUILD_ID_SIZE) {
- err = 0;
- break;
- }
- } else if (read(fd, bf, descsz) != descsz)
- break;
- } else {
- int n = namesz + descsz;
- if (read(fd, bf, n) != n)
- break;
- }
- }
- close(fd);
-out:
- return err;
-}
-
-char dso__symtab_origin(const struct dso *self)
-{
- static const char origin[] = {
- [DSO__ORIG_KERNEL] = 'k',
- [DSO__ORIG_JAVA_JIT] = 'j',
- [DSO__ORIG_BUILD_ID_CACHE] = 'B',
- [DSO__ORIG_FEDORA] = 'f',
- [DSO__ORIG_UBUNTU] = 'u',
- [DSO__ORIG_BUILDID] = 'b',
- [DSO__ORIG_DSO] = 'd',
- [DSO__ORIG_KMODULE] = 'K',
- [DSO__ORIG_GUEST_KERNEL] = 'g',
- [DSO__ORIG_GUEST_KMODULE] = 'G',
- };
-
- if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
- return '!';
- return origin[self->origin];
-}
-
-int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
-{
- int size = PATH_MAX;
- char *name;
- int ret = -1;
- int fd;
- struct machine *machine;
- const char *root_dir;
- int want_symtab;
-
- dso__set_loaded(self, map->type);
-
- if (self->kernel == DSO_TYPE_KERNEL)
- return dso__load_kernel_sym(self, map, filter);
- else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
- return dso__load_guest_kernel_sym(self, map, filter);
-
- if (map->groups && map->groups->machine)
- machine = map->groups->machine;
- else
- machine = NULL;
-
- name = malloc(size);
- if (!name)
- return -1;
-
- self->adjust_symbols = 0;
-
- if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
- ret = dso__load_perf_map(self, map, filter);
- self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
- DSO__ORIG_NOT_FOUND;
- return ret;
- }
-
- /* Iterate over candidate debug images.
- * On the first pass, only load images if they have a full symtab.
- * Failing that, do a second pass where we accept .dynsym also
- */
- for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
- self->origin != DSO__ORIG_NOT_FOUND;
- self->origin++) {
- switch (self->origin) {
- case DSO__ORIG_BUILD_ID_CACHE:
- if (dso__build_id_filename(self, name, size) == NULL)
- continue;
- break;
- case DSO__ORIG_FEDORA:
- snprintf(name, size, "/usr/lib/debug%s.debug",
- self->long_name);
- break;
- case DSO__ORIG_UBUNTU:
- snprintf(name, size, "/usr/lib/debug%s",
- self->long_name);
- break;
- case DSO__ORIG_BUILDID: {
- char build_id_hex[BUILD_ID_SIZE * 2 + 1];
-
- if (!self->has_build_id)
- continue;
-
- build_id__sprintf(self->build_id,
- sizeof(self->build_id),
- build_id_hex);
- snprintf(name, size,
- "/usr/lib/debug/.build-id/%.2s/%s.debug",
- build_id_hex, build_id_hex + 2);
- }
- break;
- case DSO__ORIG_DSO:
- snprintf(name, size, "%s", self->long_name);
- break;
- case DSO__ORIG_GUEST_KMODULE:
- if (map->groups && map->groups->machine)
- root_dir = map->groups->machine->root_dir;
- else
- root_dir = "";
- snprintf(name, size, "%s%s", root_dir, self->long_name);
- break;
-
- default:
- /*
- * If we wanted a full symtab but no image had one,
- * relax our requirements and repeat the search.
- */
- if (want_symtab) {
- want_symtab = 0;
- self->origin = DSO__ORIG_BUILD_ID_CACHE;
- } else
- continue;
- }
-
- /* Name is now the name of the next image to try */
- fd = open(name, O_RDONLY);
- if (fd < 0)
- continue;
-
- ret = dso__load_sym(self, map, name, fd, filter, 0,
- want_symtab);
- close(fd);
-
- /*
- * Some people seem to have debuginfo files _WITHOUT_ debug
- * info!?!?
- */
- if (!ret)
- continue;
-
- if (ret > 0) {
- int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
- if (nr_plt > 0)
- ret += nr_plt;
- break;
- }
- }
-
- free(name);
- if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
- return 0;
- return ret;
-}
-
-struct map *map_groups__find_by_name(struct map_groups *self,
- enum map_type type, const char *name)
-{
- struct rb_node *nd;
-
- for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
- struct map *map = rb_entry(nd, struct map, rb_node);
-
- if (map->dso && strcmp(map->dso->short_name, name) == 0)
- return map;
- }
-
- return NULL;
-}
-
-static int dso__kernel_module_get_build_id(struct dso *self,
- const char *root_dir)
-{
- char filename[PATH_MAX];
- /*
- * kernel module short names are of the form "[module]" and
- * we need just "module" here.
- */
- const char *name = self->short_name + 1;
-
- snprintf(filename, sizeof(filename),
- "%s/sys/module/%.*s/notes/.note.gnu.build-id",
- root_dir, (int)strlen(name) - 1, name);
-
- if (sysfs__read_build_id(filename, self->build_id,
- sizeof(self->build_id)) == 0)
- self->has_build_id = true;
-
- return 0;
-}
-
-static int map_groups__set_modules_path_dir(struct map_groups *self,
- const char *dir_name)
-{
- struct dirent *dent;
- DIR *dir = opendir(dir_name);
- int ret = 0;
-
- if (!dir) {
- pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
- return -1;
- }
-
- while ((dent = readdir(dir)) != NULL) {
- char path[PATH_MAX];
- struct stat st;
-
- /*sshfs might return bad dent->d_type, so we have to stat*/
- sprintf(path, "%s/%s", dir_name, dent->d_name);
- if (stat(path, &st))
- continue;
-
- if (S_ISDIR(st.st_mode)) {
- if (!strcmp(dent->d_name, ".") ||
- !strcmp(dent->d_name, ".."))
- continue;
-
- snprintf(path, sizeof(path), "%s/%s",
- dir_name, dent->d_name);
- ret = map_groups__set_modules_path_dir(self, path);
- if (ret < 0)
- goto out;
- } else {
- char *dot = strrchr(dent->d_name, '.'),
- dso_name[PATH_MAX];
- struct map *map;
- char *long_name;
-
- if (dot == NULL || strcmp(dot, ".ko"))
- continue;
- snprintf(dso_name, sizeof(dso_name), "[%.*s]",
- (int)(dot - dent->d_name), dent->d_name);
-
- strxfrchar(dso_name, '-', '_');
- map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name);
- if (map == NULL)
- continue;
-
- snprintf(path, sizeof(path), "%s/%s",
- dir_name, dent->d_name);
-
- long_name = strdup(path);
- if (long_name == NULL) {
- ret = -1;
- goto out;
- }
- dso__set_long_name(map->dso, long_name);
- map->dso->lname_alloc = 1;
- dso__kernel_module_get_build_id(map->dso, "");
- }
- }
-
-out:
- closedir(dir);
- return ret;
-}
-
-static char *get_kernel_version(const char *root_dir)
-{
- char version[PATH_MAX];
- FILE *file;
- char *name, *tmp;
- const char *prefix = "Linux version ";
-
- sprintf(version, "%s/proc/version", root_dir);
- file = fopen(version, "r");
- if (!file)
- return NULL;
-
- version[0] = '\0';
- tmp = fgets(version, sizeof(version), file);
- fclose(file);
-
- name = strstr(version, prefix);
- if (!name)
- return NULL;
- name += strlen(prefix);
- tmp = strchr(name, ' ');
- if (tmp)
- *tmp = '\0';
-
- return strdup(name);
-}
-
-static int machine__set_modules_path(struct machine *self)
-{
- char *version;
- char modules_path[PATH_MAX];
-
- version = get_kernel_version(self->root_dir);
- if (!version)
- return -1;
-
- snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
- self->root_dir, version);
- free(version);
-
- return map_groups__set_modules_path_dir(&self->kmaps, modules_path);
-}
-
-/*
- * Constructor variant for modules (where we know from /proc/modules where
- * they are loaded) and for vmlinux, where only after we load all the
- * symbols we'll know where it starts and ends.
- */
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
-{
- struct map *self = calloc(1, (sizeof(*self) +
- (dso->kernel ? sizeof(struct kmap) : 0)));
- if (self != NULL) {
- /*
- * ->end will be filled after we load all the symbols
- */
- map__init(self, type, start, 0, 0, dso);
- }
-
- return self;
-}
-
-struct map *machine__new_module(struct machine *self, u64 start,
- const char *filename)
-{
- struct map *map;
- struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename);
-
- if (dso == NULL)
- return NULL;
-
- map = map__new2(start, dso, MAP__FUNCTION);
- if (map == NULL)
- return NULL;
-
- if (machine__is_host(self))
- dso->origin = DSO__ORIG_KMODULE;
- else
- dso->origin = DSO__ORIG_GUEST_KMODULE;
- map_groups__insert(&self->kmaps, map);
- return map;
-}
-
-static int machine__create_modules(struct machine *self)
-{
- char *line = NULL;
- size_t n;
- FILE *file;
- struct map *map;
- const char *modules;
- char path[PATH_MAX];
-
- if (machine__is_default_guest(self))
- modules = symbol_conf.default_guest_modules;
- else {
- sprintf(path, "%s/proc/modules", self->root_dir);
- modules = path;
- }
-
- file = fopen(modules, "r");
- if (file == NULL)
- return -1;
-
- while (!feof(file)) {
- char name[PATH_MAX];
- u64 start;
- char *sep;
- int line_len;
-
- line_len = getline(&line, &n, file);
- if (line_len < 0)
- break;
-
- if (!line)
- goto out_failure;
-
- line[--line_len] = '\0'; /* \n */
-
- sep = strrchr(line, 'x');
- if (sep == NULL)
- continue;
-
- hex2u64(sep + 1, &start);
-
- sep = strchr(line, ' ');
- if (sep == NULL)
- continue;
-
- *sep = '\0';
-
- snprintf(name, sizeof(name), "[%s]", line);
- map = machine__new_module(self, start, name);
- if (map == NULL)
- goto out_delete_line;
- dso__kernel_module_get_build_id(map->dso, self->root_dir);
- }
-
- free(line);
- fclose(file);
-
- return machine__set_modules_path(self);
-
-out_delete_line:
- free(line);
-out_failure:
- return -1;
-}
-
-static int dso__load_vmlinux(struct dso *self, struct map *map,
- const char *vmlinux, symbol_filter_t filter)
-{
- int err = -1, fd;
-
- fd = open(vmlinux, O_RDONLY);
- if (fd < 0)
- return -1;
-
- dso__set_loaded(self, map->type);
- err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
- close(fd);
-
- if (err > 0)
- pr_debug("Using %s for symbols\n", vmlinux);
-
- return err;
-}
-
-int dso__load_vmlinux_path(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- int i, err = 0;
- char *filename;
-
- pr_debug("Looking at the vmlinux_path (%d entries long)\n",
- vmlinux_path__nr_entries + 1);
-
- filename = dso__build_id_filename(self, NULL, 0);
- if (filename != NULL) {
- err = dso__load_vmlinux(self, map, filename, filter);
- if (err > 0) {
- dso__set_long_name(self, filename);
- goto out;
- }
- free(filename);
- }
-
- for (i = 0; i < vmlinux_path__nr_entries; ++i) {
- err = dso__load_vmlinux(self, map, vmlinux_path[i], filter);
- if (err > 0) {
- dso__set_long_name(self, strdup(vmlinux_path[i]));
- break;
- }
- }
-out:
- return err;
-}
-
-static int dso__load_kernel_sym(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- int err;
- const char *kallsyms_filename = NULL;
- char *kallsyms_allocated_filename = NULL;
- /*
- * Step 1: if the user specified a vmlinux filename, use it and only
- * it, reporting errors to the user if it cannot be used.
- *
- * For instance, try to analyse an ARM perf.data file _without_ a
- * build-id, or if the user specifies the wrong path to the right
- * vmlinux file, obviously we can't fallback to another vmlinux (a
- * x86_86 one, on the machine where analysis is being performed, say),
- * or worse, /proc/kallsyms.
- *
- * If the specified file _has_ a build-id and there is a build-id
- * section in the perf.data file, we will still do the expected
- * validation in dso__load_vmlinux and will bail out if they don't
- * match.
- */
- if (symbol_conf.vmlinux_name != NULL) {
- err = dso__load_vmlinux(self, map,
- symbol_conf.vmlinux_name, filter);
- if (err > 0) {
- dso__set_long_name(self,
- strdup(symbol_conf.vmlinux_name));
- goto out_fixup;
- }
- return err;
- }
-
- if (vmlinux_path != NULL) {
- err = dso__load_vmlinux_path(self, map, filter);
- if (err > 0)
- goto out_fixup;
- }
-
- /*
- * Say the kernel DSO was created when processing the build-id header table,
- * we have a build-id, so check if it is the same as the running kernel,
- * using it if it is.
- */
- if (self->has_build_id) {
- u8 kallsyms_build_id[BUILD_ID_SIZE];
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
- sizeof(kallsyms_build_id)) == 0) {
- if (dso__build_id_equal(self, kallsyms_build_id)) {
- kallsyms_filename = "/proc/kallsyms";
- goto do_kallsyms;
- }
- }
- /*
- * Now look if we have it on the build-id cache in
- * $HOME/.debug/[kernel.kallsyms].
- */
- build_id__sprintf(self->build_id, sizeof(self->build_id),
- sbuild_id);
-
- if (asprintf(&kallsyms_allocated_filename,
- "%s/.debug/[kernel.kallsyms]/%s",
- getenv("HOME"), sbuild_id) == -1) {
- pr_err("Not enough memory for kallsyms file lookup\n");
- return -1;
- }
-
- kallsyms_filename = kallsyms_allocated_filename;
-
- if (access(kallsyms_filename, F_OK)) {
- pr_err("No kallsyms or vmlinux with build-id %s "
- "was found\n", sbuild_id);
- free(kallsyms_allocated_filename);
- return -1;
- }
- } else {
- /*
- * Last resort, if we don't have a build-id and couldn't find
- * any vmlinux file, try the running kernel kallsyms table.
- */
- kallsyms_filename = "/proc/kallsyms";
- }
-
-do_kallsyms:
- err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
- if (err > 0)
- pr_debug("Using %s for symbols\n", kallsyms_filename);
- free(kallsyms_allocated_filename);
-
- if (err > 0) {
-out_fixup:
- if (kallsyms_filename != NULL)
- dso__set_long_name(self, strdup("[kernel.kallsyms]"));
- map__fixup_start(map);
- map__fixup_end(map);
- }
-
- return err;
-}
-
-static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
- symbol_filter_t filter)
-{
- int err;
- const char *kallsyms_filename = NULL;
- struct machine *machine;
- char path[PATH_MAX];
-
- if (!map->groups) {
- pr_debug("Guest kernel map hasn't the point to groups\n");
- return -1;
- }
- machine = map->groups->machine;
-
- if (machine__is_default_guest(machine)) {
- /*
- * if the user specified a vmlinux filename, use it and only
- * it, reporting errors to the user if it cannot be used.
- * Or use file guest_kallsyms inputted by user on commandline
- */
- if (symbol_conf.default_guest_vmlinux_name != NULL) {
- err = dso__load_vmlinux(self, map,
- symbol_conf.default_guest_vmlinux_name, filter);
- goto out_try_fixup;
- }
-
- kallsyms_filename = symbol_conf.default_guest_kallsyms;
- if (!kallsyms_filename)
- return -1;
- } else {
- sprintf(path, "%s/proc/kallsyms", machine->root_dir);
- kallsyms_filename = path;
- }
-
- err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
- if (err > 0)
- pr_debug("Using %s for symbols\n", kallsyms_filename);
-
-out_try_fixup:
- if (err > 0) {
- if (kallsyms_filename != NULL) {
- machine__mmap_name(machine, path, sizeof(path));
- dso__set_long_name(self, strdup(path));
- }
- map__fixup_start(map);
- map__fixup_end(map);
- }
-
- return err;
-}
-
-static void dsos__add(struct list_head *head, struct dso *dso)
-{
- list_add_tail(&dso->node, head);
-}
-
-static struct dso *dsos__find(struct list_head *head, const char *name)
-{
- struct dso *pos;
-
- list_for_each_entry(pos, head, node)
- if (strcmp(pos->long_name, name) == 0)
- return pos;
- return NULL;
-}
-
-struct dso *__dsos__findnew(struct list_head *head, const char *name)
-{
- struct dso *dso = dsos__find(head, name);
-
- if (!dso) {
- dso = dso__new(name);
- if (dso != NULL) {
- dsos__add(head, dso);
- dso__set_basename(dso);
- }
- }
-
- return dso;
-}
-
-size_t __dsos__fprintf(struct list_head *head, FILE *fp)
-{
- struct dso *pos;
- size_t ret = 0;
-
- list_for_each_entry(pos, head, node) {
- int i;
- for (i = 0; i < MAP__NR_TYPES; ++i)
- ret += dso__fprintf(pos, i, fp);
- }
-
- return ret;
-}
-
-size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp)
-{
- struct rb_node *nd;
- size_t ret = 0;
-
- for (nd = rb_first(self); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret += __dsos__fprintf(&pos->kernel_dsos, fp);
- ret += __dsos__fprintf(&pos->user_dsos, fp);
- }
-
- return ret;
-}
-
-static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
- bool with_hits)
-{
- struct dso *pos;
- size_t ret = 0;
-
- list_for_each_entry(pos, head, node) {
- if (with_hits && !pos->hit)
- continue;
- ret += dso__fprintf_buildid(pos, fp);
- ret += fprintf(fp, " %s\n", pos->long_name);
- }
- return ret;
-}
-
-size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits)
-{
- return __dsos__fprintf_buildid(&self->kernel_dsos, fp, with_hits) +
- __dsos__fprintf_buildid(&self->user_dsos, fp, with_hits);
-}
-
-size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
-{
- struct rb_node *nd;
- size_t ret = 0;
-
- for (nd = rb_first(self); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret += machine__fprintf_dsos_buildid(pos, fp, with_hits);
- }
- return ret;
-}
-
-struct dso *dso__new_kernel(const char *name)
-{
- struct dso *self = dso__new(name ?: "[kernel.kallsyms]");
-
- if (self != NULL) {
- dso__set_short_name(self, "[kernel]");
- self->kernel = DSO_TYPE_KERNEL;
- }
-
- return self;
-}
-
-static struct dso *dso__new_guest_kernel(struct machine *machine,
- const char *name)
-{
- char bf[PATH_MAX];
- struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf)));
-
- if (self != NULL) {
- dso__set_short_name(self, "[guest.kernel]");
- self->kernel = DSO_TYPE_GUEST_KERNEL;
- }
-
- return self;
-}
-
-void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine)
-{
- char path[PATH_MAX];
-
- if (machine__is_default_guest(machine))
- return;
- sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
- if (sysfs__read_build_id(path, self->build_id,
- sizeof(self->build_id)) == 0)
- self->has_build_id = true;
-}
-
-static struct dso *machine__create_kernel(struct machine *self)
-{
- const char *vmlinux_name = NULL;
- struct dso *kernel;
-
- if (machine__is_host(self)) {
- vmlinux_name = symbol_conf.vmlinux_name;
- kernel = dso__new_kernel(vmlinux_name);
- } else {
- if (machine__is_default_guest(self))
- vmlinux_name = symbol_conf.default_guest_vmlinux_name;
- kernel = dso__new_guest_kernel(self, vmlinux_name);
- }
-
- if (kernel != NULL) {
- dso__read_running_kernel_build_id(kernel, self);
- dsos__add(&self->kernel_dsos, kernel);
- }
- return kernel;
-}
-
-int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
-{
- enum map_type type;
-
- for (type = 0; type < MAP__NR_TYPES; ++type) {
- struct kmap *kmap;
-
- self->vmlinux_maps[type] = map__new2(0, kernel, type);
- if (self->vmlinux_maps[type] == NULL)
- return -1;
-
- self->vmlinux_maps[type]->map_ip =
- self->vmlinux_maps[type]->unmap_ip = identity__map_ip;
-
- kmap = map__kmap(self->vmlinux_maps[type]);
- kmap->kmaps = &self->kmaps;
- map_groups__insert(&self->kmaps, self->vmlinux_maps[type]);
- }
-
- return 0;
-}
-
-void machine__destroy_kernel_maps(struct machine *self)
-{
- enum map_type type;
-
- for (type = 0; type < MAP__NR_TYPES; ++type) {
- struct kmap *kmap;
-
- if (self->vmlinux_maps[type] == NULL)
- continue;
-
- kmap = map__kmap(self->vmlinux_maps[type]);
- map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
- if (kmap->ref_reloc_sym) {
- /*
- * ref_reloc_sym is shared among all maps, so free just
- * on one of them.
- */
- if (type == MAP__FUNCTION) {
- free((char *)kmap->ref_reloc_sym->name);
- kmap->ref_reloc_sym->name = NULL;
- free(kmap->ref_reloc_sym);
- }
- kmap->ref_reloc_sym = NULL;
- }
-
- map__delete(self->vmlinux_maps[type]);
- self->vmlinux_maps[type] = NULL;
- }
-}
-
-int machine__create_kernel_maps(struct machine *self)
-{
- struct dso *kernel = machine__create_kernel(self);
-
- if (kernel == NULL ||
- __machine__create_kernel_maps(self, kernel) < 0)
- return -1;
-
- if (symbol_conf.use_modules && machine__create_modules(self) < 0)
- pr_debug("Problems creating module maps, continuing anyway...\n");
- /*
- * Now that we have all the maps created, just set the ->end of them:
- */
- map_groups__fixup_end(&self->kmaps);
- return 0;
-}
-
-static void vmlinux_path__exit(void)
-{
- while (--vmlinux_path__nr_entries >= 0) {
- free(vmlinux_path[vmlinux_path__nr_entries]);
- vmlinux_path[vmlinux_path__nr_entries] = NULL;
- }
-
- free(vmlinux_path);
- vmlinux_path = NULL;
-}
-
-static int vmlinux_path__init(void)
-{
- struct utsname uts;
- char bf[PATH_MAX];
-
- if (uname(&uts) < 0)
- return -1;
-
- vmlinux_path = malloc(sizeof(char *) * 5);
- if (vmlinux_path == NULL)
- return -1;
-
- vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
- uts.release);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
-
- return 0;
-
-out_fail:
- vmlinux_path__exit();
- return -1;
-}
-
-size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp)
-{
- int i;
- size_t printed = 0;
- struct dso *kdso = self->vmlinux_maps[MAP__FUNCTION]->dso;
-
- if (kdso->has_build_id) {
- char filename[PATH_MAX];
- if (dso__build_id_filename(kdso, filename, sizeof(filename)))
- printed += fprintf(fp, "[0] %s\n", filename);
- }
-
- for (i = 0; i < vmlinux_path__nr_entries; ++i)
- printed += fprintf(fp, "[%d] %s\n",
- i + kdso->has_build_id, vmlinux_path[i]);
-
- return printed;
-}
-
-static int setup_list(struct strlist **list, const char *list_str,
- const char *list_name)
-{
- if (list_str == NULL)
- return 0;
-
- *list = strlist__new(true, list_str);
- if (!*list) {
- pr_err("problems parsing %s list\n", list_name);
- return -1;
- }
- return 0;
-}
-
-int symbol__init(void)
-{
- if (symbol_conf.initialized)
- return 0;
-
- elf_version(EV_CURRENT);
- if (symbol_conf.sort_by_name)
- symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
- sizeof(struct symbol));
-
- if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
- return -1;
-
- if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
- pr_err("'.' is the only non valid --field-separator argument\n");
- return -1;
- }
-
- if (setup_list(&symbol_conf.dso_list,
- symbol_conf.dso_list_str, "dso") < 0)
- return -1;
-
- if (setup_list(&symbol_conf.comm_list,
- symbol_conf.comm_list_str, "comm") < 0)
- goto out_free_dso_list;
-
- if (setup_list(&symbol_conf.sym_list,
- symbol_conf.sym_list_str, "symbol") < 0)
- goto out_free_comm_list;
-
- symbol_conf.initialized = true;
- return 0;
-
-out_free_dso_list:
- strlist__delete(symbol_conf.dso_list);
-out_free_comm_list:
- strlist__delete(symbol_conf.comm_list);
- return -1;
-}
-
-void symbol__exit(void)
-{
- if (!symbol_conf.initialized)
- return;
- strlist__delete(symbol_conf.sym_list);
- strlist__delete(symbol_conf.dso_list);
- strlist__delete(symbol_conf.comm_list);
- vmlinux_path__exit();
- symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
- symbol_conf.initialized = false;
-}
-
-int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
-{
- struct machine *machine = machines__findnew(self, pid);
-
- if (machine == NULL)
- return -1;
-
- return machine__create_kernel_maps(machine);
-}
-
-static int hex(char ch)
-{
- if ((ch >= '0') && (ch <= '9'))
- return ch - '0';
- if ((ch >= 'a') && (ch <= 'f'))
- return ch - 'a' + 10;
- if ((ch >= 'A') && (ch <= 'F'))
- return ch - 'A' + 10;
- return -1;
-}
-
-/*
- * While we find nice hex chars, build a long_val.
- * Return number of chars processed.
- */
-int hex2u64(const char *ptr, u64 *long_val)
-{
- const char *p = ptr;
- *long_val = 0;
-
- while (*p) {
- const int hex_val = hex(*p);
-
- if (hex_val < 0)
- break;
-
- *long_val = (*long_val << 4) | hex_val;
- p++;
- }
-
- return p - ptr;
-}
-
-char *strxfrchar(char *s, char from, char to)
-{
- char *p = s;
-
- while ((p = strchr(p, from)) != NULL)
- *p++ = to;
-
- return s;
-}
-
-int machines__create_guest_kernel_maps(struct rb_root *self)
-{
- int ret = 0;
- struct dirent **namelist = NULL;
- int i, items = 0;
- char path[PATH_MAX];
- pid_t pid;
-
- if (symbol_conf.default_guest_vmlinux_name ||
- symbol_conf.default_guest_modules ||
- symbol_conf.default_guest_kallsyms) {
- machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID);
- }
-
- if (symbol_conf.guestmount) {
- items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL);
- if (items <= 0)
- return -ENOENT;
- for (i = 0; i < items; i++) {
- if (!isdigit(namelist[i]->d_name[0])) {
- /* Filter out . and .. */
- continue;
- }
- pid = atoi(namelist[i]->d_name);
- sprintf(path, "%s/%s/proc/kallsyms",
- symbol_conf.guestmount,
- namelist[i]->d_name);
- ret = access(path, R_OK);
- if (ret) {
- pr_debug("Can't access file %s\n", path);
- goto failure;
- }
- machines__create_kernel_maps(self, pid);
- }
-failure:
- free(namelist);
- }
-
- return ret;
-}
-
-void machines__destroy_guest_kernel_maps(struct rb_root *self)
-{
- struct rb_node *next = rb_first(self);
-
- while (next) {
- struct machine *pos = rb_entry(next, struct machine, rb_node);
-
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, self);
- machine__delete(pos);
- }
-}
-
-int machine__load_kallsyms(struct machine *self, const char *filename,
- enum map_type type, symbol_filter_t filter)
-{
- struct map *map = self->vmlinux_maps[type];
- int ret = dso__load_kallsyms(map->dso, filename, map, filter);
-
- if (ret > 0) {
- dso__set_loaded(map->dso, type);
- /*
- * Since /proc/kallsyms will have multiple sessions for the
- * kernel, with modules between them, fixup the end of all
- * sections.
- */
- __map_groups__fixup_end(&self->kmaps, type);
- }
-
- return ret;
-}
-
-int machine__load_vmlinux_path(struct machine *self, enum map_type type,
- symbol_filter_t filter)
-{
- struct map *map = self->vmlinux_maps[type];
- int ret = dso__load_vmlinux_path(map->dso, map, filter);
-
- if (ret > 0) {
- dso__set_loaded(map->dso, type);
- map__reloc_vmlinux(map);
- }
-
- return ret;
-}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
deleted file mode 100644
index adc7211..0000000
--- a/tools/perf/util/symbol.h
+++ /dev/null
@@ -1,231 +0,0 @@
-#ifndef __PERF_SYMBOL
-#define __PERF_SYMBOL 1
-
-#include <linux/types.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include "map.h"
-#include <linux/list.h>
-#include <lk/rbtree.h>
-#include <stdio.h>
-
-#ifdef HAVE_CPLUS_DEMANGLE
-extern char *cplus_demangle(const char *, int);
-
-static inline char *bfd_demangle(void __used *v, const char *c, int i)
-{
- return cplus_demangle(c, i);
-}
-#else
-#ifdef NO_DEMANGLE
-static inline char *bfd_demangle(void __used *v, const char __used *c,
- int __used i)
-{
- return NULL;
-}
-#else
-#include <bfd.h>
-#endif
-#endif
-
-int hex2u64(const char *ptr, u64 *val);
-char *strxfrchar(char *s, char from, char to);
-
-/*
- * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
- * for newer versions we can use mmap to reduce memory usage:
- */
-#ifdef LIBELF_NO_MMAP
-# define PERF_ELF_C_READ_MMAP ELF_C_READ
-#else
-# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
-#endif
-
-#ifndef DMGL_PARAMS
-#define DMGL_PARAMS (1 << 0) /* Include function args */
-#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
-#endif
-
-#define BUILD_ID_SIZE 20
-
-struct symbol {
- struct rb_node rb_node;
- u64 start;
- u64 end;
- u16 namelen;
- u8 binding;
- char name[0];
-};
-
-void symbol__delete(struct symbol *self);
-
-struct strlist;
-
-struct symbol_conf {
- unsigned short priv_size;
- bool try_vmlinux_path,
- use_modules,
- sort_by_name,
- show_nr_samples,
- use_callchain,
- exclude_other,
- show_cpu_utilization,
- initialized;
- const char *vmlinux_name,
- *source_prefix,
- *field_sep;
- const char *default_guest_vmlinux_name,
- *default_guest_kallsyms,
- *default_guest_modules;
- const char *guestmount;
- const char *dso_list_str,
- *comm_list_str,
- *sym_list_str,
- *col_width_list_str;
- struct strlist *dso_list,
- *comm_list,
- *sym_list;
-};
-
-extern struct symbol_conf symbol_conf;
-
-static inline void *symbol__priv(struct symbol *self)
-{
- return ((void *)self) - symbol_conf.priv_size;
-}
-
-struct ref_reloc_sym {
- const char *name;
- u64 addr;
- u64 unrelocated_addr;
-};
-
-struct map_symbol {
- struct map *map;
- struct symbol *sym;
- bool unfolded;
- bool has_children;
-};
-
-struct addr_location {
- struct thread *thread;
- struct map *map;
- struct symbol *sym;
- u64 addr;
- char level;
- bool filtered;
- u8 cpumode;
- s32 cpu;
-};
-
-enum dso_kernel_type {
- DSO_TYPE_USER = 0,
- DSO_TYPE_KERNEL,
- DSO_TYPE_GUEST_KERNEL
-};
-
-struct dso {
- struct list_head node;
- struct rb_root symbols[MAP__NR_TYPES];
- struct rb_root symbol_names[MAP__NR_TYPES];
- enum dso_kernel_type kernel;
- u8 adjust_symbols:1;
- u8 slen_calculated:1;
- u8 has_build_id:1;
- u8 hit:1;
- u8 annotate_warned:1;
- u8 sname_alloc:1;
- u8 lname_alloc:1;
- unsigned char origin;
- u8 sorted_by_name;
- u8 loaded;
- u8 build_id[BUILD_ID_SIZE];
- const char *short_name;
- char *long_name;
- u16 long_name_len;
- u16 short_name_len;
- char name[0];
-};
-
-struct dso *dso__new(const char *name);
-struct dso *dso__new_kernel(const char *name);
-void dso__delete(struct dso *self);
-
-int dso__name_len(const struct dso *self);
-
-bool dso__loaded(const struct dso *self, enum map_type type);
-bool dso__sorted_by_name(const struct dso *self, enum map_type type);
-
-static inline void dso__set_loaded(struct dso *self, enum map_type type)
-{
- self->loaded |= (1 << type);
-}
-
-void dso__sort_by_name(struct dso *self, enum map_type type);
-
-struct dso *__dsos__findnew(struct list_head *head, const char *name);
-
-int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
-int dso__load_vmlinux_path(struct dso *self, struct map *map,
- symbol_filter_t filter);
-int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
- symbol_filter_t filter);
-int machine__load_kallsyms(struct machine *self, const char *filename,
- enum map_type type, symbol_filter_t filter);
-int machine__load_vmlinux_path(struct machine *self, enum map_type type,
- symbol_filter_t filter);
-
-size_t __dsos__fprintf(struct list_head *head, FILE *fp);
-
-size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits);
-size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
-size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
-
-size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
-size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
-
-enum dso_origin {
- DSO__ORIG_KERNEL = 0,
- DSO__ORIG_GUEST_KERNEL,
- DSO__ORIG_JAVA_JIT,
- DSO__ORIG_BUILD_ID_CACHE,
- DSO__ORIG_FEDORA,
- DSO__ORIG_UBUNTU,
- DSO__ORIG_BUILDID,
- DSO__ORIG_DSO,
- DSO__ORIG_GUEST_KMODULE,
- DSO__ORIG_KMODULE,
- DSO__ORIG_NOT_FOUND,
-};
-
-char dso__symtab_origin(const struct dso *self);
-void dso__set_long_name(struct dso *self, char *name);
-void dso__set_build_id(struct dso *self, void *build_id);
-void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
-struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
-struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
- const char *name);
-
-int filename__read_build_id(const char *filename, void *bf, size_t size);
-int sysfs__read_build_id(const char *filename, void *bf, size_t size);
-bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
-int build_id__sprintf(const u8 *self, int len, char *bf);
-int kallsyms__parse(const char *filename, void *arg,
- int (*process_symbol)(void *arg, const char *name,
- char type, u64 start));
-
-void machine__destroy_kernel_maps(struct machine *self);
-int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
-int machine__create_kernel_maps(struct machine *self);
-
-int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
-int machines__create_guest_kernel_maps(struct rb_root *self);
-void machines__destroy_guest_kernel_maps(struct rb_root *self);
-
-int symbol__init(void);
-void symbol__exit(void);
-bool symbol_type__is_a(char symbol_type, enum map_type map_type);
-
-size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
-
-#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 8ab6e09..c36be4b 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -5,6 +5,7 @@
#include "session.h"
#include "thread.h"
#include <lk/util.h>
+#include <perf/config.h>
#include "debug.h"
/* Skip "." and ".." directories */
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 1a70f60..0b1d79a 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -3,7 +3,7 @@
#include <lk/rbtree.h>
#include <unistd.h>
-#include "symbol.h"
+#include <perf/symbol.h>
struct thread {
union {
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index a90273e..689317a 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -3,7 +3,7 @@
#include "../libslang.h"
#include "../../hist.h"
#include "../../sort.h"
-#include "../../symbol.h"
+#include <perf/symbol.h>
static void ui__error_window(const char *fmt, ...)
{
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index 1d4ee98..4af0b95 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -15,7 +15,7 @@
#include "../browser.h"
#include "../helpline.h"
#include "../util.h"
-#include "map.h"
+#include <perf/map.h>
struct hist_browser {
struct ui_browser b;
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
index 142b825..663e0b0 100644
--- a/tools/perf/util/ui/browsers/map.c
+++ b/tools/perf/util/ui/browsers/map.c
@@ -6,10 +6,10 @@
#include <string.h>
#include <linux/bitops.h>
#include "../../debug.h"
-#include "../../symbol.h"
+#include <perf/symbol.h>
#include "../browser.h"
#include "../helpline.h"
-#include "map.h"
+#include <perf/map.h>
static int ui_entry__read(const char *title, char *bf, size_t size, int width)
{
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 17/20] perf: Export trace parsing utils
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (15 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 16/20] perf: Export map.ch and symbol.ch Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 18/20] Move string.c to the library Borislav Petkov
` (3 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
.. and put them into lib/trace/ for a wider use. Also, move a couple of
helpers from perf/util/parse-events.c -> lib/trace/trace-event-info.c.
While at it, merge duplicated enum trace_flag_type.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/debugfs.c | 1 +
tools/lib/lk/debugfs.h | 1 +
tools/lib/trace/Makefile | 7 +-
tools/lib/trace/parse-events.h | 12 +-
tools/lib/trace/trace-event-info.c | 650 ++++++++++++++++++++
tools/lib/trace/trace-event-parse.c | 231 +++++++
tools/lib/trace/trace-event-read.c | 541 ++++++++++++++++
tools/lib/trace/trace-event.h | 115 ++++
tools/perf/Makefile | 4 -
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-kvm.c | 2 +-
tools/perf/builtin-lock.c | 2 +-
tools/perf/builtin-sched.c | 2 +-
tools/perf/builtin-timechart.c | 19 -
tools/perf/builtin-trace.c | 2 +-
tools/perf/scripts/perl/Perf-Trace-Util/Context.c | 2 +-
tools/perf/scripts/perl/Perf-Trace-Util/Context.xs | 2 +-
.../perf/scripts/python/Perf-Trace-Util/Context.c | 2 +-
| 7 +-
tools/perf/util/parse-events.c | 103 +---
tools/perf/util/parse-events.h | 9 +-
tools/perf/util/probe-event.c | 2 +-
.../perf/util/scripting-engines/trace-event-perl.c | 2 +-
.../util/scripting-engines/trace-event-python.c | 2 +-
tools/perf/util/trace-event-info.c | 566 -----------------
tools/perf/util/trace-event-parse.c | 234 -------
tools/perf/util/trace-event-read.c | 541 ----------------
tools/perf/util/trace-event-scripting.c | 2 +-
tools/perf/util/trace-event.h | 90 ---
29 files changed, 1574 insertions(+), 1581 deletions(-)
create mode 100644 tools/lib/trace/trace-event-info.c
create mode 100644 tools/lib/trace/trace-event-parse.c
create mode 100644 tools/lib/trace/trace-event-read.c
create mode 100644 tools/lib/trace/trace-event.h
delete mode 100644 tools/perf/util/trace-event-info.c
delete mode 100644 tools/perf/util/trace-event-parse.c
delete mode 100644 tools/perf/util/trace-event-read.c
delete mode 100644 tools/perf/util/trace-event.h
diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c
index c4e7ffe..6920cf9 100644
--- a/tools/lib/lk/debugfs.c
+++ b/tools/lib/lk/debugfs.c
@@ -7,6 +7,7 @@
static int debugfs_premounted;
static char debugfs_mountpoint[MAX_PATH+1];
char debugfs_mntpt[MAXPATHLEN];
+char debugfs_path[MAXPATHLEN];
static const char *debugfs_known_mountpoints[] = {
"/sys/kernel/debug/",
diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h
index 5970f0b..5590c43 100644
--- a/tools/lib/lk/debugfs.h
+++ b/tools/lib/lk/debugfs.h
@@ -16,6 +16,7 @@
#define LK_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
extern char debugfs_mntpt[];
+extern char debugfs_path[];
extern const char *debugfs_find_mountpoint(void);
extern int debugfs_valid_mountpoint(const char *debugfs);
diff --git a/tools/lib/trace/Makefile b/tools/lib/trace/Makefile
index 22fa7b6..a0ca116 100644
--- a/tools/lib/trace/Makefile
+++ b/tools/lib/trace/Makefile
@@ -37,17 +37,22 @@ TRACE_LIB = $(LIB_OUTPUT)libparsevent.a
all: $(TRACE_LIB)
+PEVENT_LIB_H += trace-event.h
+
PEVENT_LIB_OBJS += parse-events.o
PEVENT_LIB_OBJS += parse-filter.o
PEVENT_LIB_OBJS += parse-utils.o
PEVENT_LIB_OBJS += trace-seq.o
+PEVENT_LIB_OBJS += trace-event-parse.o
+PEVENT_LIB_OBJS += trace-event-read.o
+PEVENT_LIB_OBJS += trace-event-info.o
ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
$(OUTPUT)%.o: %.c
$(QUIET_CC)$(CC) -g -o $@ -c $(ALL_CFLAGS) $<
-$(TRACE_LIB): $(PEVENT_LIB_OBJS)
+$(TRACE_LIB): $(PEVENT_LIB_OBJS) $(PEVENT_LIB_H)
$(RM) $@; $(AR) rcs $@ $^
clean:
diff --git a/tools/lib/trace/parse-events.h b/tools/lib/trace/parse-events.h
index 31f46f5..f967885 100644
--- a/tools/lib/trace/parse-events.h
+++ b/tools/lib/trace/parse-events.h
@@ -425,7 +425,17 @@ __data2host8(struct pevent *pevent, unsigned long long data)
__data2host8(pevent, __val); \
})
-/* taken from kernel/trace/trace.h */
+/*
+ * trace_flag_type is an enumeration that holds different
+ * states when a trace occurs. These are:
+ * IRQS_OFF - interrupts were disabled
+ * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags
+ * NEED_RESCED - reschedule is requested
+ * HARDIRQ - inside an interrupt handler
+ * SOFTIRQ - inside a softirq handler
+ *
+ * taken from kernel/trace/trace.h
+ */
enum trace_flag_type {
TRACE_FLAG_IRQS_OFF = 0x01,
TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
diff --git a/tools/lib/trace/trace-event-info.c b/tools/lib/trace/trace-event-info.c
new file mode 100644
index 0000000..98faa44
--- /dev/null
+++ b/tools/lib/trace/trace-event-info.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <lk/util.h>
+#include <lk/kernel.h>
+#include <lk/debugfs.h>
+
+#include "../../perf/perf.h"
+#include "trace-event.h"
+
+#define VERSION "0.5"
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+#define MAX_PATH 256
+
+#define TRACE_CTRL "tracing_on"
+#define TRACE "trace"
+#define AVAILABLE "available_tracers"
+#define CURRENT "current_tracer"
+#define ITER_CTRL "trace_options"
+#define MAX_LATENCY "tracing_max_latency"
+
+unsigned int page_size;
+
+static const char *output_file = "trace.info";
+static int output_fd;
+
+struct event_list {
+ struct event_list *next;
+ const char *event;
+};
+
+struct events {
+ struct events *sibling;
+ struct events *children;
+ struct events *next;
+ char *name;
+};
+
+
+
+static void t_die(const char *fmt, ...)
+{
+ va_list ap;
+ int ret = errno;
+
+ if (errno)
+ perror("trace-cmd");
+ else
+ ret = -1;
+
+ va_start(ap, fmt);
+ fprintf(stderr, " ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(ret);
+}
+
+void *malloc_or_die(unsigned int size)
+{
+ void *data;
+
+ data = malloc(size);
+ if (!data)
+ t_die("malloc");
+ return data;
+}
+
+static const char *find_debugfs(void)
+{
+ const char *path = debugfs_mount(NULL);
+
+ if (!path)
+ t_die("Your kernel not support debugfs filesystem");
+
+ return path;
+}
+
+/*
+ * Finds the path to the debugfs/tracing
+ * Allocates the string and stores it.
+ */
+static const char *find_tracing_dir(void)
+{
+ static char *tracing;
+ static int tracing_found;
+ const char *debugfs;
+
+ if (tracing_found)
+ return tracing;
+
+ debugfs = find_debugfs();
+
+ tracing = malloc_or_die(strlen(debugfs) + 9);
+
+ sprintf(tracing, "%s/tracing", debugfs);
+
+ tracing_found = 1;
+ return tracing;
+}
+
+static char *get_tracing_file(const char *name)
+{
+ const char *tracing;
+ char *file;
+
+ tracing = find_tracing_dir();
+ if (!tracing)
+ return NULL;
+
+ file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
+
+ sprintf(file, "%s/%s", tracing, name);
+ return file;
+}
+
+static void put_tracing_file(char *file)
+{
+ free(file);
+}
+
+static ssize_t calc_data_size;
+
+static ssize_t write_or_die(const void *buf, size_t len)
+{
+ int ret;
+
+ if (calc_data_size) {
+ calc_data_size += len;
+ return len;
+ }
+
+ ret = write(output_fd, buf, len);
+ if (ret < 0)
+ t_die("writing to '%s'", output_file);
+
+ return ret;
+}
+
+int bigendian(void)
+{
+ unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
+ unsigned int *ptr;
+
+ ptr = (unsigned int *)(void *)str;
+ return *ptr == 0x01020304;
+}
+
+static unsigned long long copy_file_fd(int fd)
+{
+ unsigned long long size = 0;
+ char buf[BUFSIZ];
+ int r;
+
+ do {
+ r = read(fd, buf, BUFSIZ);
+ if (r > 0) {
+ size += r;
+ write_or_die(buf, r);
+ }
+ } while (r > 0);
+
+ return size;
+}
+
+static unsigned long long copy_file(const char *file)
+{
+ unsigned long long size = 0;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ t_die("Can't read '%s'", file);
+ size = copy_file_fd(fd);
+ close(fd);
+
+ return size;
+}
+
+static unsigned long get_size_fd(int fd)
+{
+ unsigned long long size = 0;
+ char buf[BUFSIZ];
+ int r;
+
+ do {
+ r = read(fd, buf, BUFSIZ);
+ if (r > 0)
+ size += r;
+ } while (r > 0);
+
+ lseek(fd, 0, SEEK_SET);
+
+ return size;
+}
+
+static unsigned long get_size(const char *file)
+{
+ unsigned long long size = 0;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ t_die("Can't read '%s'", file);
+ size = get_size_fd(fd);
+ close(fd);
+
+ return size;
+}
+
+static void read_header_files(void)
+{
+ unsigned long long size, check_size;
+ char *path;
+ int fd;
+
+ path = get_tracing_file("events/header_page");
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ t_die("can't read '%s'", path);
+
+ /* unfortunately, you can not stat debugfs files for size */
+ size = get_size_fd(fd);
+
+ write_or_die("header_page", 12);
+ write_or_die(&size, 8);
+ check_size = copy_file_fd(fd);
+ close(fd);
+
+ if (size != check_size)
+ t_die("wrong size for '%s' size=%lld read=%lld",
+ path, size, check_size);
+ put_tracing_file(path);
+
+ path = get_tracing_file("events/header_event");
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ t_die("can't read '%s'", path);
+
+ size = get_size_fd(fd);
+
+ write_or_die("header_event", 13);
+ write_or_die(&size, 8);
+ check_size = copy_file_fd(fd);
+ if (size != check_size)
+ t_die("wrong size for '%s'", path);
+ put_tracing_file(path);
+ close(fd);
+}
+
+static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
+{
+ while (tps) {
+ if (!strcmp(sys, tps->name))
+ return true;
+ tps = tps->next;
+ }
+
+ return false;
+}
+
+static void copy_event_system(const char *sys, struct tracepoint_path *tps)
+{
+ unsigned long long size, check_size;
+ struct dirent *dent;
+ struct stat st;
+ char *format;
+ DIR *dir;
+ int count = 0;
+ int ret;
+
+ dir = opendir(sys);
+ if (!dir)
+ t_die("can't read directory '%s'", sys);
+
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ !name_in_tp_list(dent->d_name, tps))
+ continue;
+ format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
+ sprintf(format, "%s/%s/format", sys, dent->d_name);
+ ret = stat(format, &st);
+ free(format);
+ if (ret < 0)
+ continue;
+ count++;
+ }
+
+ write_or_die(&count, 4);
+
+ rewinddir(dir);
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ !name_in_tp_list(dent->d_name, tps))
+ continue;
+ format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
+ sprintf(format, "%s/%s/format", sys, dent->d_name);
+ ret = stat(format, &st);
+
+ if (ret >= 0) {
+ /* unfortunately, you can not stat debugfs files for size */
+ size = get_size(format);
+ write_or_die(&size, 8);
+ check_size = copy_file(format);
+ if (size != check_size)
+ t_die("error in size of file '%s'", format);
+ }
+
+ free(format);
+ }
+ closedir(dir);
+}
+
+static void read_ftrace_files(struct tracepoint_path *tps)
+{
+ char *path;
+
+ path = get_tracing_file("events/ftrace");
+
+ copy_event_system(path, tps);
+
+ put_tracing_file(path);
+}
+
+static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
+{
+ while (tps) {
+ if (!strcmp(sys, tps->system))
+ return true;
+ tps = tps->next;
+ }
+
+ return false;
+}
+
+static void read_event_files(struct tracepoint_path *tps)
+{
+ struct dirent *dent;
+ struct stat st;
+ char *path;
+ char *sys;
+ DIR *dir;
+ int count = 0;
+ int ret;
+
+ path = get_tracing_file("events");
+
+ dir = opendir(path);
+ if (!dir)
+ t_die("can't read directory '%s'", path);
+
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ strcmp(dent->d_name, "ftrace") == 0 ||
+ !system_in_tp_list(dent->d_name, tps))
+ continue;
+ count++;
+ }
+
+ write_or_die(&count, 4);
+
+ rewinddir(dir);
+ while ((dent = readdir(dir))) {
+ if (dent->d_type != DT_DIR ||
+ strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ strcmp(dent->d_name, "ftrace") == 0 ||
+ !system_in_tp_list(dent->d_name, tps))
+ continue;
+ sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
+ sprintf(sys, "%s/%s", path, dent->d_name);
+ ret = stat(sys, &st);
+ if (ret >= 0) {
+ write_or_die(dent->d_name, strlen(dent->d_name) + 1);
+ copy_event_system(sys, tps);
+ }
+ free(sys);
+ }
+
+ closedir(dir);
+ put_tracing_file(path);
+}
+
+static void read_proc_kallsyms(void)
+{
+ unsigned int size, check_size;
+ const char *path = "/proc/kallsyms";
+ struct stat st;
+ int ret;
+
+ ret = stat(path, &st);
+ if (ret < 0) {
+ /* not found */
+ size = 0;
+ write_or_die(&size, 4);
+ return;
+ }
+ size = get_size(path);
+ write_or_die(&size, 4);
+ check_size = copy_file(path);
+ if (size != check_size)
+ t_die("error in size of file '%s'", path);
+
+}
+
+static void read_ftrace_printk(void)
+{
+ unsigned int size, check_size;
+ char *path;
+ struct stat st;
+ int ret;
+
+ path = get_tracing_file("printk_formats");
+ ret = stat(path, &st);
+ if (ret < 0) {
+ /* not found */
+ size = 0;
+ write_or_die(&size, 4);
+ goto out;
+ }
+ size = get_size(path);
+ write_or_die(&size, 4);
+ check_size = copy_file(path);
+ if (size != check_size)
+ t_die("error in size of file '%s'", path);
+out:
+ put_tracing_file(path);
+}
+
+int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
+{
+ char evt_path[MAXPATHLEN];
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_dir->d_name, evt_dir->d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return -EINVAL;
+ close(fd);
+
+ return 0;
+}
+
+struct tracepoint_path *tracepoint_id_to_path(u64 config)
+{
+ struct tracepoint_path *path = NULL;
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ char id_buf[4];
+ int fd;
+ u64 id;
+ char evt_path[MAXPATHLEN];
+ char dir_path[MAXPATHLEN];
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return NULL;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ return NULL;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next) {
+
+ snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
+ sys_dirent.d_name);
+ evt_dir = opendir(dir_path);
+ if (!evt_dir)
+ continue;
+
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
+ evt_dirent.d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ continue;
+ }
+ close(fd);
+ id = atoll(id_buf);
+ if (id == config) {
+ closedir(evt_dir);
+ closedir(sys_dir);
+ path = zalloc(sizeof(*path));
+ path->system = malloc(MAX_EVENT_LENGTH);
+ if (!path->system) {
+ free(path);
+ return NULL;
+ }
+ path->name = malloc(MAX_EVENT_LENGTH);
+ if (!path->name) {
+ free(path->system);
+ free(path);
+ return NULL;
+ }
+ strncpy(path->system, sys_dirent.d_name,
+ MAX_EVENT_LENGTH);
+ strncpy(path->name, evt_dirent.d_name,
+ MAX_EVENT_LENGTH);
+ return path;
+ }
+ }
+ closedir(evt_dir);
+ }
+
+ closedir(sys_dir);
+ return NULL;
+}
+
+static struct tracepoint_path *
+get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events)
+{
+ struct tracepoint_path path, *ppath = &path;
+ int i, nr_tracepoints = 0;
+
+ for (i = 0; i < nb_events; i++) {
+ if (pattrs[i].type != PERF_TYPE_TRACEPOINT)
+ continue;
+ ++nr_tracepoints;
+ ppath->next = tracepoint_id_to_path(pattrs[i].config);
+ if (!ppath->next)
+ t_die("%s\n", "No memory to alloc tracepoints list");
+ ppath = ppath->next;
+ }
+
+ return nr_tracepoints > 0 ? path.next : NULL;
+}
+
+bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events)
+{
+ int i;
+
+ for (i = 0; i < nb_events; i++)
+ if (pattrs[i].type == PERF_TYPE_TRACEPOINT)
+ return true;
+
+ return false;
+}
+
+int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
+{
+ char buf[BUFSIZ];
+ struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events);
+
+ /*
+ * What? No tracepoints? No sense writing anything here, bail out.
+ */
+ if (tps == NULL)
+ return -1;
+
+ output_fd = fd;
+
+ buf[0] = 23;
+ buf[1] = 8;
+ buf[2] = 68;
+ memcpy(buf + 3, "tracing", 7);
+
+ write_or_die(buf, 10);
+
+ write_or_die(VERSION, strlen(VERSION) + 1);
+
+ /* save endian */
+ if (bigendian())
+ buf[0] = 1;
+ else
+ buf[0] = 0;
+
+ read_trace_init(buf[0], buf[0]);
+
+ write_or_die(buf, 1);
+
+ /* save size of long */
+ buf[0] = sizeof(long);
+ write_or_die(buf, 1);
+
+ /* save page_size */
+ page_size = sysconf(_SC_PAGESIZE);
+ write_or_die(&page_size, 4);
+
+ read_header_files();
+ read_ftrace_files(tps);
+ read_event_files(tps);
+ read_proc_kallsyms();
+ read_ftrace_printk();
+
+ return 0;
+}
+
+ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
+ int nb_events)
+{
+ ssize_t size;
+ int err = 0;
+
+ calc_data_size = 1;
+ err = read_tracing_data(fd, pattrs, nb_events);
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+
+ if (err < 0)
+ return err;
+
+ return size;
+}
diff --git a/tools/lib/trace/trace-event-parse.c b/tools/lib/trace/trace-event-parse.c
new file mode 100644
index 0000000..c6f77c9
--- /dev/null
+++ b/tools/lib/trace/trace-event-parse.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#undef _GNU_SOURCE
+#include "../../perf/perf.h"
+#include <lk/util.h>
+#include "trace-event.h"
+
+int header_page_size_size;
+int header_page_ts_size;
+int header_page_data_offset;
+
+struct pevent *perf_pevent;
+static struct pevent *pevent;
+
+bool latency_format;
+
+int read_trace_init(int file_bigendian, int host_bigendian)
+{
+ if (pevent)
+ return 0;
+
+ perf_pevent = pevent_alloc();
+ pevent = perf_pevent;
+
+ pevent_set_file_bigendian(pevent, file_bigendian);
+ pevent_set_host_bigendian(pevent, host_bigendian);
+
+ return 0;
+}
+
+unsigned long long
+raw_field_value(struct event_format *event, const char *name, void *data)
+{
+ struct format_field *field;
+ unsigned long long val;
+
+ field = pevent_find_any_field(event, name);
+ if (!field)
+ return 0ULL;
+
+ pevent_read_number_field(field, data, &val);
+
+ return val;
+}
+
+void *raw_field_ptr(struct event_format *event, const char *name, void *data)
+{
+ struct format_field *field;
+
+ field = pevent_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;
+}
+
+int trace_parse_common_type(void *data)
+{
+ struct record record;
+
+ record.data = data;
+ return pevent_data_type(pevent, &record);
+}
+
+struct event_format *trace_find_event(int type)
+{
+ return pevent_find_event(pevent, type);
+}
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm)
+{
+ struct record record;
+ struct trace_seq s;
+ int pid;
+
+ pevent->latency_format = latency_format;
+
+ record.ts = nsecs;
+ record.cpu = cpu;
+ record.size = size;
+ record.data = data;
+ pid = pevent_data_pid(pevent, &record);
+
+ if (!pevent_pid_is_registered(pevent, pid))
+ pevent_register_comm(pevent, comm, pid);
+
+ trace_seq_init(&s);
+ pevent_print_event(pevent, &s, &record);
+ trace_seq_do_printf(&s);
+ printf("\n");
+
+#if 0
+ struct event *event;
+ unsigned long secs;
+ unsigned long usecs;
+ int type;
+ int pid;
+
+ secs = nsecs / NSECS_PER_SEC;
+ nsecs -= secs * NSECS_PER_SEC;
+ usecs = nsecs / NSECS_PER_USEC;
+
+ 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 (latency_format) {
+ printf("%8.8s-%-5d %3d",
+ comm, pid, cpu);
+ print_lat_fmt(data, size);
+ } else
+ printf("%16s-%-5d [%03d]", comm, pid, cpu);
+
+ printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+
+ if (event->flags & EVENT_FL_FAILED) {
+ printf("EVENT '%s' FAILED TO PARSE\n",
+ event->name);
+ return;
+ }
+
+ pretty_print(data, size, event);
+ printf("\n");
+#endif
+}
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused)
+{
+ unsigned long long addr;
+ char *func;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char *mod;
+ char ch;
+ int ret;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ mod = NULL;
+ ret = sscanf(line, "%as %c %as\t[%as",
+ &addr_str, &ch, &func, &mod);
+ addr = strtoull(addr_str, NULL, 16);
+ free(addr_str);
+
+ /* truncate the extra ']' */
+ if (mod)
+ mod[strlen(mod) - 1] = 0;
+
+ pevent_register_function(pevent, func, addr, mod);
+ free(func);
+ free(mod);
+
+ line = strtok_r(NULL, "\n", &next);
+ }
+}
+
+void parse_ftrace_printk(char *file, unsigned int size __unused)
+{
+ unsigned long long addr;
+ char *printk;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char *fmt;
+
+ 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);
+ }
+}
+
+int parse_ftrace_file(char *buf, unsigned long size)
+{
+ return pevent_parse_event(pevent, buf, size, "ftrace");
+}
+
+int parse_event_file(char *buf, unsigned long size, char *sys)
+{
+ return pevent_parse_event(pevent, buf, size, sys);
+}
diff --git a/tools/lib/trace/trace-event-read.c b/tools/lib/trace/trace-event-read.c
new file mode 100644
index 0000000..2801b1b
--- /dev/null
+++ b/tools/lib/trace/trace-event-read.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define _FILE_OFFSET_BITS 64
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "../../perf/perf.h"
+#include <lk/util.h>
+#include "trace-event.h"
+
+static int input_fd;
+
+static int read_page;
+
+int file_bigendian;
+int host_bigendian;
+static int long_size;
+
+static unsigned long page_size;
+
+static ssize_t calc_data_size;
+static bool repipe;
+
+static int do_read(int fd, void *buf, int size)
+{
+ int rsize = size;
+
+ while (size) {
+ int ret = read(fd, buf, size);
+
+ if (ret <= 0)
+ return -1;
+
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, buf, ret);
+
+ if (retw <= 0 || retw != ret)
+ die("repiping input file");
+ }
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return rsize;
+}
+
+static int read_or_die(void *data, int size)
+{
+ int r;
+
+ r = do_read(input_fd, data, size);
+ if (r <= 0)
+ die("reading input file (size expected=%d received=%d)",
+ size, r);
+
+ if (calc_data_size)
+ calc_data_size += r;
+
+ return r;
+}
+
+/* If it fails, the next read will report it */
+static void skip(int size)
+{
+ char buf[BUFSIZ];
+ int r;
+
+ while (size) {
+ r = size > BUFSIZ ? BUFSIZ : size;
+ read_or_die(buf, r);
+ size -= r;
+ };
+}
+
+static unsigned int read4(void)
+{
+ unsigned int data;
+
+ read_or_die(&data, 4);
+ return __data2host4(perf_pevent, data);
+}
+
+static unsigned long long read8(void)
+{
+ unsigned long long data;
+
+ read_or_die(&data, 8);
+ return __data2host8(perf_pevent, data);
+}
+
+static char *read_string(void)
+{
+ char buf[BUFSIZ];
+ char *str = NULL;
+ int size = 0;
+ off_t r;
+ char c;
+
+ for (;;) {
+ r = read(input_fd, &c, 1);
+ if (r < 0)
+ die("reading input file");
+
+ if (!r)
+ die("no data");
+
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, &c, 1);
+
+ if (retw <= 0 || retw != r)
+ die("repiping input file string");
+ }
+
+ buf[size++] = c;
+
+ if (!c)
+ break;
+ }
+
+ if (calc_data_size)
+ calc_data_size += size;
+
+ str = malloc_or_die(size);
+ memcpy(str, buf, size);
+
+ return str;
+}
+
+static void read_proc_kallsyms(void)
+{
+ unsigned int size;
+ char *buf;
+
+ size = read4();
+ if (!size)
+ return;
+
+ buf = malloc_or_die(size + 1);
+ read_or_die(buf, size);
+ buf[size] = '\0';
+
+ parse_proc_kallsyms(buf, size);
+
+ free(buf);
+}
+
+static void read_ftrace_printk(void)
+{
+ unsigned int size;
+ char *buf;
+
+ size = read4();
+ if (!size)
+ return;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+
+ parse_ftrace_printk(buf, size);
+
+ free(buf);
+}
+
+static void read_header_files(void)
+{
+ unsigned long long size;
+ char *header_event;
+ char buf[BUFSIZ];
+
+ read_or_die(buf, 12);
+
+ if (memcmp(buf, "header_page", 12) != 0)
+ die("did not read header page");
+
+ size = read8();
+ skip(size);
+
+ /*
+ * The size field in the page is of type long,
+ * use that instead, since it represents the kernel.
+ */
+ long_size = header_page_size_size;
+
+ read_or_die(buf, 13);
+ if (memcmp(buf, "header_event", 13) != 0)
+ die("did not read header event");
+
+ size = read8();
+ header_event = malloc_or_die(size);
+ read_or_die(header_event, size);
+ free(header_event);
+}
+
+static void read_ftrace_file(unsigned long long size)
+{
+ char *buf;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+ parse_ftrace_file(buf, size);
+ free(buf);
+}
+
+static void read_event_file(char *sys, unsigned long long size)
+{
+ char *buf;
+
+ buf = malloc_or_die(size);
+ read_or_die(buf, size);
+ parse_event_file(buf, size, sys);
+ free(buf);
+}
+
+static void read_ftrace_files(void)
+{
+ unsigned long long size;
+ int count;
+ int i;
+
+ count = read4();
+
+ for (i = 0; i < count; i++) {
+ size = read8();
+ read_ftrace_file(size);
+ }
+}
+
+static void read_event_files(void)
+{
+ unsigned long long size;
+ char *sys;
+ int systems;
+ int count;
+ int i,x;
+
+ systems = read4();
+
+ for (i = 0; i < systems; i++) {
+ sys = read_string();
+
+ count = read4();
+ for (x=0; x < count; x++) {
+ size = read8();
+ read_event_file(sys, size);
+ }
+ }
+}
+
+struct cpu_data {
+ unsigned long long offset;
+ unsigned long long size;
+ unsigned long long timestamp;
+ struct record *next;
+ char *page;
+ int cpu;
+ int index;
+ int page_size;
+};
+
+static struct cpu_data *cpu_data;
+
+static void update_cpu_data_index(int cpu)
+{
+ cpu_data[cpu].offset += page_size;
+ cpu_data[cpu].size -= page_size;
+ cpu_data[cpu].index = 0;
+}
+
+static void get_next_page(int cpu)
+{
+ off_t save_seek;
+ off_t ret;
+
+ if (!cpu_data[cpu].page)
+ return;
+
+ if (read_page) {
+ if (cpu_data[cpu].size <= page_size) {
+ free(cpu_data[cpu].page);
+ cpu_data[cpu].page = NULL;
+ return;
+ }
+
+ update_cpu_data_index(cpu);
+
+ /* other parts of the code may expect the pointer to not move */
+ save_seek = lseek(input_fd, 0, SEEK_CUR);
+
+ ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET);
+ if (ret == (off_t)-1)
+ die("failed to lseek");
+ ret = read(input_fd, cpu_data[cpu].page, page_size);
+ if (ret < 0)
+ die("failed to read page");
+
+ /* reset the file pointer back */
+ lseek(input_fd, save_seek, SEEK_SET);
+
+ return;
+ }
+
+ munmap(cpu_data[cpu].page, page_size);
+ cpu_data[cpu].page = NULL;
+
+ if (cpu_data[cpu].size <= page_size)
+ return;
+
+ update_cpu_data_index(cpu);
+
+ cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE,
+ input_fd, cpu_data[cpu].offset);
+ if (cpu_data[cpu].page == MAP_FAILED)
+ die("failed to mmap cpu %d at offset 0x%llx",
+ cpu, cpu_data[cpu].offset);
+}
+
+static unsigned int type_len4host(unsigned int type_len_ts)
+{
+ if (file_bigendian)
+ return (type_len_ts >> 27) & ((1 << 5) - 1);
+ else
+ return type_len_ts & ((1 << 5) - 1);
+}
+
+static unsigned int ts4host(unsigned int type_len_ts)
+{
+ if (file_bigendian)
+ return type_len_ts & ((1 << 27) - 1);
+ else
+ return type_len_ts >> 5;
+}
+
+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 record *data;
+ void *page = cpu_data[cpu].page;
+ int idx = cpu_data[cpu].index;
+ void *ptr = page + idx;
+ unsigned long long extend;
+ unsigned int type_len_ts;
+ unsigned int type_len;
+ unsigned int delta;
+ unsigned int length = 0;
+
+ if (cpu_data[cpu].next)
+ return cpu_data[cpu].next;
+
+ if (!page)
+ return NULL;
+
+ if (!idx) {
+ /* FIXME: handle header page */
+ if (header_page_ts_size != 8)
+ die("expected a long long type for timestamp");
+ cpu_data[cpu].timestamp = data2host8(perf_pevent, ptr);
+ ptr += 8;
+ switch (header_page_size_size) {
+ case 4:
+ cpu_data[cpu].page_size = data2host4(perf_pevent, ptr);
+ ptr += 4;
+ break;
+ case 8:
+ cpu_data[cpu].page_size = data2host8(perf_pevent, ptr);
+ ptr += 8;
+ break;
+ default:
+ die("bad long size");
+ }
+ ptr = cpu_data[cpu].page + header_page_data_offset;
+ }
+
+read_again:
+ idx = calc_index(ptr, cpu);
+
+ if (idx >= cpu_data[cpu].page_size) {
+ get_next_page(cpu);
+ return trace_peek_data(cpu);
+ }
+
+ type_len_ts = data2host4(perf_pevent, ptr);
+ ptr += 4;
+
+ type_len = type_len4host(type_len_ts);
+ delta = ts4host(type_len_ts);
+
+ switch (type_len) {
+ case RINGBUF_TYPE_PADDING:
+ if (!delta)
+ die("error, hit unexpected end of page");
+ length = data2host4(perf_pevent, ptr);
+ ptr += 4;
+ length *= 4;
+ ptr += length;
+ goto read_again;
+
+ case RINGBUF_TYPE_TIME_EXTEND:
+ extend = data2host4(perf_pevent, ptr);
+ ptr += 4;
+ extend <<= TS_SHIFT;
+ extend += delta;
+ cpu_data[cpu].timestamp += extend;
+ goto read_again;
+
+ case RINGBUF_TYPE_TIME_STAMP:
+ ptr += 12;
+ break;
+ case 0:
+ length = data2host4(perf_pevent, ptr);
+ ptr += 4;
+ die("here! length=%d", length);
+ break;
+ default:
+ length = type_len * 4;
+ break;
+ }
+
+ cpu_data[cpu].timestamp += delta;
+
+ data = malloc_or_die(sizeof(*data));
+ memset(data, 0, sizeof(*data));
+
+ data->ts = cpu_data[cpu].timestamp;
+ data->size = length;
+ data->data = ptr;
+ ptr += length;
+
+ cpu_data[cpu].index = calc_index(ptr, cpu);
+ cpu_data[cpu].next = data;
+
+ return data;
+}
+
+struct record *trace_read_data(int cpu)
+{
+ struct record *data;
+
+ data = trace_peek_data(cpu);
+ cpu_data[cpu].next = NULL;
+
+ return data;
+}
+
+ssize_t trace_report(int fd, bool __repipe)
+{
+ char buf[BUFSIZ];
+ char test[] = { 23, 8, 68 };
+ char *version;
+ int show_version = 0;
+ int show_funcs = 0;
+ int show_printk = 0;
+ ssize_t size;
+
+ calc_data_size = 1;
+ repipe = __repipe;
+
+ input_fd = fd;
+
+ read_or_die(buf, 3);
+ if (memcmp(buf, test, 3) != 0)
+ die("no trace data in the file");
+
+ read_or_die(buf, 7);
+ if (memcmp(buf, "tracing", 7) != 0)
+ die("not a trace file (missing 'tracing' tag)");
+
+ version = read_string();
+ if (show_version)
+ printf("version = %s\n", version);
+ free(version);
+
+ read_or_die(buf, 1);
+ file_bigendian = buf[0];
+ host_bigendian = bigendian();
+
+ read_trace_init(file_bigendian, host_bigendian);
+
+ read_or_die(buf, 1);
+ long_size = buf[0];
+
+ page_size = read4();
+
+ read_header_files();
+
+ read_ftrace_files();
+ read_event_files();
+ read_proc_kallsyms();
+ read_ftrace_printk();
+
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+ repipe = false;
+
+ if (show_funcs) {
+ pevent_print_funcs(perf_pevent);
+ return size;
+ }
+ if (show_printk) {
+ pevent_print_printk(perf_pevent);
+ return size;
+ }
+
+ return size;
+}
diff --git a/tools/lib/trace/trace-event.h b/tools/lib/trace/trace-event.h
new file mode 100644
index 0000000..9c9d342
--- /dev/null
+++ b/tools/lib/trace/trace-event.h
@@ -0,0 +1,115 @@
+#ifndef _PERF_UTIL_TRACE_EVENT_H
+#define _PERF_UTIL_TRACE_EVENT_H
+
+#include "../../perf/perf.h"
+#include "parse-events.h"
+
+extern int header_page_size_size;
+extern int header_page_ts_size;
+extern int header_page_data_offset;
+
+extern bool latency_format;
+extern struct pevent *perf_pevent;
+
+struct scripting_context {
+ void *event_data;
+};
+
+enum {
+ RINGBUF_TYPE_PADDING = 29,
+ RINGBUF_TYPE_TIME_EXTEND = 30,
+ RINGBUF_TYPE_TIME_STAMP = 31,
+};
+
+#ifndef TS_SHIFT
+#define TS_SHIFT 27
+#endif
+
+int bigendian(void);
+
+int read_trace_init(int file_bigendian, int host_bigendian);
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+ char *comm);
+
+struct record *trace_read_data(int cpu);
+int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
+ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
+ int nb_events);
+
+int parse_ftrace_file(char *buf, unsigned long size);
+int parse_event_file(char *buf, unsigned long size, char *sys);
+
+struct record *trace_peek_data(int cpu);
+struct event_format *trace_find_event(int type);
+
+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);
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused);
+void parse_ftrace_printk(char *file, unsigned int size __unused);
+
+ssize_t trace_report(int fd, bool repipe);
+
+struct scripting_ops {
+ const char *name;
+ int (*start_script) (const char *script, int argc, const char **argv);
+ int (*stop_script) (void);
+ void (*process_event) (int cpu, void *data, int size,
+ unsigned long long nsecs, char *comm);
+ int (*generate_script) (const char *outfile);
+};
+
+int script_spec_register(const char *spec, struct scripting_ops *ops);
+
+void setup_perl_scripting(void);
+void setup_python_scripting(void);
+
+int trace_parse_common_type(void *data);
+int trace_parse_common_pid(void *data);
+#if 0
+int parse_common_pc(void *data);
+int parse_common_flags(void *data);
+int parse_common_lock_depth(void *data);
+struct event_format *trace_find_event(int id);
+#endif
+struct event_format *trace_find_next_event(struct event_format *event);
+unsigned long long read_size(void *ptr, int size);
+#if 0
+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);
+#endif
+unsigned long long eval_flag(const char *flag);
+
+int common_pc(struct scripting_context *context);
+int common_flags(struct scripting_context *context);
+int common_lock_depth(struct scripting_context *context);
+
+#define MAX_EVENT_LENGTH 512
+#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
+
+#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
+ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
+ if (sys_dirent.d_type == DT_DIR && \
+ (strcmp(sys_dirent.d_name, ".")) && \
+ (strcmp(sys_dirent.d_name, "..")))
+
+#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
+ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
+ if (evt_dirent.d_type == DT_DIR && \
+ (strcmp(evt_dirent.d_name, ".")) && \
+ (strcmp(evt_dirent.d_name, "..")) && \
+ (!tp_event_has_id(&sys_dirent, &evt_dirent)))
+
+
+struct tracepoint_path {
+ char *system;
+ char *name;
+ struct tracepoint_path *next;
+};
+
+extern int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir);
+extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
+#endif /* _PERF_UTIL_TRACE_EVENT_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 98cdd20..f899b08 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -370,7 +370,6 @@ LIB_H += util/values.h
LIB_H += util/sort.h
LIB_H += util/hist.h
LIB_H += util/thread.h
-LIB_H += util/trace-event.h
LIB_H += util/probe-finder.h
LIB_H += util/probe-event.h
LIB_H += util/pstack.h
@@ -399,9 +398,6 @@ LIB_OBJS += $(OUTPUT)util/debug.o
LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
-LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
-LIB_OBJS += $(OUTPUT)util/trace-event-read.o
-LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
LIB_OBJS += $(OUTPUT)util/svghelper.o
LIB_OBJS += $(OUTPUT)util/sort.o
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 703cc93..e432afe 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -9,7 +9,7 @@
#include "util/session.h"
#include "util/parse-options.h"
-#include "util/trace-event.h"
+#include <trace/trace-event.h>
#include "util/debug.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index b849f99e..75ef557 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -9,7 +9,7 @@
#include "util/session.h"
#include "util/parse-options.h"
-#include "util/trace-event.h"
+#include <trace/trace-event.h>
#include "util/debug.h"
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 6257d26..94b454f 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -8,7 +8,7 @@
#include "util/header.h"
#include "util/parse-options.h"
-#include "util/trace-event.h"
+#include <trace/trace-event.h>
#include "util/debug.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index c881900..4ef6fe1 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -9,7 +9,7 @@
#include "util/session.h"
#include "util/parse-options.h"
-#include "util/trace-event.h"
+#include <trace/trace-event.h>
#include "util/debug.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 43cfc18..2b010ec 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -314,25 +314,6 @@ struct wakeup_entry {
int success;
};
-/*
- * trace_flag_type is an enumeration that holds different
- * states when a trace occurs. These are:
- * IRQS_OFF - interrupts were disabled
- * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags
- * NEED_RESCED - reschedule is requested
- * HARDIRQ - inside an interrupt handler
- * SOFTIRQ - inside a softirq handler
- */
-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 sched_switch {
struct trace_entry te;
char prev_comm[TASK_COMM_LEN];
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index a6c41c6..f41a9e0 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -9,7 +9,7 @@
#include "util/session.h"
#include <perf/config.h>
#include "util/thread.h"
-#include "util/trace-event.h"
+#include <trace/trace-event.h>
#include <lk/util.h>
static char const *script_name;
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
index 01a64ad..f38aacf 100644
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
@@ -32,7 +32,7 @@
#include "perl.h"
#include "XSUB.h"
#include "../../../perf.h"
-#include "../../../util/trace-event.h"
+#include <trace/trace-event.h>
#ifndef PERL_UNUSED_VAR
# define PERL_UNUSED_VAR(var) if (0) var = var
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
index 549cf04..baff89f 100644
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
@@ -23,7 +23,7 @@
#include "perl.h"
#include "XSUB.h"
#include "../../../perf.h"
-#include "../../../util/trace-event.h"
+#include <trace/trace-event.h>
MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
PROTOTYPES: ENABLE
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
index 957085d..6474be7 100644
--- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c
+++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
@@ -21,7 +21,7 @@
#include <Python.h>
#include "../../../perf.h"
-#include "../../../util/trace-event.h"
+#include <trace/trace-event.h>
PyMODINIT_FUNC initperf_trace_context(void);
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index f233545..e0f84b5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -6,15 +6,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>
+#include <lk/util.h>
#include <lk/kernel.h>
+#include <perf/symbol.h>
+#include <trace/trace-event.h>
-#include <lk/util.h>
#include "header.h"
#include "../perf.h"
-#include "trace-event.h"
#include "session.h"
-#include <perf/symbol.h>
#include "debug.h"
+#include "parse-events.h"
static bool no_buildid_cache = false;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index ad36237..6e1e01c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -8,6 +8,7 @@
#include "exec_cmd.h"
#include "string.h"
#include <perf/symbol.h>
+#include <trace/trace-event.h>
#include "cache.h"
#include "header.h"
@@ -29,8 +30,6 @@ enum event_result {
EVT_HANDLED_ALL
};
-char debugfs_path[MAXPATHLEN];
-
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -126,106 +125,6 @@ static unsigned long hw_cache_stat[C(MAX)] = {
[C(BPU)] = (CACHE_READ),
};
-#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
- while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
- if (sys_dirent.d_type == DT_DIR && \
- (strcmp(sys_dirent.d_name, ".")) && \
- (strcmp(sys_dirent.d_name, "..")))
-
-static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
-{
- char evt_path[MAXPATHLEN];
- int fd;
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
- sys_dir->d_name, evt_dir->d_name);
- fd = open(evt_path, O_RDONLY);
- if (fd < 0)
- return -EINVAL;
- close(fd);
-
- return 0;
-}
-
-#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
- while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
- if (evt_dirent.d_type == DT_DIR && \
- (strcmp(evt_dirent.d_name, ".")) && \
- (strcmp(evt_dirent.d_name, "..")) && \
- (!tp_event_has_id(&sys_dirent, &evt_dirent)))
-
-#define MAX_EVENT_LENGTH 512
-
-
-struct tracepoint_path *tracepoint_id_to_path(u64 config)
-{
- struct tracepoint_path *path = NULL;
- DIR *sys_dir, *evt_dir;
- struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
- char id_buf[4];
- int fd;
- u64 id;
- char evt_path[MAXPATHLEN];
- char dir_path[MAXPATHLEN];
-
- if (debugfs_valid_mountpoint(debugfs_path))
- return NULL;
-
- sys_dir = opendir(debugfs_path);
- if (!sys_dir)
- return NULL;
-
- for_each_subsystem(sys_dir, sys_dirent, sys_next) {
-
- snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
- sys_dirent.d_name);
- evt_dir = opendir(dir_path);
- if (!evt_dir)
- continue;
-
- for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
- evt_dirent.d_name);
- fd = open(evt_path, O_RDONLY);
- if (fd < 0)
- continue;
- if (read(fd, id_buf, sizeof(id_buf)) < 0) {
- close(fd);
- continue;
- }
- close(fd);
- id = atoll(id_buf);
- if (id == config) {
- closedir(evt_dir);
- closedir(sys_dir);
- path = zalloc(sizeof(*path));
- path->system = malloc(MAX_EVENT_LENGTH);
- if (!path->system) {
- free(path);
- return NULL;
- }
- path->name = malloc(MAX_EVENT_LENGTH);
- if (!path->name) {
- free(path->system);
- free(path);
- return NULL;
- }
- strncpy(path->system, sys_dirent.d_name,
- MAX_EVENT_LENGTH);
- strncpy(path->name, evt_dirent.d_name,
- MAX_EVENT_LENGTH);
- return path;
- }
- }
- closedir(evt_dir);
- }
-
- closedir(sys_dir);
- return NULL;
-}
-
-#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
static const char *tracepoint_id_to_name(u64 config)
{
static char buf[TP_PATH_LEN];
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 9492e3c..41ebdc4 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -4,16 +4,10 @@
* Parse symbolic events/counts passed in as options:
*/
#include <perf/util.h>
+#include <trace/parse-events.h>
struct option;
-struct tracepoint_path {
- char *system;
- char *name;
- struct tracepoint_path *next;
-};
-
-extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events);
extern int nr_counters;
@@ -31,7 +25,6 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
extern void print_events(void);
-extern char debugfs_path[];
extern int valid_debugfs_mount(const char *debugfs);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 577944c..2bed689 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -44,7 +44,7 @@
#include <perf/color.h>
#include <perf/symbol.h>
#include "thread.h"
-#include "trace-event.h" /* For __unused */
+#include <trace/trace-event.h> /* For __unused */
#include "probe-event.h"
#include "probe-finder.h"
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 78af7d1..af37491 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -27,7 +27,7 @@
#include "../../perf.h"
#include <lk/util.h>
-#include "../trace-event.h"
+#include <trace/trace-event.h>
#include <EXTERN.h>
#include <perl.h>
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 524d355..464c25a 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -29,7 +29,7 @@
#include "../../perf.h"
#include <lk/util.h>
-#include "../trace-event.h"
+#include <trace/trace-event.h>
PyMODINIT_FUNC initperf_trace_context(void);
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
deleted file mode 100644
index fadc2c0..0000000
--- a/tools/perf/util/trace-event-info.c
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define _GNU_SOURCE
-#include <dirent.h>
-#include <mntent.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <pthread.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdbool.h>
-#include <lk/kernel.h>
-
-#include <lk/debugfs.h>
-
-#include "../perf.h"
-#include "trace-event.h"
-
-#define VERSION "0.5"
-
-#define _STR(x) #x
-#define STR(x) _STR(x)
-#define MAX_PATH 256
-
-#define TRACE_CTRL "tracing_on"
-#define TRACE "trace"
-#define AVAILABLE "available_tracers"
-#define CURRENT "current_tracer"
-#define ITER_CTRL "trace_options"
-#define MAX_LATENCY "tracing_max_latency"
-
-unsigned int page_size;
-
-static const char *output_file = "trace.info";
-static int output_fd;
-
-struct event_list {
- struct event_list *next;
- const char *event;
-};
-
-struct events {
- struct events *sibling;
- struct events *children;
- struct events *next;
- char *name;
-};
-
-
-
-static void die(const char *fmt, ...)
-{
- va_list ap;
- int ret = errno;
-
- if (errno)
- perror("trace-cmd");
- else
- ret = -1;
-
- va_start(ap, fmt);
- fprintf(stderr, " ");
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fprintf(stderr, "\n");
- exit(ret);
-}
-
-void *malloc_or_die(unsigned int size)
-{
- void *data;
-
- data = malloc(size);
- if (!data)
- die("malloc");
- return data;
-}
-
-static const char *find_debugfs(void)
-{
- const char *path = debugfs_mount(NULL);
-
- if (!path)
- die("Your kernel not support debugfs filesystem");
-
- return path;
-}
-
-/*
- * Finds the path to the debugfs/tracing
- * Allocates the string and stores it.
- */
-static const char *find_tracing_dir(void)
-{
- static char *tracing;
- static int tracing_found;
- const char *debugfs;
-
- if (tracing_found)
- return tracing;
-
- debugfs = find_debugfs();
-
- tracing = malloc_or_die(strlen(debugfs) + 9);
-
- sprintf(tracing, "%s/tracing", debugfs);
-
- tracing_found = 1;
- return tracing;
-}
-
-static char *get_tracing_file(const char *name)
-{
- const char *tracing;
- char *file;
-
- tracing = find_tracing_dir();
- if (!tracing)
- return NULL;
-
- file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
-
- sprintf(file, "%s/%s", tracing, name);
- return file;
-}
-
-static void put_tracing_file(char *file)
-{
- free(file);
-}
-
-static ssize_t calc_data_size;
-
-static ssize_t write_or_die(const void *buf, size_t len)
-{
- int ret;
-
- if (calc_data_size) {
- calc_data_size += len;
- return len;
- }
-
- ret = write(output_fd, buf, len);
- if (ret < 0)
- die("writing to '%s'", output_file);
-
- return ret;
-}
-
-int bigendian(void)
-{
- unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
- unsigned int *ptr;
-
- ptr = (unsigned int *)(void *)str;
- return *ptr == 0x01020304;
-}
-
-static unsigned long long copy_file_fd(int fd)
-{
- unsigned long long size = 0;
- char buf[BUFSIZ];
- int r;
-
- do {
- r = read(fd, buf, BUFSIZ);
- if (r > 0) {
- size += r;
- write_or_die(buf, r);
- }
- } while (r > 0);
-
- return size;
-}
-
-static unsigned long long copy_file(const char *file)
-{
- unsigned long long size = 0;
- int fd;
-
- fd = open(file, O_RDONLY);
- if (fd < 0)
- die("Can't read '%s'", file);
- size = copy_file_fd(fd);
- close(fd);
-
- return size;
-}
-
-static unsigned long get_size_fd(int fd)
-{
- unsigned long long size = 0;
- char buf[BUFSIZ];
- int r;
-
- do {
- r = read(fd, buf, BUFSIZ);
- if (r > 0)
- size += r;
- } while (r > 0);
-
- lseek(fd, 0, SEEK_SET);
-
- return size;
-}
-
-static unsigned long get_size(const char *file)
-{
- unsigned long long size = 0;
- int fd;
-
- fd = open(file, O_RDONLY);
- if (fd < 0)
- die("Can't read '%s'", file);
- size = get_size_fd(fd);
- close(fd);
-
- return size;
-}
-
-static void read_header_files(void)
-{
- unsigned long long size, check_size;
- char *path;
- int fd;
-
- path = get_tracing_file("events/header_page");
- fd = open(path, O_RDONLY);
- if (fd < 0)
- die("can't read '%s'", path);
-
- /* unfortunately, you can not stat debugfs files for size */
- size = get_size_fd(fd);
-
- write_or_die("header_page", 12);
- write_or_die(&size, 8);
- check_size = copy_file_fd(fd);
- close(fd);
-
- if (size != check_size)
- die("wrong size for '%s' size=%lld read=%lld",
- path, size, check_size);
- put_tracing_file(path);
-
- path = get_tracing_file("events/header_event");
- fd = open(path, O_RDONLY);
- if (fd < 0)
- die("can't read '%s'", path);
-
- size = get_size_fd(fd);
-
- write_or_die("header_event", 13);
- write_or_die(&size, 8);
- check_size = copy_file_fd(fd);
- if (size != check_size)
- die("wrong size for '%s'", path);
- put_tracing_file(path);
- close(fd);
-}
-
-static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
-{
- while (tps) {
- if (!strcmp(sys, tps->name))
- return true;
- tps = tps->next;
- }
-
- return false;
-}
-
-static void copy_event_system(const char *sys, struct tracepoint_path *tps)
-{
- unsigned long long size, check_size;
- struct dirent *dent;
- struct stat st;
- char *format;
- DIR *dir;
- int count = 0;
- int ret;
-
- dir = opendir(sys);
- if (!dir)
- die("can't read directory '%s'", sys);
-
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- !name_in_tp_list(dent->d_name, tps))
- continue;
- format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
- sprintf(format, "%s/%s/format", sys, dent->d_name);
- ret = stat(format, &st);
- free(format);
- if (ret < 0)
- continue;
- count++;
- }
-
- write_or_die(&count, 4);
-
- rewinddir(dir);
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- !name_in_tp_list(dent->d_name, tps))
- continue;
- format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
- sprintf(format, "%s/%s/format", sys, dent->d_name);
- ret = stat(format, &st);
-
- if (ret >= 0) {
- /* unfortunately, you can not stat debugfs files for size */
- size = get_size(format);
- write_or_die(&size, 8);
- check_size = copy_file(format);
- if (size != check_size)
- die("error in size of file '%s'", format);
- }
-
- free(format);
- }
- closedir(dir);
-}
-
-static void read_ftrace_files(struct tracepoint_path *tps)
-{
- char *path;
-
- path = get_tracing_file("events/ftrace");
-
- copy_event_system(path, tps);
-
- put_tracing_file(path);
-}
-
-static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
-{
- while (tps) {
- if (!strcmp(sys, tps->system))
- return true;
- tps = tps->next;
- }
-
- return false;
-}
-
-static void read_event_files(struct tracepoint_path *tps)
-{
- struct dirent *dent;
- struct stat st;
- char *path;
- char *sys;
- DIR *dir;
- int count = 0;
- int ret;
-
- path = get_tracing_file("events");
-
- dir = opendir(path);
- if (!dir)
- die("can't read directory '%s'", path);
-
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- strcmp(dent->d_name, "ftrace") == 0 ||
- !system_in_tp_list(dent->d_name, tps))
- continue;
- count++;
- }
-
- write_or_die(&count, 4);
-
- rewinddir(dir);
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- strcmp(dent->d_name, "ftrace") == 0 ||
- !system_in_tp_list(dent->d_name, tps))
- continue;
- sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
- sprintf(sys, "%s/%s", path, dent->d_name);
- ret = stat(sys, &st);
- if (ret >= 0) {
- write_or_die(dent->d_name, strlen(dent->d_name) + 1);
- copy_event_system(sys, tps);
- }
- free(sys);
- }
-
- closedir(dir);
- put_tracing_file(path);
-}
-
-static void read_proc_kallsyms(void)
-{
- unsigned int size, check_size;
- const char *path = "/proc/kallsyms";
- struct stat st;
- int ret;
-
- ret = stat(path, &st);
- if (ret < 0) {
- /* not found */
- size = 0;
- write_or_die(&size, 4);
- return;
- }
- size = get_size(path);
- write_or_die(&size, 4);
- check_size = copy_file(path);
- if (size != check_size)
- die("error in size of file '%s'", path);
-
-}
-
-static void read_ftrace_printk(void)
-{
- unsigned int size, check_size;
- char *path;
- struct stat st;
- int ret;
-
- path = get_tracing_file("printk_formats");
- ret = stat(path, &st);
- if (ret < 0) {
- /* not found */
- size = 0;
- write_or_die(&size, 4);
- goto out;
- }
- size = get_size(path);
- write_or_die(&size, 4);
- check_size = copy_file(path);
- if (size != check_size)
- die("error in size of file '%s'", path);
-out:
- put_tracing_file(path);
-}
-
-static struct tracepoint_path *
-get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events)
-{
- struct tracepoint_path path, *ppath = &path;
- int i, nr_tracepoints = 0;
-
- for (i = 0; i < nb_events; i++) {
- if (pattrs[i].type != PERF_TYPE_TRACEPOINT)
- continue;
- ++nr_tracepoints;
- ppath->next = tracepoint_id_to_path(pattrs[i].config);
- if (!ppath->next)
- die("%s\n", "No memory to alloc tracepoints list");
- ppath = ppath->next;
- }
-
- return nr_tracepoints > 0 ? path.next : NULL;
-}
-
-bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events)
-{
- int i;
-
- for (i = 0; i < nb_events; i++)
- if (pattrs[i].type == PERF_TYPE_TRACEPOINT)
- return true;
-
- return false;
-}
-
-int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
-{
- char buf[BUFSIZ];
- struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events);
-
- /*
- * What? No tracepoints? No sense writing anything here, bail out.
- */
- if (tps == NULL)
- return -1;
-
- output_fd = fd;
-
- buf[0] = 23;
- buf[1] = 8;
- buf[2] = 68;
- memcpy(buf + 3, "tracing", 7);
-
- write_or_die(buf, 10);
-
- write_or_die(VERSION, strlen(VERSION) + 1);
-
- /* save endian */
- if (bigendian())
- buf[0] = 1;
- else
- buf[0] = 0;
-
- read_trace_init(buf[0], buf[0]);
-
- write_or_die(buf, 1);
-
- /* save size of long */
- buf[0] = sizeof(long);
- write_or_die(buf, 1);
-
- /* save page_size */
- page_size = sysconf(_SC_PAGESIZE);
- write_or_die(&page_size, 4);
-
- read_header_files();
- read_ftrace_files(tps);
- read_event_files(tps);
- read_proc_kallsyms();
- read_ftrace_printk();
-
- return 0;
-}
-
-ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
- int nb_events)
-{
- ssize_t size;
- int err = 0;
-
- calc_data_size = 1;
- err = read_tracing_data(fd, pattrs, nb_events);
- size = calc_data_size - 1;
- calc_data_size = 0;
-
- if (err < 0)
- return err;
-
- return size;
-}
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
deleted file mode 100644
index 489907c..0000000
--- a/tools/perf/util/trace-event-parse.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#undef _GNU_SOURCE
-#include "../perf.h"
-#include <lk/util.h>
-#include "trace-event.h"
-
-int header_page_size_size;
-int header_page_ts_size;
-int header_page_data_offset;
-
-struct pevent *perf_pevent;
-static struct pevent *pevent;
-
-bool latency_format;
-
-int read_trace_init(int file_bigendian, int host_bigendian)
-{
- if (pevent)
- return 0;
-
- perf_pevent = pevent_alloc();
- pevent = perf_pevent;
-
- pevent_set_file_bigendian(pevent, file_bigendian);
- pevent_set_host_bigendian(pevent, host_bigendian);
-
- return 0;
-}
-
-unsigned long long
-raw_field_value(struct event_format *event, const char *name, void *data)
-{
- struct format_field *field;
- unsigned long long val;
-
- field = pevent_find_any_field(event, name);
- if (!field)
- return 0ULL;
-
- pevent_read_number_field(field, data, &val);
-
- return val;
-}
-
-void *raw_field_ptr(struct event_format *event, const char *name, void *data)
-{
- struct format_field *field;
-
- field = pevent_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;
-}
-
-int trace_parse_common_type(void *data)
-{
- struct record record;
-
- record.data = data;
- return pevent_data_type(pevent, &record);
-}
-
-struct event_format *trace_find_event(int type)
-{
- return pevent_find_event(pevent, type);
-}
-
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm)
-{
- struct record record;
- struct trace_seq s;
- int pid;
-
- pevent->latency_format = latency_format;
-
- record.ts = nsecs;
- record.cpu = cpu;
- record.size = size;
- record.data = data;
- pid = pevent_data_pid(pevent, &record);
-
- if (!pevent_pid_is_registered(pevent, pid))
- pevent_register_comm(pevent, comm, pid);
-
- trace_seq_init(&s);
- pevent_print_event(pevent, &s, &record);
- trace_seq_do_printf(&s);
- printf("\n");
-
-#if 0
- struct event *event;
- unsigned long secs;
- unsigned long usecs;
- int type;
- int pid;
-
- secs = nsecs / NSECS_PER_SEC;
- nsecs -= secs * NSECS_PER_SEC;
- usecs = nsecs / NSECS_PER_USEC;
-
- 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 (latency_format) {
- printf("%8.8s-%-5d %3d",
- comm, pid, cpu);
- print_lat_fmt(data, size);
- } else
- printf("%16s-%-5d [%03d]", comm, pid, cpu);
-
- printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
-
- if (event->flags & EVENT_FL_FAILED) {
- printf("EVENT '%s' FAILED TO PARSE\n",
- event->name);
- return;
- }
-
- pretty_print(data, size, event);
- printf("\n");
-#endif
-}
-
-void parse_proc_kallsyms(char *file, unsigned int size __unused)
-{
- unsigned long long addr;
- char *func;
- char *line;
- char *next = NULL;
- char *addr_str;
- char *mod;
- char ch;
- int ret;
-
- line = strtok_r(file, "\n", &next);
- while (line) {
- mod = NULL;
- ret = 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);
-
- /* truncate the extra ']' */
- if (mod)
- mod[strlen(mod) - 1] = 0;
-
- pevent_register_function(pevent, func, addr, mod);
- free(func);
- free(mod);
-
- line = strtok_r(NULL, "\n", &next);
- }
-}
-
-void parse_ftrace_printk(char *file, unsigned int size __unused)
-{
- unsigned long long addr;
- char *printk;
- char *line;
- char *next = NULL;
- char *addr_str;
- char *fmt;
-
- 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);
- }
-}
-
-int parse_ftrace_file(char *buf, unsigned long size)
-{
- return pevent_parse_event(pevent, buf, size, "ftrace");
-}
-
-int parse_event_file(char *buf, unsigned long size, char *sys)
-{
- return pevent_parse_event(pevent, buf, size, sys);
-}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
deleted file mode 100644
index d42a5a9..0000000
--- a/tools/perf/util/trace-event-read.c
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define _FILE_OFFSET_BITS 64
-
-#include <dirent.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/mman.h>
-#include <pthread.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "../perf.h"
-#include <lk/util.h>
-#include "trace-event.h"
-
-static int input_fd;
-
-static int read_page;
-
-int file_bigendian;
-int host_bigendian;
-static int long_size;
-
-static unsigned long page_size;
-
-static ssize_t calc_data_size;
-static bool repipe;
-
-static int do_read(int fd, void *buf, int size)
-{
- int rsize = size;
-
- while (size) {
- int ret = read(fd, buf, size);
-
- if (ret <= 0)
- return -1;
-
- if (repipe) {
- int retw = write(STDOUT_FILENO, buf, ret);
-
- if (retw <= 0 || retw != ret)
- die("repiping input file");
- }
-
- size -= ret;
- buf += ret;
- }
-
- return rsize;
-}
-
-static int read_or_die(void *data, int size)
-{
- int r;
-
- r = do_read(input_fd, data, size);
- if (r <= 0)
- die("reading input file (size expected=%d received=%d)",
- size, r);
-
- if (calc_data_size)
- calc_data_size += r;
-
- return r;
-}
-
-/* If it fails, the next read will report it */
-static void skip(int size)
-{
- char buf[BUFSIZ];
- int r;
-
- while (size) {
- r = size > BUFSIZ ? BUFSIZ : size;
- read_or_die(buf, r);
- size -= r;
- };
-}
-
-static unsigned int read4(void)
-{
- unsigned int data;
-
- read_or_die(&data, 4);
- return __data2host4(perf_pevent, data);
-}
-
-static unsigned long long read8(void)
-{
- unsigned long long data;
-
- read_or_die(&data, 8);
- return __data2host8(perf_pevent, data);
-}
-
-static char *read_string(void)
-{
- char buf[BUFSIZ];
- char *str = NULL;
- int size = 0;
- off_t r;
- char c;
-
- for (;;) {
- r = read(input_fd, &c, 1);
- if (r < 0)
- die("reading input file");
-
- if (!r)
- die("no data");
-
- if (repipe) {
- int retw = write(STDOUT_FILENO, &c, 1);
-
- if (retw <= 0 || retw != r)
- die("repiping input file string");
- }
-
- buf[size++] = c;
-
- if (!c)
- break;
- }
-
- if (calc_data_size)
- calc_data_size += size;
-
- str = malloc_or_die(size);
- memcpy(str, buf, size);
-
- return str;
-}
-
-static void read_proc_kallsyms(void)
-{
- unsigned int size;
- char *buf;
-
- size = read4();
- if (!size)
- return;
-
- buf = malloc_or_die(size + 1);
- read_or_die(buf, size);
- buf[size] = '\0';
-
- parse_proc_kallsyms(buf, size);
-
- free(buf);
-}
-
-static void read_ftrace_printk(void)
-{
- unsigned int size;
- char *buf;
-
- size = read4();
- if (!size)
- return;
-
- buf = malloc_or_die(size);
- read_or_die(buf, size);
-
- parse_ftrace_printk(buf, size);
-
- free(buf);
-}
-
-static void read_header_files(void)
-{
- unsigned long long size;
- char *header_event;
- char buf[BUFSIZ];
-
- read_or_die(buf, 12);
-
- if (memcmp(buf, "header_page", 12) != 0)
- die("did not read header page");
-
- size = read8();
- skip(size);
-
- /*
- * The size field in the page is of type long,
- * use that instead, since it represents the kernel.
- */
- long_size = header_page_size_size;
-
- read_or_die(buf, 13);
- if (memcmp(buf, "header_event", 13) != 0)
- die("did not read header event");
-
- size = read8();
- header_event = malloc_or_die(size);
- read_or_die(header_event, size);
- free(header_event);
-}
-
-static void read_ftrace_file(unsigned long long size)
-{
- char *buf;
-
- buf = malloc_or_die(size);
- read_or_die(buf, size);
- parse_ftrace_file(buf, size);
- free(buf);
-}
-
-static void read_event_file(char *sys, unsigned long long size)
-{
- char *buf;
-
- buf = malloc_or_die(size);
- read_or_die(buf, size);
- parse_event_file(buf, size, sys);
- free(buf);
-}
-
-static void read_ftrace_files(void)
-{
- unsigned long long size;
- int count;
- int i;
-
- count = read4();
-
- for (i = 0; i < count; i++) {
- size = read8();
- read_ftrace_file(size);
- }
-}
-
-static void read_event_files(void)
-{
- unsigned long long size;
- char *sys;
- int systems;
- int count;
- int i,x;
-
- systems = read4();
-
- for (i = 0; i < systems; i++) {
- sys = read_string();
-
- count = read4();
- for (x=0; x < count; x++) {
- size = read8();
- read_event_file(sys, size);
- }
- }
-}
-
-struct cpu_data {
- unsigned long long offset;
- unsigned long long size;
- unsigned long long timestamp;
- struct record *next;
- char *page;
- int cpu;
- int index;
- int page_size;
-};
-
-static struct cpu_data *cpu_data;
-
-static void update_cpu_data_index(int cpu)
-{
- cpu_data[cpu].offset += page_size;
- cpu_data[cpu].size -= page_size;
- cpu_data[cpu].index = 0;
-}
-
-static void get_next_page(int cpu)
-{
- off_t save_seek;
- off_t ret;
-
- if (!cpu_data[cpu].page)
- return;
-
- if (read_page) {
- if (cpu_data[cpu].size <= page_size) {
- free(cpu_data[cpu].page);
- cpu_data[cpu].page = NULL;
- return;
- }
-
- update_cpu_data_index(cpu);
-
- /* other parts of the code may expect the pointer to not move */
- save_seek = lseek(input_fd, 0, SEEK_CUR);
-
- ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET);
- if (ret == (off_t)-1)
- die("failed to lseek");
- ret = read(input_fd, cpu_data[cpu].page, page_size);
- if (ret < 0)
- die("failed to read page");
-
- /* reset the file pointer back */
- lseek(input_fd, save_seek, SEEK_SET);
-
- return;
- }
-
- munmap(cpu_data[cpu].page, page_size);
- cpu_data[cpu].page = NULL;
-
- if (cpu_data[cpu].size <= page_size)
- return;
-
- update_cpu_data_index(cpu);
-
- cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE,
- input_fd, cpu_data[cpu].offset);
- if (cpu_data[cpu].page == MAP_FAILED)
- die("failed to mmap cpu %d at offset 0x%llx",
- cpu, cpu_data[cpu].offset);
-}
-
-static unsigned int type_len4host(unsigned int type_len_ts)
-{
- if (file_bigendian)
- return (type_len_ts >> 27) & ((1 << 5) - 1);
- else
- return type_len_ts & ((1 << 5) - 1);
-}
-
-static unsigned int ts4host(unsigned int type_len_ts)
-{
- if (file_bigendian)
- return type_len_ts & ((1 << 27) - 1);
- else
- return type_len_ts >> 5;
-}
-
-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 record *data;
- void *page = cpu_data[cpu].page;
- int idx = cpu_data[cpu].index;
- void *ptr = page + idx;
- unsigned long long extend;
- unsigned int type_len_ts;
- unsigned int type_len;
- unsigned int delta;
- unsigned int length = 0;
-
- if (cpu_data[cpu].next)
- return cpu_data[cpu].next;
-
- if (!page)
- return NULL;
-
- if (!idx) {
- /* FIXME: handle header page */
- if (header_page_ts_size != 8)
- die("expected a long long type for timestamp");
- cpu_data[cpu].timestamp = data2host8(perf_pevent, ptr);
- ptr += 8;
- switch (header_page_size_size) {
- case 4:
- cpu_data[cpu].page_size = data2host4(perf_pevent, ptr);
- ptr += 4;
- break;
- case 8:
- cpu_data[cpu].page_size = data2host8(perf_pevent, ptr);
- ptr += 8;
- break;
- default:
- die("bad long size");
- }
- ptr = cpu_data[cpu].page + header_page_data_offset;
- }
-
-read_again:
- idx = calc_index(ptr, cpu);
-
- if (idx >= cpu_data[cpu].page_size) {
- get_next_page(cpu);
- return trace_peek_data(cpu);
- }
-
- type_len_ts = data2host4(perf_pevent, ptr);
- ptr += 4;
-
- type_len = type_len4host(type_len_ts);
- delta = ts4host(type_len_ts);
-
- switch (type_len) {
- case RINGBUF_TYPE_PADDING:
- if (!delta)
- die("error, hit unexpected end of page");
- length = data2host4(perf_pevent, ptr);
- ptr += 4;
- length *= 4;
- ptr += length;
- goto read_again;
-
- case RINGBUF_TYPE_TIME_EXTEND:
- extend = data2host4(perf_pevent, ptr);
- ptr += 4;
- extend <<= TS_SHIFT;
- extend += delta;
- cpu_data[cpu].timestamp += extend;
- goto read_again;
-
- case RINGBUF_TYPE_TIME_STAMP:
- ptr += 12;
- break;
- case 0:
- length = data2host4(perf_pevent, ptr);
- ptr += 4;
- die("here! length=%d", length);
- break;
- default:
- length = type_len * 4;
- break;
- }
-
- cpu_data[cpu].timestamp += delta;
-
- data = malloc_or_die(sizeof(*data));
- memset(data, 0, sizeof(*data));
-
- data->ts = cpu_data[cpu].timestamp;
- data->size = length;
- data->data = ptr;
- ptr += length;
-
- cpu_data[cpu].index = calc_index(ptr, cpu);
- cpu_data[cpu].next = data;
-
- return data;
-}
-
-struct record *trace_read_data(int cpu)
-{
- struct record *data;
-
- data = trace_peek_data(cpu);
- cpu_data[cpu].next = NULL;
-
- return data;
-}
-
-ssize_t trace_report(int fd, bool __repipe)
-{
- char buf[BUFSIZ];
- char test[] = { 23, 8, 68 };
- char *version;
- int show_version = 0;
- int show_funcs = 0;
- int show_printk = 0;
- ssize_t size;
-
- calc_data_size = 1;
- repipe = __repipe;
-
- input_fd = fd;
-
- read_or_die(buf, 3);
- if (memcmp(buf, test, 3) != 0)
- die("no trace data in the file");
-
- read_or_die(buf, 7);
- if (memcmp(buf, "tracing", 7) != 0)
- die("not a trace file (missing 'tracing' tag)");
-
- version = read_string();
- if (show_version)
- printf("version = %s\n", version);
- free(version);
-
- read_or_die(buf, 1);
- file_bigendian = buf[0];
- host_bigendian = bigendian();
-
- read_trace_init(file_bigendian, host_bigendian);
-
- read_or_die(buf, 1);
- long_size = buf[0];
-
- page_size = read4();
-
- read_header_files();
-
- read_ftrace_files();
- read_event_files();
- read_proc_kallsyms();
- read_ftrace_printk();
-
- size = calc_data_size - 1;
- calc_data_size = 0;
- repipe = false;
-
- if (show_funcs) {
- pevent_print_funcs(perf_pevent);
- return size;
- }
- if (show_printk) {
- pevent_print_printk(perf_pevent);
- return size;
- }
-
- return size;
-}
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index cc4f4d5..e97b15a 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -27,7 +27,7 @@
#include "../perf.h"
#include <lk/util.h>
-#include "trace-event.h"
+#include <trace/trace-event.h>
struct scripting_context *scripting_context;
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
deleted file mode 100644
index 6597c73..0000000
--- a/tools/perf/util/trace-event.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef _PERF_UTIL_TRACE_EVENT_H
-#define _PERF_UTIL_TRACE_EVENT_H
-
-#include "../../lib/trace/parse-events.h"
-#include "parse-events.h"
-
-extern int header_page_size_size;
-extern int header_page_ts_size;
-extern int header_page_data_offset;
-
-extern bool latency_format;
-extern struct pevent *perf_pevent;
-
-struct scripting_context {
- void *event_data;
-};
-
-enum {
- RINGBUF_TYPE_PADDING = 29,
- RINGBUF_TYPE_TIME_EXTEND = 30,
- RINGBUF_TYPE_TIME_STAMP = 31,
-};
-
-#ifndef TS_SHIFT
-#define TS_SHIFT 27
-#endif
-
-int bigendian(void);
-
-int read_trace_init(int file_bigendian, int host_bigendian);
-
-void print_event(int cpu, void *data, int size, unsigned long long nsecs,
- char *comm);
-
-struct record *trace_read_data(int cpu);
-int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
-ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
- int nb_events);
-
-int parse_ftrace_file(char *buf, unsigned long size);
-int parse_event_file(char *buf, unsigned long size, char *sys);
-
-struct record *trace_peek_data(int cpu);
-struct event_format *trace_find_event(int type);
-
-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);
-
-void parse_proc_kallsyms(char *file, unsigned int size __unused);
-void parse_ftrace_printk(char *file, unsigned int size __unused);
-
-ssize_t trace_report(int fd, bool repipe);
-
-struct scripting_ops {
- const char *name;
- int (*start_script) (const char *script, int argc, const char **argv);
- int (*stop_script) (void);
- void (*process_event) (int cpu, void *data, int size,
- unsigned long long nsecs, char *comm);
- int (*generate_script) (const char *outfile);
-};
-
-int script_spec_register(const char *spec, struct scripting_ops *ops);
-
-void setup_perl_scripting(void);
-void setup_python_scripting(void);
-
-int trace_parse_common_type(void *data);
-int trace_parse_common_pid(void *data);
-#if 0
-int parse_common_pc(void *data);
-int parse_common_flags(void *data);
-int parse_common_lock_depth(void *data);
-struct event_format *trace_find_event(int id);
-#endif
-struct event_format *trace_find_next_event(struct event_format *event);
-unsigned long long read_size(void *ptr, int size);
-#if 0
-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);
-#endif
-unsigned long long eval_flag(const char *flag);
-
-int common_pc(struct scripting_context *context);
-int common_flags(struct scripting_context *context);
-int common_lock_depth(struct scripting_context *context);
-
-#endif /* _PERF_UTIL_TRACE_EVENT_H */
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 18/20] Move string.c to the library
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (16 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 17/20] perf: Export trace parsing utils Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 19/20] perf, trace: Export event parsing helpers Borislav Petkov
` (2 subsequent siblings)
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
This one is generic enough so move it to lib/lk/string.c
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/Makefile | 1 +
tools/lib/lk/string.c | 296 +++++++++++++++++++++++++++++++++++++++++
tools/lib/lk/util.h | 2 +-
tools/perf/Makefile | 1 -
tools/perf/bench/mem-memcpy.c | 2 +-
tools/perf/util/string.c | 296 -----------------------------------------
6 files changed, 299 insertions(+), 299 deletions(-)
create mode 100644 tools/lib/lk/string.c
delete mode 100644 tools/perf/util/string.c
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index 36395b1..b81f11c 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -25,6 +25,7 @@ LIB_OBJS += rbtree.o
LIB_OBJS += hweight.o
LIB_OBJS += bitmap.o
LIB_OBJS += strlist.o
+LIB_OBJS += string.o
LIBFILE = $(LIB_OUTPUT)lklib.a
diff --git a/tools/lib/lk/string.c b/tools/lib/lk/string.c
new file mode 100644
index 0000000..0b02099
--- /dev/null
+++ b/tools/lib/lk/string.c
@@ -0,0 +1,296 @@
+#include <lk/util.h>
+#include "string.h"
+
+#define K 1024LL
+/*
+ * lk_atoll()
+ * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
+ * and return its numeric value
+ */
+s64 lk_atoll(const char *str)
+{
+ unsigned int i;
+ s64 length = -1, unit = 1;
+
+ if (!isdigit(str[0]))
+ goto out_err;
+
+ for (i = 1; i < strlen(str); i++) {
+ switch (str[i]) {
+ case 'B':
+ case 'b':
+ break;
+ case 'K':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto kilo;
+ case 'k':
+ if (str[i + 1] != 'b')
+ goto out_err;
+kilo:
+ unit = K;
+ break;
+ case 'M':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto mega;
+ case 'm':
+ if (str[i + 1] != 'b')
+ goto out_err;
+mega:
+ unit = K * K;
+ break;
+ case 'G':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto giga;
+ case 'g':
+ if (str[i + 1] != 'b')
+ goto out_err;
+giga:
+ unit = K * K * K;
+ break;
+ case 'T':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto tera;
+ case 't':
+ if (str[i + 1] != 'b')
+ goto out_err;
+tera:
+ unit = K * K * K * K;
+ break;
+ case '\0': /* only specified figures */
+ unit = 1;
+ break;
+ default:
+ if (!isdigit(str[i]))
+ goto out_err;
+ break;
+ }
+ }
+
+ length = atoll(str) * unit;
+ goto out;
+
+out_err:
+ length = -1;
+out:
+ return length;
+}
+
+/*
+ * Helper function for splitting a string into an argv-like array.
+ * originaly copied from lib/argv_split.c
+ */
+static const char *skip_sep(const char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+static const char *skip_arg(const char *cp)
+{
+ while (*cp && !isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+static int count_argc(const char *str)
+{
+ int count = 0;
+
+ while (*str) {
+ str = skip_sep(str);
+ if (*str) {
+ count++;
+ str = skip_arg(str);
+ }
+ }
+
+ return count;
+}
+
+/**
+ * argv_free - free an argv
+ * @argv - the argument vector to be freed
+ *
+ * Frees an argv and the strings it points to.
+ */
+void argv_free(char **argv)
+{
+ char **p;
+ for (p = argv; *p; p++)
+ free(*p);
+
+ free(argv);
+}
+
+/**
+ * argv_split - split a string at whitespace, returning an argv
+ * @str: the string to be split
+ * @argcp: returned argument count
+ *
+ * Returns an array of pointers to strings which are split out from
+ * @str. This is performed by strictly splitting on white-space; no
+ * quote processing is performed. Multiple whitespace characters are
+ * considered to be a single argument separator. The returned array
+ * is always NULL-terminated. Returns NULL on memory allocation
+ * failure.
+ */
+char **argv_split(const char *str, int *argcp)
+{
+ int argc = count_argc(str);
+ char **argv = zalloc(sizeof(*argv) * (argc+1));
+ char **argvp;
+
+ if (argv == NULL)
+ goto out;
+
+ if (argcp)
+ *argcp = argc;
+
+ argvp = argv;
+
+ while (*str) {
+ str = skip_sep(str);
+
+ if (*str) {
+ const char *p = str;
+ char *t;
+
+ str = skip_arg(str);
+
+ t = strndup(p, str-p);
+ if (t == NULL)
+ goto fail;
+ *argvp++ = t;
+ }
+ }
+ *argvp = NULL;
+
+out:
+ return argv;
+
+fail:
+ argv_free(argv);
+ return NULL;
+}
+
+/* Character class matching */
+static bool __match_charclass(const char *pat, char c, const char **npat)
+{
+ bool complement = false, ret = true;
+
+ if (*pat == '!') {
+ complement = true;
+ pat++;
+ }
+ if (*pat++ == c) /* First character is special */
+ goto end;
+
+ while (*pat && *pat != ']') { /* Matching */
+ if (*pat == '-' && *(pat + 1) != ']') { /* Range */
+ if (*(pat - 1) <= c && c <= *(pat + 1))
+ goto end;
+ if (*(pat - 1) > *(pat + 1))
+ goto error;
+ pat += 2;
+ } else if (*pat++ == c)
+ goto end;
+ }
+ if (!*pat)
+ goto error;
+ ret = false;
+
+end:
+ while (*pat && *pat != ']') /* Searching closing */
+ pat++;
+ if (!*pat)
+ goto error;
+ *npat = pat + 1;
+ return complement ? !ret : ret;
+
+error:
+ return false;
+}
+
+/* Glob/lazy pattern matching */
+static bool __match_glob(const char *str, const char *pat, bool ignore_space)
+{
+ while (*str && *pat && *pat != '*') {
+ if (ignore_space) {
+ /* Ignore spaces for lazy matching */
+ if (isspace(*str)) {
+ str++;
+ continue;
+ }
+ if (isspace(*pat)) {
+ pat++;
+ continue;
+ }
+ }
+ if (*pat == '?') { /* Matches any single character */
+ str++;
+ pat++;
+ continue;
+ } else if (*pat == '[') /* Character classes/Ranges */
+ if (__match_charclass(pat + 1, *str, &pat)) {
+ str++;
+ continue;
+ } else
+ return false;
+ else if (*pat == '\\') /* Escaped char match as normal char */
+ pat++;
+ if (*str++ != *pat++)
+ return false;
+ }
+ /* Check wild card */
+ if (*pat == '*') {
+ while (*pat == '*')
+ pat++;
+ if (!*pat) /* Tail wild card matches all */
+ return true;
+ while (*str)
+ if (strglobmatch(str++, pat))
+ return true;
+ }
+ return !*str && !*pat;
+}
+
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
+bool strglobmatch(const char *str, const char *pat)
+{
+ return __match_glob(str, pat, false);
+}
+
+/**
+ * strlazymatch - matching pattern strings lazily with glob pattern
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This is similar to strglobmatch, except this ignores spaces in
+ * the target string.
+ */
+bool strlazymatch(const char *str, const char *pat)
+{
+ return __match_glob(str, pat, true);
+}
diff --git a/tools/lib/lk/util.h b/tools/lib/lk/util.h
index 23f3e61..bb6ba42 100644
--- a/tools/lib/lk/util.h
+++ b/tools/lib/lk/util.h
@@ -261,7 +261,7 @@ static inline int sane_case(int x, int high)
int mkdir_p(char *path, mode_t mode);
int copyfile(const char *from, const char *to);
-s64 perf_atoll(const char *str);
+s64 lk_atoll(const char *str);
char **argv_split(const char *str, int *argcp);
void argv_free(char **argv);
bool strglobmatch(const char *str, const char *pat);
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index f899b08..96f7c65 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -387,7 +387,6 @@ LIB_OBJS += $(OUTPUT)util/parse-events.o
LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
-LIB_OBJS += $(OUTPUT)util/string.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/pager.o
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index 1b2f508..86db09e 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -105,7 +105,7 @@ int bench_mem_memcpy(int argc, const char **argv,
tv_diff.tv_sec = 0;
tv_diff.tv_usec = 0;
- length = (size_t)perf_atoll((char *)length_str);
+ length = (size_t)lk_atoll((char *)length_str);
if ((s64)length <= 0) {
fprintf(stderr, "Invalid length:%s\n", length_str);
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
deleted file mode 100644
index 881ef63..0000000
--- a/tools/perf/util/string.c
+++ /dev/null
@@ -1,296 +0,0 @@
-#include <lk/util.h>
-#include "string.h"
-
-#define K 1024LL
-/*
- * perf_atoll()
- * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
- * and return its numeric value
- */
-s64 perf_atoll(const char *str)
-{
- unsigned int i;
- s64 length = -1, unit = 1;
-
- if (!isdigit(str[0]))
- goto out_err;
-
- for (i = 1; i < strlen(str); i++) {
- switch (str[i]) {
- case 'B':
- case 'b':
- break;
- case 'K':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto kilo;
- case 'k':
- if (str[i + 1] != 'b')
- goto out_err;
-kilo:
- unit = K;
- break;
- case 'M':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto mega;
- case 'm':
- if (str[i + 1] != 'b')
- goto out_err;
-mega:
- unit = K * K;
- break;
- case 'G':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto giga;
- case 'g':
- if (str[i + 1] != 'b')
- goto out_err;
-giga:
- unit = K * K * K;
- break;
- case 'T':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto tera;
- case 't':
- if (str[i + 1] != 'b')
- goto out_err;
-tera:
- unit = K * K * K * K;
- break;
- case '\0': /* only specified figures */
- unit = 1;
- break;
- default:
- if (!isdigit(str[i]))
- goto out_err;
- break;
- }
- }
-
- length = atoll(str) * unit;
- goto out;
-
-out_err:
- length = -1;
-out:
- return length;
-}
-
-/*
- * Helper function for splitting a string into an argv-like array.
- * originaly copied from lib/argv_split.c
- */
-static const char *skip_sep(const char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
-
- return cp;
-}
-
-static const char *skip_arg(const char *cp)
-{
- while (*cp && !isspace(*cp))
- cp++;
-
- return cp;
-}
-
-static int count_argc(const char *str)
-{
- int count = 0;
-
- while (*str) {
- str = skip_sep(str);
- if (*str) {
- count++;
- str = skip_arg(str);
- }
- }
-
- return count;
-}
-
-/**
- * argv_free - free an argv
- * @argv - the argument vector to be freed
- *
- * Frees an argv and the strings it points to.
- */
-void argv_free(char **argv)
-{
- char **p;
- for (p = argv; *p; p++)
- free(*p);
-
- free(argv);
-}
-
-/**
- * argv_split - split a string at whitespace, returning an argv
- * @str: the string to be split
- * @argcp: returned argument count
- *
- * Returns an array of pointers to strings which are split out from
- * @str. This is performed by strictly splitting on white-space; no
- * quote processing is performed. Multiple whitespace characters are
- * considered to be a single argument separator. The returned array
- * is always NULL-terminated. Returns NULL on memory allocation
- * failure.
- */
-char **argv_split(const char *str, int *argcp)
-{
- int argc = count_argc(str);
- char **argv = zalloc(sizeof(*argv) * (argc+1));
- char **argvp;
-
- if (argv == NULL)
- goto out;
-
- if (argcp)
- *argcp = argc;
-
- argvp = argv;
-
- while (*str) {
- str = skip_sep(str);
-
- if (*str) {
- const char *p = str;
- char *t;
-
- str = skip_arg(str);
-
- t = strndup(p, str-p);
- if (t == NULL)
- goto fail;
- *argvp++ = t;
- }
- }
- *argvp = NULL;
-
-out:
- return argv;
-
-fail:
- argv_free(argv);
- return NULL;
-}
-
-/* Character class matching */
-static bool __match_charclass(const char *pat, char c, const char **npat)
-{
- bool complement = false, ret = true;
-
- if (*pat == '!') {
- complement = true;
- pat++;
- }
- if (*pat++ == c) /* First character is special */
- goto end;
-
- while (*pat && *pat != ']') { /* Matching */
- if (*pat == '-' && *(pat + 1) != ']') { /* Range */
- if (*(pat - 1) <= c && c <= *(pat + 1))
- goto end;
- if (*(pat - 1) > *(pat + 1))
- goto error;
- pat += 2;
- } else if (*pat++ == c)
- goto end;
- }
- if (!*pat)
- goto error;
- ret = false;
-
-end:
- while (*pat && *pat != ']') /* Searching closing */
- pat++;
- if (!*pat)
- goto error;
- *npat = pat + 1;
- return complement ? !ret : ret;
-
-error:
- return false;
-}
-
-/* Glob/lazy pattern matching */
-static bool __match_glob(const char *str, const char *pat, bool ignore_space)
-{
- while (*str && *pat && *pat != '*') {
- if (ignore_space) {
- /* Ignore spaces for lazy matching */
- if (isspace(*str)) {
- str++;
- continue;
- }
- if (isspace(*pat)) {
- pat++;
- continue;
- }
- }
- if (*pat == '?') { /* Matches any single character */
- str++;
- pat++;
- continue;
- } else if (*pat == '[') /* Character classes/Ranges */
- if (__match_charclass(pat + 1, *str, &pat)) {
- str++;
- continue;
- } else
- return false;
- else if (*pat == '\\') /* Escaped char match as normal char */
- pat++;
- if (*str++ != *pat++)
- return false;
- }
- /* Check wild card */
- if (*pat == '*') {
- while (*pat == '*')
- pat++;
- if (!*pat) /* Tail wild card matches all */
- return true;
- while (*str)
- if (strglobmatch(str++, pat))
- return true;
- }
- return !*str && !*pat;
-}
-
-/**
- * strglobmatch - glob expression pattern matching
- * @str: the target string to match
- * @pat: the pattern string to match
- *
- * This returns true if the @str matches @pat. @pat can includes wildcards
- * ('*','?') and character classes ([CHARS], complementation and ranges are
- * also supported). Also, this supports escape character ('\') to use special
- * characters as normal character.
- *
- * Note: if @pat syntax is broken, this always returns false.
- */
-bool strglobmatch(const char *str, const char *pat)
-{
- return __match_glob(str, pat, false);
-}
-
-/**
- * strlazymatch - matching pattern strings lazily with glob pattern
- * @str: the target string to match
- * @pat: the pattern string to match
- *
- * This is similar to strglobmatch, except this ignores spaces in
- * the target string.
- */
-bool strlazymatch(const char *str, const char *pat)
-{
- return __match_glob(str, pat, true);
-}
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 19/20] perf, trace: Export event parsing helpers
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (17 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 18/20] Move string.c to the library Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-04 15:36 ` [PATCH 20/20] ras: Add RAS daemon Borislav Petkov
2010-11-05 12:02 ` [RFC PATCH 00/20] RAS daemon v3 Mauro Carvalho Chehab
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Those are needed for the RAS daemon.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/trace/parse-events.c | 12 ++++++------
tools/lib/trace/parse-events.h | 5 +++++
tools/lib/trace/trace-event-info.c | 10 +++++-----
tools/lib/trace/trace-event.h | 2 ++
4 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/tools/lib/trace/parse-events.c b/tools/lib/trace/parse-events.c
index 1f95f87..02fc69b 100644
--- a/tools/lib/trace/parse-events.c
+++ b/tools/lib/trace/parse-events.c
@@ -45,7 +45,7 @@ static int show_warning = 1;
warning(fmt, ##__VA_ARGS__); \
} while (0)
-static void init_input_buf(const char *buf, unsigned long long size)
+void init_input_buf(const char *buf, unsigned long long size)
{
input_buf = buf;
input_buf_siz = size;
@@ -585,7 +585,7 @@ void pevent_print_printk(struct pevent *pevent)
}
}
-static struct event_format *alloc_event(void)
+struct event_format *alloc_event(void)
{
struct event_format *event;
@@ -1079,7 +1079,7 @@ 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 *event_read_name(void)
{
char *token;
@@ -1099,7 +1099,7 @@ static char *event_read_name(void)
return NULL;
}
-static int event_read_id(void)
+int event_read_id(void)
{
char *token;
int id;
@@ -1394,7 +1394,7 @@ fail_expect:
return -1;
}
-static int event_read_format(struct event_format *event)
+int event_read_format(struct event_format *event)
{
char *token;
int ret;
@@ -4287,7 +4287,7 @@ int pevent_parse_event(struct pevent *pevent,
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");
diff --git a/tools/lib/trace/parse-events.h b/tools/lib/trace/parse-events.h
index f967885..4d9400a 100644
--- a/tools/lib/trace/parse-events.h
+++ b/tools/lib/trace/parse-events.h
@@ -728,4 +728,9 @@ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source
int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2);
+extern struct event_format *alloc_event(void);
+extern void init_input_buf(const char *buf, unsigned long long size);
+extern char *event_read_name(void);
+extern int event_read_id(void);
+extern int event_read_format(struct event_format *event);
#endif /* _PARSE_EVENTS_H */
diff --git a/tools/lib/trace/trace-event-info.c b/tools/lib/trace/trace-event-info.c
index 98faa44..8188c59 100644
--- a/tools/lib/trace/trace-event-info.c
+++ b/tools/lib/trace/trace-event-info.c
@@ -136,7 +136,7 @@ static const char *find_tracing_dir(void)
return tracing;
}
-static char *get_tracing_file(const char *name)
+char *get_tracing_file(const char *name)
{
const char *tracing;
char *file;
@@ -231,7 +231,7 @@ static unsigned long get_size_fd(int fd)
return size;
}
-static unsigned long get_size(const char *file)
+unsigned long get_filesize(const char *file)
{
unsigned long long size = 0;
int fd;
@@ -340,7 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
if (ret >= 0) {
/* unfortunately, you can not stat debugfs files for size */
- size = get_size(format);
+ size = get_filesize(format);
write_or_die(&size, 8);
check_size = copy_file(format);
if (size != check_size)
@@ -438,7 +438,7 @@ static void read_proc_kallsyms(void)
write_or_die(&size, 4);
return;
}
- size = get_size(path);
+ size = get_filesize(path);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
@@ -461,7 +461,7 @@ static void read_ftrace_printk(void)
write_or_die(&size, 4);
goto out;
}
- size = get_size(path);
+ size = get_filesize(path);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
diff --git a/tools/lib/trace/trace-event.h b/tools/lib/trace/trace-event.h
index 9c9d342..c7e8e0b 100644
--- a/tools/lib/trace/trace-event.h
+++ b/tools/lib/trace/trace-event.h
@@ -76,6 +76,8 @@ struct event_format *trace_find_event(int id);
#endif
struct event_format *trace_find_next_event(struct event_format *event);
unsigned long long read_size(void *ptr, int size);
+unsigned long get_filesize(const char *file);
+char *get_tracing_file(const char *name);
#if 0
unsigned long long
raw_field_value(struct event_format *event, const char *name, void *data);
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH 20/20] ras: Add RAS daemon
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (18 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 19/20] perf, trace: Export event parsing helpers Borislav Petkov
@ 2010-11-04 15:36 ` Borislav Petkov
2010-11-05 12:02 ` [RFC PATCH 00/20] RAS daemon v3 Mauro Carvalho Chehab
20 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-04 15:36 UTC (permalink / raw)
To: acme, fweisbec, mingo, peterz, rostedt; +Cc: linux-kernel, Borislav Petkov
From: Borislav Petkov <borislav.petkov@amd.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 4 +
tools/ras/Makefile | 16 +++
tools/ras/rasd.c | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 325 insertions(+), 0 deletions(-)
create mode 100644 tools/ras/Makefile
create mode 100644 tools/ras/rasd.c
diff --git a/tools/Makefile b/tools/Makefile
index 691f78b..360454c 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -37,6 +37,9 @@ export PERF_TOP_DIR
perf: libparsevent lklib lkperflib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
+ras: libparsevent lklib lkperflib .FORCE
+ $(QUIET_SUBDIR0)ras/ $(QUIET_SUBDIR1)
+
libparsevent: .FORCE
$(QUIET_SUBDIR0)lib/trace/ $(QUIET_SUBDIR1)
@@ -51,5 +54,6 @@ clean:
$(QUIET_SUBDIR0)lib/lk/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)lib/perf/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)ras/ $(QUIET_SUBDIR1) clean
.PHONY: clean .FORCE
diff --git a/tools/ras/Makefile b/tools/ras/Makefile
new file mode 100644
index 0000000..370ae35
--- /dev/null
+++ b/tools/ras/Makefile
@@ -0,0 +1,16 @@
+include ../scripts/Makefile.lib
+
+CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 -DNO_NEWT_SUPPORT $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+ALL_LDFLAGS = $(LDFLAGS)
+
+RASLIBS=$(LIB_OUTPUT)libparsevent.a $(LIB_OUTPUT)lklib.a $(LIB_OUTPUT)lkperflib.a
+
+rasd: rasd.o
+ $(QUIET_CC)$(CC) $(ALL_CFLAGS) -o $@ $^ $(RASLIBS)
+
+%.o: %.c
+ $(QUIET_CC)$(CC) $(ALL_CFLAGS) -c $<
+
+clean:
+ rm -rf *.o rasd
diff --git a/tools/ras/rasd.c b/tools/ras/rasd.c
new file mode 100644
index 0000000..2b02742
--- /dev/null
+++ b/tools/ras/rasd.c
@@ -0,0 +1,305 @@
+/*
+ * Linux RAS daemon.
+ *
+ * Initial code reused from Linux Daemon Writing HOWTO
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <lk/util.h>
+#include <lk/cpumap.h>
+#include <lk/kernel.h>
+#include <lk/debugfs.h>
+#include <lk/compiler.h>
+#include <perf/mmap.h>
+#include <perf/util.h>
+#include <trace/trace-event.h>
+#include <trace/parse-events.h>
+
+#include "../../arch/x86/include/asm/mce.h"
+
+#define DBG(fmt, args...) fprintf(stderr, "DBG %s: " fmt "\n", __func__, ##args)
+
+#define MMAP_PAGES 128
+
+#define PFX "rasd: "
+
+static int fds[MAX_NR_CPUS];
+static struct mmap_data mmaps[MAX_NR_CPUS];
+static struct event_format *mce_event;
+static struct mce m;
+
+static int nr_cpus;
+static unsigned int page_size;
+static volatile int done = 0;
+
+static void sig_handler(int sig __used)
+{
+ done = 1;
+}
+
+static void write_output(void *buf, size_t size)
+{
+ while (size) {
+ int ret = write(STDOUT_FILENO, buf, size);
+
+ if (ret < 0)
+ die("failed to write");
+
+ size -= ret;
+ buf += ret;
+ }
+}
+
+static unsigned long long read_file(const char *file, void *buf)
+{
+ unsigned long long size = 0;
+ int fd, r;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ die("Can't read '%s'", file);
+
+ do {
+ r = read(fd, buf, BUFSIZ);
+ if (r > 0)
+ size += r;
+ } while (r > 0);
+
+ close(fd);
+
+ return size;
+}
+
+static void parse_mce_event(const char *event_name)
+{
+ struct stat st;
+ char *format_path, *format_buf, *path;
+ int fsize, err = 0;
+
+ path = get_tracing_file("events");
+
+ DBG("Got %s", path);
+
+ format_path = malloc_or_die(MAXPATHLEN + sizeof(event_name) + 10);
+ sprintf(format_path, "%s/%s/format", path, event_name);
+
+ err = stat(format_path, &st);
+ if (err < 0)
+ die("accessing %s", format_path);
+
+ DBG("Format access %s ok", format_path);
+
+ fsize = get_filesize(format_path);
+ DBG("Format file size: %d", fsize);
+
+ format_buf = malloc_or_die(fsize);
+ if (!format_buf)
+ die("allocating format buffer");
+
+ if (!read_file(format_path, format_buf))
+ die("reading in format file");
+
+ DBG("Format file contents:\n%s", format_buf);
+
+ init_input_buf(format_buf, fsize);
+
+ mce_event = alloc_event();
+ if (!mce_event)
+ die("Cannot alloc mce_event");
+
+ mce_event->name = event_read_name();
+ if (!mce_event->name)
+ error("no event name");
+
+ mce_event->id = event_read_id();
+ if (mce_event->id < 0)
+ error(PFX "failed to read event id");
+
+ if (event_read_format(mce_event))
+ die("parsing event");
+
+
+ free(format_buf);
+ free(format_path);
+ free(path);
+}
+
+static void fill_mce_data(void *vbuf, size_t buflen)
+{
+ struct format_field *field;
+ char *buf = vbuf;
+ u32 tp_len;
+ unsigned i;
+
+ if (!buflen)
+ return;
+
+ DBG("buflen %lu", buflen);
+
+ for (i = 0; i < buflen; i++) {
+
+ if (!(i & 0xf) && i)
+ printf("\n");
+
+ printf("0x%2.2x ", *(unsigned char *)(buf + i));
+ }
+
+ /* skip event header for now, u32 size inclusive */
+ buf += (sizeof(struct perf_event_header) + 4);
+ buflen -= (sizeof(struct perf_event_header) + 4);
+
+ tp_len = *(u32 *)buf;
+
+ if (tp_len != buflen)
+ warning("buffer size mismatch: %lu <-> %u (tp)\n", buflen, tp_len);
+
+ for (field = mce_event->format.fields; field; field = field->next) {
+ if ((size_t)(field->offset + field->size) > buflen)
+ warning("MCE buffer truncated? (off: %d <-> buflen: %lu)",
+ field->offset, buflen);
+
+ DBG("field %s, offset: %d", field->name, field->offset);
+
+ if (!strncmp(field->name, "bank", 4))
+ m.bank = *(u8 *)(buf + field->offset);
+ else if (!strncmp(field->name, "status", 6))
+ m.status = *(u64 *)(buf + field->offset);
+ else if (!strncmp(field->name, "addr", 4))
+ m.addr = *(u64 *)(buf + field->offset);
+ else if (!strncmp(field->name, "misc", 4))
+ m.misc = *(u64 *)(buf + field->offset);
+ else if (!strncmp(field->name, "ip", 2))
+ m.ip = *(u64 *)(buf + field->offset);
+ else if (!strncmp(field->name, "cs", 2))
+ m.cs = *(u8 *)(buf + field->offset);
+ else if (!strncmp(field->name, "tsc", 3))
+ m.tsc = *(u64 *)(buf + field->offset);
+ else if (!strncmp(field->name, "cpu", 3))
+ m.cpu = *(u8 *)(buf + field->offset);
+ else
+ DBG("skipping %s", field->name);
+ }
+}
+
+static int ras_init(void)
+{
+ const char *event_name = "mce/mce_record";
+ int cpu;
+
+
+ fprintf(stderr, PFX "Starting daemon.\n");
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ if (get_debugfs_mntpt()) {
+ error("Cannot mount debugfs, exiting...");
+ return 1;
+ }
+
+ nr_cpus = read_cpu_map(NULL);
+
+ parse_mce_event(event_name);
+ assert(event_name);
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ char dfs_path[MAXPATHLEN];
+
+ snprintf(dfs_path, MAXPATHLEN, "%s/%s%d",
+ debugfs_mntpt, event_name, cpu);
+
+ DBG("dfs_path: %s", dfs_path);
+
+ fds[cpu] = open(dfs_path, O_RDONLY, O_NONBLOCK);
+ if (fds[cpu] < 0) {
+ error("open perf event on cpu %d\n", cpu);
+ return 1;
+ } else
+ DBG("cpu %d, fd %d", cpu, fds[cpu]);
+ }
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ mmaps[cpu].prev = 0;
+ mmaps[cpu].mask = MMAP_PAGES*page_size - 1;
+ mmaps[cpu].base = mmap(NULL, (MMAP_PAGES + 1) * page_size,
+ PROT_READ, MAP_SHARED, fds[cpu], 0);
+
+ if (mmaps[cpu].base == MAP_FAILED) {
+ error("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void ras_exit(void)
+{
+ free(mce_event);
+}
+
+int main(void)
+{
+ pid_t pid, sid;
+
+ pid = fork();
+ if (pid < 0) {
+ perror(PFX "Error forking daemon thread");
+ exit(EXIT_FAILURE);
+ }
+
+ /* parent can disappear now */
+ if (pid > 0)
+ exit(EXIT_SUCCESS);
+
+ umask(0);
+
+ /* TODO: open system logs */
+
+ sid = setsid();
+ if (sid < 0) {
+ perror(PFX "Error creating session");
+ exit(EXIT_FAILURE);
+ }
+
+ if (chdir("/") < 0) {
+ perror(PFX "Error chdir to /");
+ exit(EXIT_FAILURE);
+ }
+
+ close(STDIN_FILENO);
+/* close(STDOUT_FILENO); */
+/* close(STDERR_FILENO); */
+
+ if (ras_init())
+ exit(EXIT_FAILURE);
+
+ signal(SIGCHLD, sig_handler);
+ signal(SIGINT, sig_handler);
+
+ while(1) {
+
+ if (mmap_read_all(mmaps, nr_cpus, fill_mce_data)) {
+ DBG("Read some mmapped data");
+ DBG("MCE status: 0x%016llx", m.status);
+ }
+
+ if (done)
+ goto out;
+
+ sleep(30);
+ }
+
+out:
+ ras_exit();
+ return 0;
+
+}
--
1.7.3.1
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [RFC PATCH 00/20] RAS daemon v3
2010-11-04 15:36 [RFC PATCH 00/20] RAS daemon v3 Borislav Petkov
` (19 preceding siblings ...)
2010-11-04 15:36 ` [PATCH 20/20] ras: Add RAS daemon Borislav Petkov
@ 2010-11-05 12:02 ` Mauro Carvalho Chehab
2010-11-05 13:46 ` Borislav Petkov
20 siblings, 1 reply; 31+ messages in thread
From: Mauro Carvalho Chehab @ 2010-11-05 12:02 UTC (permalink / raw)
To: Borislav Petkov
Cc: acme, fweisbec, mingo, peterz, rostedt, linux-kernel,
Borislav Petkov
Hi Boris,
Em 04-11-2010 11:36, Borislav Petkov escreveu:
> From: Borislav Petkov <borislav.petkov@amd.com>
>
> Hi all,
>
> I finally had some time to work on this thing again. This time it can
> parse the MCE tracepoint and should be conceptually almost done. What
> needs to be done now is fleshing out a bunch of details here and there.
> I'm sending it early so that I can collect some more feedback.
>
> So the patchset is ontop of 2.6.36 + Steven's trace_cmd restructuring
> set from
>
> git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git tip/perf/parse-events
>
> I'm adding his patches too here, for completeness (although they need
> some more work).
I tried to apply your patches here, but they didn't apply. i suspect that Steven
added some patches there at the meantime, as two patches on your series are already
on his tree. IMO, the better would be if you could create a temporary tree or branch
to allow us to better view it.
> I've also cherry-picked the bunch of EDAC's MCE injection stuff for
> testing.
>
> So, in the end of the day, if you do
>
> echo 0x9c00410000010016 > /sys/devices/system/edac/mce/status
>
> (0x9c.. is the MCE signature of a data cache L2 TLB multimatch, for
> example)
This example looks quite ugly to me. I doubt anyone without a datasheet and after a
very careful inspection would know what 0x9c00410000010016 magic number means. I
suspect that writing a wrong magic number will also produce a completely undesired
result. So, the better it to keep the MCE code internally to the driver.
Also, writing a magic number to a node named as "status" seems weird to me.
IMO, instead, it should be something like:
echo 1 >/sys/devices/system/edac/mce/error_inject
>
> echo 0 > /sys/devices/system/edac/mce/bank
>
> (0 means bank 0, i.e. data cache errors)
>
> after having loaded the mce_amd_inj injection testing module, the RAS
> daemon get's the status signature in userspace:
>
> ...
> DBG main: Read some mmapped data
> DBG main: MCE status: 0x9c00410000010016
>
> All of the remaining fields can be postprocessed in arbitrary manner
> after that. The MCE decoding in the kernel can then be simplified by
> sharing it with the daemon, if needed. But that's another story.
>
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [RFC PATCH 00/20] RAS daemon v3
2010-11-05 12:02 ` [RFC PATCH 00/20] RAS daemon v3 Mauro Carvalho Chehab
@ 2010-11-05 13:46 ` Borislav Petkov
0 siblings, 0 replies; 31+ messages in thread
From: Borislav Petkov @ 2010-11-05 13:46 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: acme@infradead.org, fweisbec@gmail.com, mingo@elte.hu,
peterz@infradead.org, rostedt@goodmis.org,
linux-kernel@vger.kernel.org
On Fri, Nov 05, 2010 at 08:02:34AM -0400, Mauro Carvalho Chehab wrote:
> I tried to apply your patches here, but they didn't apply. i suspect
> that Steven added some patches there at the meantime, as two patches
> on your series are already on his tree. IMO, the better would be if
> you could create a temporary tree or branch to allow us to better view
> it.
Sure:
git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git ras-v3
> This example looks quite ugly to me. I doubt anyone without a
> datasheet and after a very careful inspection would know what
> 0x9c00410000010016 magic number means.
Right, this was only a hands-on example of what otherwise a script does.
I wanted to show what happens in detail.
> I suspect that writing a wrong magic number will also produce a
> completely undesired result.
That's not a problem since this is software-only injection. It actually
makes sense to be able to inject crap so that you can test the decoding
code:
[81953.494078] [Hardware Error]: MC5_STATUS: Uncorrected error, other errors lost: no, CPU context corrupt: yes, UECC Error
[81953.505714] [Hardware Error]: Corrupted FR MCE info?
[81953.505718] [Hardware Error]: Transaction: GEN (GEN), no timeout, Cache Level: L3/GEN, Participating Processor: GEN
> So, the better it to keep the MCE code
> internally to the driver.
>
> Also, writing a magic number to a node named as "status" seems weird to me.
>
> IMO, instead, it should be something like:
>
> echo 1 >/sys/devices/system/edac/mce/error_inject
Well, this way you inject a random error. But you want to control the
error types which you inject and set not only one but a couple of the
MCi_ bank MSRs. In that manner, you can inject the address at which a
certain MCE happens and so on.
So, basically, the long term goal is to have a tool which could do all
that. Maybe something like this:
perf inject --mce --functional-unit DC --uncorrectable --pcc-corrupt --virtual-address 0xdeadbeef ...
or
perf inject --mce --functional-unit IC --random --correctable --ecc
(I have long options so that it's clear what we do - we can make them
shorter in the actual case.) But you get the idea. This way, you can
inject all kinds of stuff and also in a human-readable form.
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 31+ messages in thread