* [PATCH 01/21] perf_events: Export buffer handling functions
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 02/21] mce: Add persistent events Borislav Petkov
` (19 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
... for other in-kernel users.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
include/linux/perf_event.h | 8 ++++++++
kernel/perf_event.c | 8 ++++----
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 63b5aa5..cb242bd 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1017,6 +1017,10 @@ 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);
+
#else
static inline void
perf_event_task_sched_in(struct task_struct *task) { }
@@ -1054,6 +1058,10 @@ 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) { }
+extern 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) {}
+
#endif
#define perf_output_put(handle, x) \
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index c772a3d..40478af 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -1881,7 +1881,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)
{
@@ -2424,7 +2424,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;
@@ -2541,7 +2541,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;
@@ -2642,7 +2642,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;
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 02/21] mce: Add persistent events
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
2010-07-01 15:55 ` [PATCH 01/21] perf_events: Export buffer handling functions Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 03/21] perf_events: Add a helper to search for an event in a context Borislav Petkov
` (18 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Add the required glue to enable the mce_record tracepoint on boot thus
simulating a persistent event with allocated buffers. Userspace daemon
will hook into it later when booting is done.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
arch/x86/kernel/cpu/mcheck/mce.c | 89 ++++++++++++++++++++++++++++++++++++++
1 files changed, 89 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 18cc425..42d2808 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -95,6 +95,7 @@ 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 perf_event *, mce_event);
static int cpu_missing;
/*
@@ -2063,6 +2064,91 @@ 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 | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME,
+};
+
+static int mcheck_enable_perf_event_on_cpu(int cpu)
+{
+ struct perf_event *event;
+ struct perf_buffer *buffer;
+
+ if (!event_mce_record.event.type) {
+ printk(KERN_ERR "mce: Tracepoint not enumerated yet!\n");
+ return -EINVAL;
+ }
+ pattr.config = event_mce_record.event.type;
+ pattr.sample_period = ULLONG_MAX;
+
+ event = perf_event_create_kernel_counter(&pattr, cpu, -1, NULL);
+ if (IS_ERR(event))
+ return -EINVAL;
+
+ buffer = perf_buffer_alloc(128, 0, cpu, 0);
+ if (IS_ERR(buffer))
+ goto err;
+
+ rcu_assign_pointer(event->buffer, buffer);
+ per_cpu(mce_event, cpu) = event;
+
+ perf_event_enable(event);
+
+ return 0;
+
+err:
+ perf_event_release_kernel(event);
+ return -EINVAL;
+}
+
+static void mcheck_disable_perf_event_on_cpu(int cpu)
+{
+ struct perf_event *event = per_cpu(mce_event, cpu);
+
+ if (!event)
+ return;
+
+ perf_event_disable(event);
+
+ if (event->buffer) {
+ perf_buffer_put(event->buffer);
+ rcu_assign_pointer(event->buffer, NULL);
+ }
+
+ per_cpu(mce_event, cpu) = NULL;
+
+ perf_event_release_kernel(event);
+}
+
+static int mcheck_init_perf_event(void)
+{
+ int cpu, i, err = 0;
+
+ get_online_cpus();
+
+ for_each_online_cpu(cpu) {
+ err = mcheck_enable_perf_event_on_cpu(cpu);
+ if (err) {
+ printk(KERN_ERR "mce: error initializing mce tracepoint"
+ " on cpu %d\n", cpu);
+
+ for (i = cpu - 1; i >= 0; i--)
+ mcheck_disable_perf_event_on_cpu(i);
+ }
+ }
+
+ put_online_cpus();
+
+ return err;
+}
+
+/*
+ * This has to run after after event_trace_init() which is an fs_initcall()
+ * currently
+ */
+device_initcall(mcheck_init_perf_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)
@@ -2076,6 +2162,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);
+ mcheck_enable_perf_event_on_cpu(cpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
@@ -2087,6 +2174,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
case CPU_DOWN_PREPARE_FROZEN:
del_timer_sync(t);
smp_call_function_single(cpu, mce_disable_cpu, &action, 1);
+ mcheck_disable_perf_event_on_cpu(cpu);
break;
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN:
@@ -2096,6 +2184,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
add_timer_on(t, cpu);
}
smp_call_function_single(cpu, mce_reenable_cpu, &action, 1);
+ mcheck_enable_perf_event_on_cpu(cpu);
break;
case CPU_POST_DEAD:
/* intentionally ignoring frozen here */
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 03/21] perf_events: Add a helper to search for an event in a context
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
2010-07-01 15:55 ` [PATCH 01/21] perf_events: Export buffer handling functions Borislav Petkov
2010-07-01 15:55 ` [PATCH 02/21] mce: Add persistent events Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 04/21] perf_events: Handle persistent events accordingly Borislav Petkov
` (17 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
This is a straignt-forward helper to look for, e.g. already allocated
and enabled events (think persistent events here).
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
kernel/perf_event.c | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 40478af..168764b 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -307,6 +307,27 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx)
ctx->nr_stat++;
}
+/*
+ * Find an event in a context.
+ * Must be called with ctx->mutex and ctx->lock held.
+ */
+static struct perf_event *find_event_in_context(struct perf_event_attr *attr,
+ struct perf_event_context *ctx)
+{
+ struct perf_event *event;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (event->attr.config == attr->config) {
+ rcu_read_unlock();
+ return event;
+ }
+ }
+ rcu_read_unlock();
+
+ return NULL;
+}
+
static void perf_group_attach(struct perf_event *event)
{
struct perf_event *group_leader = event->group_leader;
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 04/21] perf_events: Handle persistent events accordingly
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (2 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 03/21] perf_events: Add a helper to search for an event in a context Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 05/21] perf: rewire generic library stuff, p1 Borislav Petkov
` (16 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Add a persistent events flag which is passed to sys_perf_event_open().
Also, teach the perf syscall to differentiate between previously
allocated persistent events and new ones which are about to be created.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
include/linux/perf_event.h | 5 +++--
kernel/perf_event.c | 28 +++++++++++++++++++++++++---
2 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index cb242bd..1d7907a 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -452,8 +452,9 @@ enum perf_callchain_context {
PERF_CONTEXT_MAX = (__u64)-4095,
};
-#define PERF_FLAG_FD_NO_GROUP (1U << 0)
-#define PERF_FLAG_FD_OUTPUT (1U << 1)
+#define PERF_FLAG_FD_NO_GROUP (1U << 0)
+#define PERF_FLAG_FD_OUTPUT (1U << 1)
+#define PERF_FLAG_EVENT_PERSISTENT (1U << 2)
#ifdef __KERNEL__
/*
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 168764b..8b41a4e 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -5107,7 +5107,9 @@ SYSCALL_DEFINE5(perf_event_open,
int err;
/* for future expandability... */
- if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
+ if (flags & ~(PERF_FLAG_FD_NO_GROUP |
+ PERF_FLAG_FD_OUTPUT |
+ PERF_FLAG_EVENT_PERSISTENT))
return -EINVAL;
err = perf_copy_attr(attr_uptr, &attr);
@@ -5175,8 +5177,23 @@ SYSCALL_DEFINE5(perf_event_open,
goto err_put_context;
}
- event = perf_event_alloc(&attr, cpu, ctx, group_leader,
- NULL, NULL, GFP_KERNEL);
+ if (flags & PERF_FLAG_EVENT_PERSISTENT) {
+ mutex_lock(&ctx->mutex);
+ raw_spin_lock(&ctx->lock);
+
+ event = find_event_in_context(&attr, ctx);
+
+ raw_spin_unlock(&ctx->lock);
+ mutex_unlock(&ctx->mutex);
+
+ if (!event) {
+ printk(KERN_WARNING "Hmm, uninitialized persistent event?!");
+ return -EINVAL;
+ }
+ } else
+ 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;
@@ -5195,6 +5212,10 @@ SYSCALL_DEFINE5(perf_event_open,
}
event->filp = event_file;
+
+ if (flags & PERF_FLAG_EVENT_PERSISTENT)
+ goto out_drop_ref;
+
WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex);
perf_install_in_context(ctx, event, cpu);
@@ -5207,6 +5228,7 @@ SYSCALL_DEFINE5(perf_event_open,
list_add_tail(&event->owner_entry, ¤t->perf_event_list);
mutex_unlock(¤t->perf_event_mutex);
+out_drop_ref:
/*
* Drop the reference on the group_event after placing the
* new event on the sibling_list. This ensures destruction
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 05/21] perf: rewire generic library stuff, p1
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (3 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 04/21] perf_events: Handle persistent events accordingly Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 06/21] perf: rewire generic library stuff, p2 Borislav Petkov
` (15 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out generic library stuff into tools/lib/lk/ and rewire it with
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.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 58 ++++++++++++++++++++++
tools/Makefile.lib | 28 +++++++++++
tools/lib/Makefile | 38 ++++++++++++++
tools/lib/lk/bitmap.c | 21 ++++++++
tools/lib/lk/cpumap.c | 114 +++++++++++++++++++++++++++++++++++++++++++
tools/lib/lk/cpumap.h | 7 +++
tools/lib/lk/hweight.c | 31 ++++++++++++
tools/perf/Makefile | 41 ++--------------
tools/perf/bench/bench.h | 2 +
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-stat.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/util/bitmap.c | 21 --------
tools/perf/util/cache.h | 1 +
tools/perf/util/cpumap.c | 114 -------------------------------------------
tools/perf/util/cpumap.h | 7 ---
tools/perf/util/hweight.c | 31 ------------
17 files changed, 308 insertions(+), 212 deletions(-)
create mode 100644 tools/Makefile
create mode 100644 tools/Makefile.lib
create mode 100644 tools/lib/Makefile
create mode 100644 tools/lib/lk/bitmap.c
create mode 100644 tools/lib/lk/cpumap.c
create mode 100644 tools/lib/lk/cpumap.h
create mode 100644 tools/lib/lk/hweight.c
delete mode 100644 tools/perf/util/bitmap.c
delete mode 100644 tools/perf/util/cpumap.c
delete mode 100644 tools/perf/util/cpumap.h
delete mode 100644 tools/perf/util/hweight.c
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..e645761
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,58 @@
+# CFLAGS and LDFLAGS are for the users to override from the command line.
+#
+
+include 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
+
+ifeq ("$(origin DEBUG)", "command line")
+ PERF_DEBUG = $(DEBUG)
+endif
+ifndef PERF_DEBUG
+ CFLAGS_OPTIMIZE = -O6
+ export CFLAGS_OPTIMIZE
+endif
+
+#
+# lib includes for submake
+BASIC_CFLAGS = -I$(CURDIR)/lib -I$(CURDIR)/perf -I$(CURDIR)/perf/util/include
+
+export BASIC_CFLAGS
+
+perf: lib .FORCE
+ $(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
+
+lib: .FORCE
+ $(QUIET_SUBDIR0)lib/ $(QUIET_SUBDIR1)
+
+clean:
+ $(QUIET_SUBDIR0)lib/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
+
+.PHONY: clean .FORCE
diff --git a/tools/Makefile.lib b/tools/Makefile.lib
new file mode 100644
index 0000000..aac6be9
--- /dev/null
+++ b/tools/Makefile.lib
@@ -0,0 +1,28 @@
+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)/
+endif
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
new file mode 100644
index 0000000..48f8e21
--- /dev/null
+++ b/tools/lib/Makefile
@@ -0,0 +1,38 @@
+include ../Makefile.lib
+
+# Guard against environment variables
+LIB_H =
+LIB_OBJS =
+
+LIB_H += lk/cpumap.h
+
+LIB_OBJS += $(OUTPUT)lk/bitmap.o
+LIB_OBJS += $(OUTPUT)lk/cpumap.o
+LIB_OBJS += $(OUTPUT)lk/hweight.o
+
+LIBFILE = lklib.a
+
+RM = rm -f
+
+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)
+STRIP ?= strip
+
+$(LIBFILE): $(LIB_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+
+$(LIB_OBJS): $(LIB_H)
+
+$(OUTPUT)%.o: %.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.s: %.c
+ $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+$(OUTPUT)%.o: %.S
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
+clean:
+ $(RM) $(LIB_OBJS)
+
+.PHONY: clean
diff --git a/tools/lib/lk/bitmap.c b/tools/lib/lk/bitmap.c
new file mode 100644
index 0000000..5e230ac
--- /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 <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/lib/lk/cpumap.c b/tools/lib/lk/cpumap.c
new file mode 100644
index 0000000..71e376b
--- /dev/null
+++ b/tools/lib/lk/cpumap.c
@@ -0,0 +1,114 @@
+#include <util/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/hweight.c b/tools/lib/lk/hweight.c
new file mode 100644
index 0000000..5c1d0d0
--- /dev/null
+++ b/tools/lib/lk/hweight.c
@@ -0,0 +1,31 @@
+#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/Makefile b/tools/perf/Makefile
index 6aa2fe3..df815d6 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -1,6 +1,4 @@
-ifeq ("$(origin O)", "command line")
- OUTPUT := $(O)/
-endif
+include ../Makefile.lib
# The default target of this Makefile is...
all::
@@ -191,33 +189,6 @@ $(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null)
# 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
@@ -297,7 +268,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 += -Iutil/include -Iarch/$(ARCH)/include
BASIC_LDFLAGS =
# Guard against environment variables
@@ -413,7 +384,6 @@ 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
@@ -430,8 +400,6 @@ 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
LIB_OBJS += $(OUTPUT)util/quote.o
LIB_OBJS += $(OUTPUT)util/strbuf.o
@@ -460,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
@@ -490,7 +457,9 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
-PERFLIBS = $(LIB_FILE)
+LKLIB = ../lib/lklib.a
+
+PERFLIBS = $(LIB_FILE) $(LKLIB)
#
# 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/builtin-record.c b/tools/perf/builtin-record.c
index 86b1c3b..499dc75 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 a6b4d44..4430d51 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 1e8e92e..def254a 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/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/cache.h b/tools/perf/util/cache.h
index 27e9ebe..0444fc2 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -5,6 +5,7 @@
#include "util.h"
#include "strbuf.h"
#include "../perf.h"
+#include <linux/compiler.h>
#define CMD_EXEC_PATH "--exec-path"
#define CMD_PERF_DIR "--perf-dir="
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
deleted file mode 100644
index 0f9b8d7..0000000
--- a/tools/perf/util/cpumap.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "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/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
-}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 06/21] perf: rewire generic library stuff, p2
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (4 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 05/21] perf: rewire generic library stuff, p1 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 07/21] perf: rewire generic library stuff, p3 Borislav Petkov
` (14 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out generic library stuff into tools/lib/lk/ and rewire it with
perf. Relocate bits from perf includes into generic include files to
satisfy build dependencies.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 6 +
tools/lib/lk/cpumap.c | 2 +-
tools/lib/lk/ctype.c | 39 +++
tools/lib/lk/debugfs.c | 239 ++++++++++++++++
tools/lib/lk/debugfs.h | 27 ++
tools/lib/lk/types.h | 17 ++
tools/lib/lk/util.c | 116 ++++++++
tools/lib/lk/util.h | 285 ++++++++++++++++++++
tools/perf/Makefile | 6 -
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 | 4 +-
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 | 2 +-
tools/perf/perf.h | 2 +-
tools/perf/util/build-id.c | 2 +-
tools/perf/util/cache.h | 3 +-
tools/perf/util/callchain.c | 2 +-
tools/perf/util/config.c | 2 +-
tools/perf/util/ctype.c | 39 ---
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 | 4 +-
tools/perf/util/parse-options.c | 2 +-
tools/perf/util/probe-event.c | 4 +-
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 | 2 +-
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 | 2 +-
tools/perf/util/util.c | 116 --------
tools/perf/util/util.h | 285 --------------------
tools/perf/util/values.c | 2 +-
tools/perf/util/values.h | 2 +-
65 files changed, 783 insertions(+), 783 deletions(-)
create mode 100644 tools/lib/lk/ctype.c
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/util.c
create mode 100644 tools/lib/lk/util.h
delete mode 100644 tools/perf/util/ctype.c
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/util.c
delete mode 100644 tools/perf/util/util.h
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 48f8e21..7ebcf8c 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -5,10 +5,16 @@ LIB_H =
LIB_OBJS =
LIB_H += lk/cpumap.h
+LIB_H += lk/debugfs.h
+LIB_H += lk/util.h
+LIB_H += lk/types.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
+LIB_OBJS += $(OUTPUT)lk/ctype.o
+LIB_OBJS += $(OUTPUT)lk/debugfs.o
LIB_OBJS += $(OUTPUT)lk/hweight.o
+LIB_OBJS += $(OUTPUT)lk/util.o
LIBFILE = lklib.a
diff --git a/tools/lib/lk/cpumap.c b/tools/lib/lk/cpumap.c
index 71e376b..22626eb 100644
--- a/tools/lib/lk/cpumap.c
+++ b/tools/lib/lk/cpumap.c
@@ -1,4 +1,4 @@
-#include <util/util.h>
+#include "util.h"
#include <perf.h>
#include "cpumap.h"
#include <assert.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/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c
new file mode 100644
index 0000000..bbe3a83
--- /dev/null
+++ b/tools/lib/lk/debugfs.c
@@ -0,0 +1,239 @@
+#include "util.h"
+#include "debugfs.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/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h
new file mode 100644
index 0000000..8c02e72
--- /dev/null
+++ b/tools/lib/lk/debugfs.h
@@ -0,0 +1,27 @@
+#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
+
+#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
+
+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__ */
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/util.c b/tools/lib/lk/util.c
new file mode 100644
index 0000000..2142656
--- /dev/null
+++ b/tools/lib/lk/util.c
@@ -0,0 +1,116 @@
+#include "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/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 df815d6..e224788 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -356,16 +356,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
@@ -389,8 +386,6 @@ 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/debugfs.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
@@ -427,7 +422,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/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 fd20670..03f80e9 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 39e6627..2f5a711 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 31f60a2..68a6ec0 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 821c158..bafb8c5 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 5455186..be24b54 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -34,11 +34,11 @@
#undef _GNU_SOURCE
#include "perf.h"
#include "builtin.h"
-#include "util/util.h"
+#include <lk/util.h>
#include "util/strlist.h"
#include "util/symbol.h"
#include "util/debug.h"
-#include "util/debugfs.h"
+#include <lk/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 499dc75..b92e009 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 371a3c9..4be3c67 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 55f3b5d..c47cf08 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 4430d51..1f60239 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 5a52ed9..92f7230 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 def254a..9813351 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 dddf3f0..3f9308a 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1,6 +1,6 @@
#include "builtin.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.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..2fda133 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -13,7 +13,7 @@
#include "util/quote.h"
#include "util/run-command.h"
#include "util/parse-events.h"
-#include "util/debugfs.h"
+#include <lk/debugfs.h>
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index ef7aa0a..2344078 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -79,7 +79,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 5c26e2d..5de09e2 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 0444fc2..ce83e82 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -2,7 +2,7 @@
#define __PERF_CACHE_H
#include <stdbool.h>
-#include "util.h"
+#include <lk/util.h>
#include "strbuf.h"
#include "../perf.h"
#include <linux/compiler.h>
@@ -16,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 e63c997..a6c99a3 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/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 =
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------";
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 318dab1..96cb72a 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 68d288c..a409e27 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 f391345..c7ed844 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 9bf0f40..3a76c8e 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,5 +1,5 @@
#include "../../../include/linux/hw_breakpoint.h"
-#include "util.h"
+#include <lk/util.h>
#include "../perf.h"
#include "parse-options.h"
#include "parse-events.h"
@@ -8,7 +8,7 @@
#include "symbol.h"
#include "cache.h"
#include "header.h"
-#include "debugfs.h"
+#include <lk/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 914c670..723b10d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -33,7 +33,7 @@
#include <limits.h>
#undef _GNU_SOURCE
-#include "util.h"
+#include <lk/util.h>
#include "event.h"
#include "string.h"
#include "strlist.h"
@@ -42,7 +42,7 @@
#include "color.h"
#include "symbol.h"
#include "thread.h"
-#include "debugfs.h"
+#include <lk/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 baf6653..eb4eb26 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -36,7 +36,7 @@
#include "string.h"
#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 e1f61dc..470611b 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 33a6325..9ddc198 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 0564a5c..638ecd0 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 560c855..8ead949 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 1f7ecd4..deb30a8 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"
int find_all_tid(int pid, pid_t ** all_tid)
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index b157260..0918a63 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -38,7 +38,7 @@
#include "../perf.h"
#include "trace-event.h"
-#include "debugfs.h"
+#include <lk/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 73a0222..9a46fa1 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -30,7 +30,7 @@
#undef _GNU_SOURCE
#include "../perf.h"
-#include "util.h"
+#include <lk/util.h>
#include "trace-event.h"
int header_page_ts_offset;
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index f55cc3a..2583227 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 7ea983a..dae67df 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
index e16bf9a..d438743 100644
--- a/tools/perf/util/usage.c
+++ b/tools/perf/util/usage.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "util.h"
+#include <lk/util.h>
static void report(const char *prefix, const char *err, va_list params)
{
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
deleted file mode 100644
index 2142656..0000000
--- a/tools/perf/util/util.c
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "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/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.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 07/21] perf: rewire generic library stuff, p3
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (5 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 06/21] perf: rewire generic library stuff, p2 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 08/21] perf: rewire generic library stuff, p4 Borislav Petkov
` (13 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out generic library stuff into tools/lib/lk/ and rewire it with
perf. Relocate more generic stuff into <lk/util.h>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 5 ++
tools/lib/lk/pstack.c | 75 ++++++++++++++++++++++++++
tools/lib/lk/pstack.h | 12 ++++
tools/lib/lk/strbuf.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/lk/strbuf.h | 99 ++++++++++++++++++++++++++++++++++
tools/lib/lk/usage.c | 80 +++++++++++++++++++++++++++
tools/lib/lk/util.h | 21 +++++++-
tools/perf/Makefile | 5 --
tools/perf/builtin.h | 2 +-
tools/perf/util/cache.h | 23 +--------
tools/perf/util/pstack.c | 75 --------------------------
tools/perf/util/pstack.h | 12 ----
tools/perf/util/strbuf.c | 133 ----------------------------------------------
tools/perf/util/strbuf.h | 92 --------------------------------
tools/perf/util/usage.c | 80 ---------------------------
15 files changed, 426 insertions(+), 421 deletions(-)
create mode 100644 tools/lib/lk/pstack.c
create mode 100644 tools/lib/lk/pstack.h
create mode 100644 tools/lib/lk/strbuf.c
create mode 100644 tools/lib/lk/strbuf.h
create mode 100644 tools/lib/lk/usage.c
delete mode 100644 tools/perf/util/pstack.c
delete mode 100644 tools/perf/util/pstack.h
delete mode 100644 tools/perf/util/strbuf.c
delete mode 100644 tools/perf/util/strbuf.h
delete mode 100644 tools/perf/util/usage.c
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 7ebcf8c..64c0dbd 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -8,6 +8,8 @@ LIB_H += lk/cpumap.h
LIB_H += lk/debugfs.h
LIB_H += lk/util.h
LIB_H += lk/types.h
+LIB_H += lk/pstack.h
+LIB_H += lk/strbuf.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -15,6 +17,9 @@ LIB_OBJS += $(OUTPUT)lk/ctype.o
LIB_OBJS += $(OUTPUT)lk/debugfs.o
LIB_OBJS += $(OUTPUT)lk/hweight.o
LIB_OBJS += $(OUTPUT)lk/util.o
+LIB_OBJS += $(OUTPUT)lk/pstack.o
+LIB_OBJS += $(OUTPUT)lk/strbuf.o
+LIB_OBJS += $(OUTPUT)lk/usage.o
LIBFILE = lklib.a
diff --git a/tools/lib/lk/pstack.c b/tools/lib/lk/pstack.c
new file mode 100644
index 0000000..13d36fa
--- /dev/null
+++ b/tools/lib/lk/pstack.c
@@ -0,0 +1,75 @@
+/*
+ * Simple pointer stack
+ *
+ * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "util.h"
+#include "pstack.h"
+#include <linux/kernel.h>
+#include <stdlib.h>
+
+struct pstack {
+ unsigned short top;
+ unsigned short max_nr_entries;
+ void *entries[0];
+};
+
+struct pstack *pstack__new(unsigned short max_nr_entries)
+{
+ struct pstack *self = zalloc((sizeof(*self) +
+ max_nr_entries * sizeof(void *)));
+ if (self != NULL)
+ self->max_nr_entries = max_nr_entries;
+ return self;
+}
+
+void pstack__delete(struct pstack *self)
+{
+ free(self);
+}
+
+bool pstack__empty(const struct pstack *self)
+{
+ return self->top == 0;
+}
+
+void pstack__remove(struct pstack *self, void *key)
+{
+ unsigned short i = self->top, last_index = self->top - 1;
+
+ while (i-- != 0) {
+ if (self->entries[i] == key) {
+ if (i < last_index)
+ memmove(self->entries + i,
+ self->entries + i + 1,
+ (last_index - i) * sizeof(void *));
+ --self->top;
+ return;
+ }
+ }
+ pr_err("%s: %p not on the pstack!\n", __func__, key);
+}
+
+void pstack__push(struct pstack *self, void *key)
+{
+ if (self->top == self->max_nr_entries) {
+ pr_err("%s: top=%d, overflow!\n", __func__, self->top);
+ return;
+ }
+ self->entries[self->top++] = key;
+}
+
+void *pstack__pop(struct pstack *self)
+{
+ void *ret;
+
+ if (self->top == 0) {
+ pr_err("%s: underflow!\n", __func__);
+ return NULL;
+ }
+
+ ret = self->entries[--self->top];
+ self->entries[self->top] = NULL;
+ return ret;
+}
diff --git a/tools/lib/lk/pstack.h b/tools/lib/lk/pstack.h
new file mode 100644
index 0000000..aa0c248
--- /dev/null
+++ b/tools/lib/lk/pstack.h
@@ -0,0 +1,12 @@
+#ifndef __LK_PSTACK_H
+#define __LK_PSTACK_H
+
+struct pstack;
+struct pstack *pstack__new(unsigned short max_nr_entries);
+void pstack__delete(struct pstack *self);
+bool pstack__empty(const struct pstack *self);
+void pstack__remove(struct pstack *self, void *key);
+void pstack__push(struct pstack *self, void *key);
+void *pstack__pop(struct pstack *self);
+
+#endif /* __LK_PSTACK_H */
diff --git a/tools/lib/lk/strbuf.c b/tools/lib/lk/strbuf.c
new file mode 100644
index 0000000..699143e
--- /dev/null
+++ b/tools/lib/lk/strbuf.c
@@ -0,0 +1,133 @@
+#include "strbuf.h"
+
+int prefixcmp(const char *str, const char *prefix)
+{
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+/*
+ * Used as the default ->buf value, so that people can always assume
+ * buf is non NULL and ->buf is NUL terminated even for a freshly
+ * initialized strbuf.
+ */
+char strbuf_slopbuf[1];
+
+void strbuf_init(struct strbuf *sb, ssize_t hint)
+{
+ sb->alloc = sb->len = 0;
+ sb->buf = strbuf_slopbuf;
+ if (hint)
+ strbuf_grow(sb, hint);
+}
+
+void strbuf_release(struct strbuf *sb)
+{
+ if (sb->alloc) {
+ free(sb->buf);
+ strbuf_init(sb, 0);
+ }
+}
+
+char *strbuf_detach(struct strbuf *sb, size_t *sz)
+{
+ char *res = sb->alloc ? sb->buf : NULL;
+ if (sz)
+ *sz = sb->len;
+ strbuf_init(sb, 0);
+ return res;
+}
+
+void strbuf_grow(struct strbuf *sb, size_t extra)
+{
+ if (sb->len + extra + 1 <= sb->len)
+ die("you want to use way too much memory");
+ if (!sb->alloc)
+ sb->buf = NULL;
+ ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
+}
+
+static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+ const void *data, size_t dlen)
+{
+ if (pos + len < pos)
+ die("you want to use way too much memory");
+ if (pos > sb->len)
+ die("`pos' is too far after the end of the buffer");
+ if (pos + len > sb->len)
+ die("`pos + len' is too far after the end of the buffer");
+
+ if (dlen >= len)
+ strbuf_grow(sb, dlen - len);
+ memmove(sb->buf + pos + dlen,
+ sb->buf + pos + len,
+ sb->len - pos - len);
+ memcpy(sb->buf + pos, data, dlen);
+ strbuf_setlen(sb, sb->len + dlen - len);
+}
+
+void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
+{
+ strbuf_splice(sb, pos, len, NULL, 0);
+}
+
+void strbuf_add(struct strbuf *sb, const void *data, size_t len)
+{
+ strbuf_grow(sb, len);
+ memcpy(sb->buf + sb->len, data, len);
+ strbuf_setlen(sb, sb->len + len);
+}
+
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+
+ if (!strbuf_avail(sb))
+ strbuf_grow(sb, 64);
+ va_start(ap, fmt);
+ len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ va_end(ap);
+ if (len < 0)
+ die("your vsnprintf is broken");
+ if (len > strbuf_avail(sb)) {
+ strbuf_grow(sb, len);
+ va_start(ap, fmt);
+ len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ va_end(ap);
+ if (len > strbuf_avail(sb)) {
+ die("this should not happen, your snprintf is broken");
+ }
+ }
+ strbuf_setlen(sb, sb->len + len);
+}
+
+ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
+{
+ size_t oldlen = sb->len;
+ size_t oldalloc = sb->alloc;
+
+ strbuf_grow(sb, hint ? hint : 8192);
+ for (;;) {
+ ssize_t cnt;
+
+ cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
+ if (cnt < 0) {
+ if (oldalloc == 0)
+ strbuf_release(sb);
+ else
+ strbuf_setlen(sb, oldlen);
+ return -1;
+ }
+ if (!cnt)
+ break;
+ sb->len += cnt;
+ strbuf_grow(sb, 8192);
+ }
+
+ sb->buf[sb->len] = '\0';
+ return sb->len - oldlen;
+}
diff --git a/tools/lib/lk/strbuf.h b/tools/lib/lk/strbuf.h
new file mode 100644
index 0000000..147c5b4
--- /dev/null
+++ b/tools/lib/lk/strbuf.h
@@ -0,0 +1,99 @@
+#ifndef __LK_STRBUF_H
+#define __LK_STRBUF_H
+
+/*
+ * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
+ * long, overflow safe strings.
+ *
+ * Strbufs has some invariants that are very important to keep in mind:
+ *
+ * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to
+ * build complex strings/buffers whose final size isn't easily known.
+ *
+ * It is NOT legal to copy the ->buf pointer away.
+ * `strbuf_detach' is the operation that detachs a buffer from its shell
+ * while keeping the shell valid wrt its invariants.
+ *
+ * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
+ * allocated. The extra byte is used to store a '\0', allowing the ->buf
+ * member to be a valid C-string. Every strbuf function ensure this
+ * invariant is preserved.
+ *
+ * Note that it is OK to "play" with the buffer directly if you work it
+ * that way:
+ *
+ * strbuf_grow(sb, SOME_SIZE);
+ * ... Here, the memory array starting at sb->buf, and of length
+ * ... strbuf_avail(sb) is all yours, and you are sure that
+ * ... strbuf_avail(sb) is at least SOME_SIZE.
+ * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
+ *
+ * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
+ *
+ * Doing so is safe, though if it has to be done in many places, adding the
+ * missing API to the strbuf module is the way to go.
+ *
+ * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1
+ * even if it's true in the current implementation. Alloc is somehow a
+ * "private" member that should not be messed with.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "util.h"
+
+extern char strbuf_slopbuf[];
+struct strbuf {
+ size_t alloc;
+ size_t len;
+ char *buf;
+};
+
+#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
+
+extern int prefixcmp(const char *str, const char *prefix);
+
+/*----- strbuf life cycle -----*/
+extern void strbuf_init(struct strbuf *buf, ssize_t hint);
+extern void strbuf_release(struct strbuf *);
+extern char *strbuf_detach(struct strbuf *, size_t *);
+
+/*----- strbuf size related -----*/
+static inline ssize_t strbuf_avail(const struct strbuf *sb) {
+ return sb->alloc ? sb->alloc - sb->len - 1 : 0;
+}
+
+extern void strbuf_grow(struct strbuf *, size_t);
+
+static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
+ if (!sb->alloc)
+ strbuf_grow(sb, 0);
+ assert(len < sb->alloc);
+ sb->len = len;
+ sb->buf[len] = '\0';
+}
+
+/*----- add data in your buffer -----*/
+static inline void strbuf_addch(struct strbuf *sb, int c) {
+ strbuf_grow(sb, 1);
+ sb->buf[sb->len++] = c;
+ sb->buf[sb->len] = '\0';
+}
+
+extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
+
+extern void strbuf_add(struct strbuf *, const void *, size_t);
+static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
+ strbuf_add(sb, s, strlen(s));
+}
+
+__attribute__((format(printf,2,3)))
+extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+
+/* XXX: if read fails, any partial read is undone */
+extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
+
+#endif /* __LK_STRBUF_H */
diff --git a/tools/lib/lk/usage.c b/tools/lib/lk/usage.c
new file mode 100644
index 0000000..d438743
--- /dev/null
+++ b/tools/lib/lk/usage.c
@@ -0,0 +1,80 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include <lk/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
index f380fed..dc88970 100644
--- a/tools/lib/lk/util.h
+++ b/tools/lib/lk/util.h
@@ -141,6 +141,26 @@ extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1,
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/*
+ * Realloc the buffer pointed at by variable 'x' so that it can hold
+ * at least 'nr' entries; the number of entries currently allocated
+ * is 'alloc', using the standard growing factor alloc_nr() macro.
+ *
+ * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ if (alloc_nr(alloc) < (nr)) \
+ alloc = (nr); \
+ else \
+ alloc = alloc_nr(alloc); \
+ x = xrealloc((x), alloc * sizeof(*(x))); \
+ } \
+ } while(0)
+
#include "../../../include/linux/stringify.h"
#define DIE_IF(cnd) \
@@ -152,7 +172,6 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
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);
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index e224788..6fc4c4a 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -366,7 +366,6 @@ LIB_H += util/quote.h
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
@@ -380,7 +379,6 @@ 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
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -397,10 +395,8 @@ LIB_OBJS += $(OUTPUT)util/path.o
LIB_OBJS += $(OUTPUT)util/rbtree.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/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
@@ -411,7 +407,6 @@ 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
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 08e69d2..8d46079 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -2,7 +2,7 @@
#define BUILTIN_H
#include <lk/util.h>
-#include "util/strbuf.h"
+#include <lk/strbuf.h>
extern const char perf_version_string[];
extern const char perf_usage_string[];
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index ce83e82..93570a8 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -3,7 +3,7 @@
#include <stdbool.h>
#include <lk/util.h>
-#include "strbuf.h"
+#include <lk/strbuf.h>
#include "../perf.h"
#include <linux/compiler.h>
@@ -47,27 +47,6 @@ void exit_browser(bool wait_for_ok);
char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv);
-#define alloc_nr(x) (((x)+16)*3/2)
-
-/*
- * Realloc the buffer pointed at by variable 'x' so that it can hold
- * at least 'nr' entries; the number of entries currently allocated
- * is 'alloc', using the standard growing factor alloc_nr() macro.
- *
- * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
- */
-#define ALLOC_GROW(x, nr, alloc) \
- do { \
- if ((nr) > alloc) { \
- if (alloc_nr(alloc) < (nr)) \
- alloc = (nr); \
- else \
- alloc = alloc_nr(alloc); \
- x = xrealloc((x), alloc * sizeof(*(x))); \
- } \
- } while(0)
-
-
static inline int is_absolute_path(const char *path)
{
return path[0] == '/';
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
deleted file mode 100644
index aacedb8..0000000
--- a/tools/perf/util/pstack.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Simple pointer stack
- *
- * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
- */
-
-#include <lk/util.h>
-#include "pstack.h"
-#include <linux/kernel.h>
-#include <stdlib.h>
-
-struct pstack {
- unsigned short top;
- unsigned short max_nr_entries;
- void *entries[0];
-};
-
-struct pstack *pstack__new(unsigned short max_nr_entries)
-{
- struct pstack *self = zalloc((sizeof(*self) +
- max_nr_entries * sizeof(void *)));
- if (self != NULL)
- self->max_nr_entries = max_nr_entries;
- return self;
-}
-
-void pstack__delete(struct pstack *self)
-{
- free(self);
-}
-
-bool pstack__empty(const struct pstack *self)
-{
- return self->top == 0;
-}
-
-void pstack__remove(struct pstack *self, void *key)
-{
- unsigned short i = self->top, last_index = self->top - 1;
-
- while (i-- != 0) {
- if (self->entries[i] == key) {
- if (i < last_index)
- memmove(self->entries + i,
- self->entries + i + 1,
- (last_index - i) * sizeof(void *));
- --self->top;
- return;
- }
- }
- pr_err("%s: %p not on the pstack!\n", __func__, key);
-}
-
-void pstack__push(struct pstack *self, void *key)
-{
- if (self->top == self->max_nr_entries) {
- pr_err("%s: top=%d, overflow!\n", __func__, self->top);
- return;
- }
- self->entries[self->top++] = key;
-}
-
-void *pstack__pop(struct pstack *self)
-{
- void *ret;
-
- if (self->top == 0) {
- pr_err("%s: underflow!\n", __func__);
- return NULL;
- }
-
- ret = self->entries[--self->top];
- self->entries[self->top] = NULL;
- return ret;
-}
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
deleted file mode 100644
index 5ad0702..0000000
--- a/tools/perf/util/pstack.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _PERF_PSTACK_
-#define _PERF_PSTACK_
-
-struct pstack;
-struct pstack *pstack__new(unsigned short max_nr_entries);
-void pstack__delete(struct pstack *self);
-bool pstack__empty(const struct pstack *self);
-void pstack__remove(struct pstack *self, void *key);
-void pstack__push(struct pstack *self, void *key);
-void *pstack__pop(struct pstack *self);
-
-#endif /* _PERF_PSTACK_ */
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
deleted file mode 100644
index 92e0685..0000000
--- a/tools/perf/util/strbuf.c
+++ /dev/null
@@ -1,133 +0,0 @@
-#include "cache.h"
-
-int prefixcmp(const char *str, const char *prefix)
-{
- for (; ; str++, prefix++)
- if (!*prefix)
- return 0;
- else if (*str != *prefix)
- return (unsigned char)*prefix - (unsigned char)*str;
-}
-
-/*
- * Used as the default ->buf value, so that people can always assume
- * buf is non NULL and ->buf is NUL terminated even for a freshly
- * initialized strbuf.
- */
-char strbuf_slopbuf[1];
-
-void strbuf_init(struct strbuf *sb, ssize_t hint)
-{
- sb->alloc = sb->len = 0;
- sb->buf = strbuf_slopbuf;
- if (hint)
- strbuf_grow(sb, hint);
-}
-
-void strbuf_release(struct strbuf *sb)
-{
- if (sb->alloc) {
- free(sb->buf);
- strbuf_init(sb, 0);
- }
-}
-
-char *strbuf_detach(struct strbuf *sb, size_t *sz)
-{
- char *res = sb->alloc ? sb->buf : NULL;
- if (sz)
- *sz = sb->len;
- strbuf_init(sb, 0);
- return res;
-}
-
-void strbuf_grow(struct strbuf *sb, size_t extra)
-{
- if (sb->len + extra + 1 <= sb->len)
- die("you want to use way too much memory");
- if (!sb->alloc)
- sb->buf = NULL;
- ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
-}
-
-static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
- const void *data, size_t dlen)
-{
- if (pos + len < pos)
- die("you want to use way too much memory");
- if (pos > sb->len)
- die("`pos' is too far after the end of the buffer");
- if (pos + len > sb->len)
- die("`pos + len' is too far after the end of the buffer");
-
- if (dlen >= len)
- strbuf_grow(sb, dlen - len);
- memmove(sb->buf + pos + dlen,
- sb->buf + pos + len,
- sb->len - pos - len);
- memcpy(sb->buf + pos, data, dlen);
- strbuf_setlen(sb, sb->len + dlen - len);
-}
-
-void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
-{
- strbuf_splice(sb, pos, len, NULL, 0);
-}
-
-void strbuf_add(struct strbuf *sb, const void *data, size_t len)
-{
- strbuf_grow(sb, len);
- memcpy(sb->buf + sb->len, data, len);
- strbuf_setlen(sb, sb->len + len);
-}
-
-void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
-{
- int len;
- va_list ap;
-
- if (!strbuf_avail(sb))
- strbuf_grow(sb, 64);
- va_start(ap, fmt);
- len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
- va_end(ap);
- if (len < 0)
- die("your vsnprintf is broken");
- if (len > strbuf_avail(sb)) {
- strbuf_grow(sb, len);
- va_start(ap, fmt);
- len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
- va_end(ap);
- if (len > strbuf_avail(sb)) {
- die("this should not happen, your snprintf is broken");
- }
- }
- strbuf_setlen(sb, sb->len + len);
-}
-
-ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
-{
- size_t oldlen = sb->len;
- size_t oldalloc = sb->alloc;
-
- strbuf_grow(sb, hint ? hint : 8192);
- for (;;) {
- ssize_t cnt;
-
- cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
- if (cnt < 0) {
- if (oldalloc == 0)
- strbuf_release(sb);
- else
- strbuf_setlen(sb, oldlen);
- return -1;
- }
- if (!cnt)
- break;
- sb->len += cnt;
- strbuf_grow(sb, 8192);
- }
-
- sb->buf[sb->len] = '\0';
- return sb->len - oldlen;
-}
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h
deleted file mode 100644
index 436ac31..0000000
--- a/tools/perf/util/strbuf.h
+++ /dev/null
@@ -1,92 +0,0 @@
-#ifndef __PERF_STRBUF_H
-#define __PERF_STRBUF_H
-
-/*
- * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
- * long, overflow safe strings.
- *
- * Strbufs has some invariants that are very important to keep in mind:
- *
- * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to
- * build complex strings/buffers whose final size isn't easily known.
- *
- * It is NOT legal to copy the ->buf pointer away.
- * `strbuf_detach' is the operation that detachs a buffer from its shell
- * while keeping the shell valid wrt its invariants.
- *
- * 2. the ->buf member is a byte array that has at least ->len + 1 bytes
- * allocated. The extra byte is used to store a '\0', allowing the ->buf
- * member to be a valid C-string. Every strbuf function ensure this
- * invariant is preserved.
- *
- * Note that it is OK to "play" with the buffer directly if you work it
- * that way:
- *
- * strbuf_grow(sb, SOME_SIZE);
- * ... Here, the memory array starting at sb->buf, and of length
- * ... strbuf_avail(sb) is all yours, and you are sure that
- * ... strbuf_avail(sb) is at least SOME_SIZE.
- * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
- *
- * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
- *
- * Doing so is safe, though if it has to be done in many places, adding the
- * missing API to the strbuf module is the way to go.
- *
- * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1
- * even if it's true in the current implementation. Alloc is somehow a
- * "private" member that should not be messed with.
- */
-
-#include <assert.h>
-
-extern char strbuf_slopbuf[];
-struct strbuf {
- size_t alloc;
- size_t len;
- char *buf;
-};
-
-#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
-
-/*----- strbuf life cycle -----*/
-extern void strbuf_init(struct strbuf *buf, ssize_t hint);
-extern void strbuf_release(struct strbuf *);
-extern char *strbuf_detach(struct strbuf *, size_t *);
-
-/*----- strbuf size related -----*/
-static inline ssize_t strbuf_avail(const struct strbuf *sb) {
- return sb->alloc ? sb->alloc - sb->len - 1 : 0;
-}
-
-extern void strbuf_grow(struct strbuf *, size_t);
-
-static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
- if (!sb->alloc)
- strbuf_grow(sb, 0);
- assert(len < sb->alloc);
- sb->len = len;
- sb->buf[len] = '\0';
-}
-
-/*----- add data in your buffer -----*/
-static inline void strbuf_addch(struct strbuf *sb, int c) {
- strbuf_grow(sb, 1);
- sb->buf[sb->len++] = c;
- sb->buf[sb->len] = '\0';
-}
-
-extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
-
-extern void strbuf_add(struct strbuf *, const void *, size_t);
-static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
- strbuf_add(sb, s, strlen(s));
-}
-
-__attribute__((format(printf,2,3)))
-extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
-
-/* XXX: if read fails, any partial read is undone */
-extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
-
-#endif /* __PERF_STRBUF_H */
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
deleted file mode 100644
index d438743..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 <lk/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);
-}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 08/21] perf: rewire generic library stuff, p4
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (6 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 07/21] perf: rewire generic library stuff, p3 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 09/21] perf: rewire generic library stuff, p5 Borislav Petkov
` (12 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Split util/config.c and util/color.c into a generic and a perf-specific
parts. As a result, remove unused perf_config_colorbool() and
perf_color_default_config(). Move pager.c elements used in color.c
temporarily into it. Will be moved to tools/perf/perf.c unit later.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 3 +
tools/lib/lk/color.c | 306 ++++++++++++++++++++++++++
tools/lib/lk/color.h | 46 ++++
tools/lib/lk/config.c | 388 +++++++++++++++++++++++++++++++++
tools/lib/lk/config.h | 18 ++
tools/lib/lk/util.h | 1 -
tools/perf/Makefile | 5 +-
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-help.c | 13 +-
tools/perf/builtin-report.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/perf.c | 7 +-
tools/perf/util/alias.c | 4 +-
tools/perf/util/build-id.c | 51 +++++
tools/perf/util/build-id.h | 2 +
tools/perf/util/cache.h | 10 -
tools/perf/util/color.c | 324 ----------------------------
tools/perf/util/color.h | 46 ----
tools/perf/util/config.c | 460 ++--------------------------------------
tools/perf/util/config.h | 8 +
tools/perf/util/debug.c | 4 +-
tools/perf/util/environment.c | 3 +-
tools/perf/util/help.c | 7 +-
| 18 +--
tools/perf/util/probe-event.c | 4 +-
tools/perf/util/sort.h | 2 +-
27 files changed, 875 insertions(+), 865 deletions(-)
create mode 100644 tools/lib/lk/color.c
create mode 100644 tools/lib/lk/color.h
create mode 100644 tools/lib/lk/config.c
create mode 100644 tools/lib/lk/config.h
delete mode 100644 tools/perf/util/color.c
delete mode 100644 tools/perf/util/color.h
create mode 100644 tools/perf/util/config.h
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 64c0dbd..7acb91d 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -10,6 +10,7 @@ LIB_H += lk/util.h
LIB_H += lk/types.h
LIB_H += lk/pstack.h
LIB_H += lk/strbuf.h
+LIB_H += lk/color.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -20,6 +21,8 @@ LIB_OBJS += $(OUTPUT)lk/util.o
LIB_OBJS += $(OUTPUT)lk/pstack.o
LIB_OBJS += $(OUTPUT)lk/strbuf.o
LIB_OBJS += $(OUTPUT)lk/usage.o
+LIB_OBJS += $(OUTPUT)lk/color.o
+LIB_OBJS += $(OUTPUT)lk/config.o
LIBFILE = lklib.a
diff --git a/tools/lib/lk/color.c b/tools/lib/lk/color.c
new file mode 100644
index 0000000..93d6381
--- /dev/null
+++ b/tools/lib/lk/color.c
@@ -0,0 +1,306 @@
+#include "config.h"
+#include "color.h"
+
+/*
+ * This variable stores the value of color.ui
+ */
+static int use_color_default = -1;
+
+/*
+ * This is going into tools/perf/perf.c next
+ */
+int spawned_pager, pager_use_color = 1;
+
+int pager_in_use(void)
+{
+ const char *env;
+
+ if (spawned_pager)
+ return 1;
+
+ env = getenv("PERF_PAGER_IN_USE");
+ return env ? lk_config_bool("PERF_PAGER_IN_USE", env) : 0;
+}
+
+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, LK_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);
+}
+
+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 (use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ use_color_default = 1;
+ else
+ use_color_default = 0;
+ }
+
+ if (use_color_default && *color)
+ r += snprintf(bf, size, "%s", color);
+ r += vsnprintf(bf + r, size - r, fmt, args);
+ if (use_color_default && *color)
+ r += snprintf(bf + r, size - r, "%s", LK_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 (use_color_default < 0) {
+ if (isatty(1) || pager_in_use())
+ use_color_default = 1;
+ else
+ use_color_default = 0;
+ }
+
+ if (use_color_default && *color)
+ r += fprintf(fp, "%s", color);
+ r += vfprintf(fp, fmt, args);
+ if (use_color_default && *color)
+ r += fprintf(fp, "%s", LK_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(LK_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 = LK_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 = LK_COLOR_RED;
+ else {
+ if (percent > MIN_GREEN)
+ color = LK_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/lk/color.h b/tools/lib/lk/color.h
new file mode 100644
index 0000000..c962e1d
--- /dev/null
+++ b/tools/lib/lk/color.h
@@ -0,0 +1,46 @@
+#ifndef __LK_COLOR_H
+#define __LK_COLOR_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "util.h"
+
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+#define COLOR_MAXLEN 24
+
+#define LK_COLOR_NORMAL ""
+#define LK_COLOR_RESET "\033[m"
+#define LK_COLOR_BOLD "\033[1m"
+#define LK_COLOR_RED "\033[31m"
+#define LK_COLOR_GREEN "\033[32m"
+#define LK_COLOR_YELLOW "\033[33m"
+#define LK_COLOR_BLUE "\033[34m"
+#define LK_COLOR_MAGENTA "\033[35m"
+#define LK_COLOR_CYAN "\033[36m"
+#define LK_COLOR_BG_RED "\033[41m"
+
+#define MIN_GREEN 0.5
+#define MIN_RED 5.0
+
+extern int spawned_pager, pager_use_color;
+
+extern int pager_in_use(void);
+
+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 /* __LK_COLOR_H */
diff --git a/tools/lib/lk/config.c b/tools/lib/lk/config.c
new file mode 100644
index 0000000..ea8a6c0
--- /dev/null
+++ b/tools/lib/lk/config.c
@@ -0,0 +1,388 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Johannes Schindelin, 2005
+ *
+ */
+#include <linux/compiler.h>
+#include <lk/util.h>
+#include <lk/strbuf.h>
+#include "config.h"
+
+#define MAXNAME (256)
+
+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;
+
+const char *lk_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 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 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 lk_config_int(const char *name, const char *value)
+{
+ long ret = 0;
+ if (!parse_long(value, &ret))
+ die_bad_config(name);
+ return ret;
+}
+
+static int 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 lk_config_int(name, value);
+}
+
+int lk_config_bool(const char *name, const char *value)
+{
+ int discard;
+ return !!config_bool_or_int(name, value, &discard);
+}
+
+const char *lk_config_dirname(const char *name, const char *value)
+{
+ if (!name)
+ return NULL;
+ return value;
+}
+
+static int default_core_config(const char *var __used, const char *value __used)
+{
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+int lk_default_config(const char *var, const char *value, void *dummy __used)
+{
+ if (!prefixcmp(var, "core."))
+ return default_core_config(var, value);
+
+ /* Add other config variables here and to Documentation/config.txt. */
+ return 0;
+}
+
+int lk_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 = parse_file(fn, data);
+ fclose(f);
+ config_file_name = NULL;
+ }
+ return ret;
+}
+
+int lk_env_bool(const char *k, int def)
+{
+ const char *v = getenv(k);
+ return v ? lk_config_bool(k, v) : def;
+}
+
+/*
+ * Call this to report error for your variable that should not
+ * get a boolean value (i.e. "[my] var" means "true").
+ */
+int lk_config_error_nonbool(const char *var)
+{
+ return error("Missing value for '%s'", var);
+}
+
+
diff --git a/tools/lib/lk/config.h b/tools/lib/lk/config.h
new file mode 100644
index 0000000..6bcce49
--- /dev/null
+++ b/tools/lib/lk/config.h
@@ -0,0 +1,18 @@
+#ifndef __LK_CONFIG_H
+#define __LK_CONFIG_H
+
+#define DEBUG_CACHE_DIR ".debug"
+
+typedef int (*config_fn_t)(const char *, const char *, void *);
+
+extern const char *lk_config_exclusive_filename;
+
+extern int lk_env_bool(const char *k, int def);
+extern int lk_config_int(const char *, const char *);
+extern int lk_config_bool(const char *name, const char *value);
+extern const char *lk_config_dirname(const char *name, const char *value);
+extern int lk_config_from_file(config_fn_t fn, const char *filename, void *data);
+extern int lk_default_config(const char *, const char *, void *);
+extern int lk_config_error_nonbool(const char *var);
+
+#endif /* __LK_CONFIG_H */
diff --git a/tools/lib/lk/util.h b/tools/lib/lk/util.h
index dc88970..4db0f3f 100644
--- a/tools/lib/lk/util.h
+++ b/tools/lib/lk/util.h
@@ -172,7 +172,6 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
-extern void set_buildid_dir(void);
extern void disable_buildid_cache(void);
static inline const char *skip_prefix(const char *str, const char *prefix)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 6fc4c4a..42a1756 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -371,7 +371,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
@@ -379,11 +378,11 @@ 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/config.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
@@ -400,7 +399,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
@@ -417,6 +415,7 @@ 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/config.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 03f80e9..42caf9c 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 <lk/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
@@ -130,7 +130,7 @@ static int objdump_line__print(struct objdump_line *self,
color_fprintf(stdout, color, " %7.2f", percent);
printf(" : ");
- color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
+ color_fprintf(stdout, LK_COLOR_BLUE, "%s\n", self->line);
} else {
if (!*self->line)
printf(" :\n");
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 6d5a8a7..b6b7e1a 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -5,6 +5,7 @@
*/
#include "perf.h"
#include "util/cache.h"
+#include "util/config.h"
#include "builtin.h"
#include "util/exec_cmd.h"
#include "common-cmds.h"
@@ -12,6 +13,8 @@
#include "util/run-command.h"
#include "util/help.h"
+#include <lk/config.h>
+
static struct man_viewer_list {
struct man_viewer_list *next;
char name[FLEX_ARRAY];
@@ -240,12 +243,12 @@ static int add_man_viewer_info(const char *var, const char *value)
if (!strcmp(subkey, ".path")) {
if (!value)
- return config_error_nonbool(var);
+ return lk_config_error_nonbool(var);
return add_man_viewer_path(name, subkey - name, value);
}
if (!strcmp(subkey, ".cmd")) {
if (!value)
- return config_error_nonbool(var);
+ return lk_config_error_nonbool(var);
return add_man_viewer_cmd(name, subkey - name, value);
}
@@ -257,20 +260,20 @@ static int perf_help_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "help.format")) {
if (!value)
- return config_error_nonbool(var);
+ return lk_config_error_nonbool(var);
help_format = parse_help_format(value);
return 0;
}
if (!strcmp(var, "man.viewer")) {
if (!value)
- return config_error_nonbool(var);
+ return lk_config_error_nonbool(var);
add_man_viewer(value);
return 0;
}
if (!prefixcmp(var, "man."))
return add_man_viewer_info(var, value);
- return perf_default_config(var, value, cb);
+ return lk_default_config(var, value, cb);
}
static struct cmdnames main_cmds, other_cmds;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 4be3c67..9a618ac 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 <lk/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 92f7230..5440b11 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 <lk/color.h>
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 9813351..2b4dce1 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 <lk/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 2fda133..9600a2d 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -10,10 +10,13 @@
#include "util/exec_cmd.h"
#include "util/cache.h"
+#include "util/config.h"
#include "util/quote.h"
+#include "util/build-id.h"
#include "util/run-command.h"
#include "util/parse-events.h"
#include <lk/debugfs.h>
+#include <lk/config.h>
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -35,7 +38,7 @@ static int pager_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
- c->val = perf_config_bool(var, value);
+ c->val = lk_config_bool(var, value);
return 0;
}
@@ -53,7 +56,7 @@ static int tui_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
- c->val = perf_config_bool(var, value);
+ c->val = lk_config_bool(var, value);
return 0;
}
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c
index b8144e8..57b303a 100644
--- a/tools/perf/util/alias.c
+++ b/tools/perf/util/alias.c
@@ -1,4 +1,6 @@
#include "cache.h"
+#include <lk/config.h>
+#include "config.h"
static const char *alias_key;
static char *alias_val;
@@ -7,7 +9,7 @@ static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
{
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
if (!v)
- return config_error_nonbool(k);
+ return lk_config_error_nonbool(k);
alias_val = strdup(v);
return 0;
}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 5de09e2..44f2878 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -7,6 +7,8 @@
* Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include <lk/util.h>
+#include <lk/config.h>
+#include <lk/strbuf.h>
#include <stdio.h>
#include "build-id.h"
#include "event.h"
@@ -57,3 +59,52 @@ char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
build_id_hex, build_id_hex + 2);
return bf;
}
+
+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 = lk_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/build-id.h b/tools/perf/util/build-id.h
index 5dafb00..e72ed79 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -2,9 +2,11 @@
#define PERF_BUILD_ID_H_ 1
#include "session.h"
+#include "config.h"
extern struct perf_event_ops build_id__mark_dso_hit_ops;
char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+extern void set_buildid_dir(void);
#endif
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 93570a8..b0902b8 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;
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
index bb2f5a0..73e6421 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -1,375 +1,19 @@
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- * Copyright (C) Johannes Schindelin, 2005
- *
- */
-#include <lk/util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "cache.h"
+#include "config.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)
+static int perf_config_system(void)
{
- /* Add other config variables here and to Documentation/config.txt. */
- return 0;
+ return !lk_env_bool("PERF_CONFIG_NOSYSTEM", 0);
}
-int perf_default_config(const char *var, const char *value, void *dummy __used)
+static int perf_config_global(void)
{
- 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;
+ return !lk_env_bool("PERF_CONFIG_NOGLOBAL", 0);
}
static const char *perf_etc_perfconfig(void)
@@ -380,22 +24,6 @@ static const char *perf_etc_perfconfig(void)
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;
@@ -403,11 +31,10 @@ int perf_config(config_fn_t fn, void *data)
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 (lk_config_exclusive_filename)
+ return lk_config_from_file(fn, lk_config_exclusive_filename, data);
if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
- ret += perf_config_from_file(fn, perf_etc_perfconfig(),
- data);
+ ret += lk_config_from_file(fn, perf_etc_perfconfig(), data);
found += 1;
}
@@ -415,7 +42,7 @@ int perf_config(config_fn_t fn, void *data)
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);
+ ret += lk_config_from_file(fn, user_config, data);
found += 1;
}
free(user_config);
@@ -423,7 +50,7 @@ int perf_config(config_fn_t fn, void *data)
repo_config = perf_pathdup("config");
if (!access(repo_config, R_OK)) {
- ret += perf_config_from_file(fn, repo_config, data);
+ ret += lk_config_from_file(fn, repo_config, data);
found += 1;
}
free(repo_config);
@@ -431,62 +58,3 @@ int perf_config(config_fn_t fn, void *data)
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/config.h b/tools/perf/util/config.h
new file mode 100644
index 0000000..120ad69
--- /dev/null
+++ b/tools/perf/util/config.h
@@ -0,0 +1,8 @@
+#ifndef __PERF_CONFIG_H
+#define __PERF_CONFIG_H
+
+#include <lk/config.h>
+
+extern int perf_config(config_fn_t fn, void *data);
+
+#endif /* __PERF_CONFIG_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 96cb72a..56635de 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -7,10 +7,10 @@
#include <stdio.h>
#include "cache.h"
-#include "color.h"
#include "event.h"
#include "debug.h"
#include <lk/util.h>
+#include <lk/color.h>
int verbose = 0;
bool dump_trace = false;
@@ -64,7 +64,7 @@ static int dump_printf_color(const char *fmt, const char *color, ...)
void trace_event(event_t *event)
{
unsigned char *raw_event = (void *)event;
- const char *color = PERF_COLOR_BLUE;
+ const char *color = LK_COLOR_BLUE;
int i, j;
if (!dump_trace)
diff --git a/tools/perf/util/environment.c b/tools/perf/util/environment.c
index 275b0ee..bd61953 100644
--- a/tools/perf/util/environment.c
+++ b/tools/perf/util/environment.c
@@ -3,7 +3,6 @@
* file, so that programs can link against the config parser
* without having to link against all the rest of perf.
*/
-#include "cache.h"
+#include <lk/color.h>
const char *pager_program;
-int pager_use_color = 1;
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c
index 6f2975a..109ebc8 100644
--- a/tools/perf/util/help.c
+++ b/tools/perf/util/help.c
@@ -3,6 +3,9 @@
#include "exec_cmd.h"
#include "levenshtein.h"
#include "help.h"
+#include "config.h"
+
+#include <lk/config.h>
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
{
@@ -237,12 +240,12 @@ static struct cmdnames aliases;
static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "help.autocorrect"))
- autocorrect = perf_config_int(var,value);
+ autocorrect = lk_config_int(var,value);
/* Also use aliases for command lookup */
if (!prefixcmp(var, "alias."))
add_cmdname(&aliases, var + 6, strlen(var + 6));
- return perf_default_config(var, value, cb);
+ return lk_default_config(var, value, cb);
}
static int levenshtein_compare(const void *p1, const void *p2)
--git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 1915de2..7afdad7 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -1,4 +1,8 @@
+#include <lk/config.h>
+#include <lk/color.h>
+
#include "cache.h"
+#include "config.h"
#include "run-command.h"
#include "sigchain.h"
@@ -7,7 +11,6 @@
* something different on Windows.
*/
-static int spawned_pager;
static void pager_preexec(void)
{
@@ -52,7 +55,7 @@ void setup_pager(void)
return;
if (!pager) {
if (!pager_program)
- perf_config(perf_default_config, NULL);
+ perf_config(lk_default_config, NULL);
pager = pager_program;
}
if (!pager)
@@ -83,14 +86,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/probe-event.c b/tools/perf/util/probe-event.c
index 723b10d..74efa4a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -39,7 +39,7 @@
#include "strlist.h"
#include "debug.h"
#include "cache.h"
-#include "color.h"
+#include <lk/color.h>
#include "symbol.h"
#include "thread.h"
#include <lk/debugfs.h>
@@ -201,7 +201,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
static int show_one_line(FILE *fp, int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
- const char *color = PERF_COLOR_BLUE;
+ const char *color = LK_COLOR_BLUE;
if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
goto error;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 8ead949..4dd264a 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 <lk/color.h>
#include <linux/list.h>
#include "cache.h"
#include <linux/rbtree.h>
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 09/21] perf: rewire generic library stuff, p5
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (7 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 08/21] perf: rewire generic library stuff, p4 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 10/21] perf: rewire generic library stuff, p6 Borislav Petkov
` (11 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out debug.[ch] and string.c and make them generic.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 3 +
tools/lib/lk/debug.c | 108 +++++++++++
tools/lib/lk/debug.h | 50 +++++
tools/lib/lk/pstack.c | 2 +-
tools/lib/lk/string.c | 296 ++++++++++++++++++++++++++++++
tools/lib/lk/util.h | 2 +-
tools/perf/Makefile | 3 -
tools/perf/bench/mem-memcpy.c | 2 +-
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-buildid-cache.c | 2 +-
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 | 2 +-
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-test.c | 2 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 3 +-
tools/perf/perf.c | 2 +-
tools/perf/util/build-id.c | 1 +
tools/perf/util/cache.h | 2 -
tools/perf/util/callchain.c | 1 +
tools/perf/util/debug.c | 98 ----------
tools/perf/util/debug.h | 39 ----
tools/perf/util/event.c | 2 +-
| 2 +-
tools/perf/util/include/linux/compiler.h | 1 +
tools/perf/util/include/linux/kernel.h | 3 -
tools/perf/util/map.c | 32 ++--
tools/perf/util/newt.c | 2 +-
tools/perf/util/parse-events.c | 1 +
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-finder.c | 2 +-
tools/perf/util/sort.h | 2 +-
tools/perf/util/string.c | 296 ------------------------------
tools/perf/util/symbol.c | 2 +
tools/perf/util/thread.c | 2 +-
44 files changed, 509 insertions(+), 484 deletions(-)
create mode 100644 tools/lib/lk/debug.c
create mode 100644 tools/lib/lk/debug.h
create mode 100644 tools/lib/lk/string.c
delete mode 100644 tools/perf/util/debug.c
delete mode 100644 tools/perf/util/debug.h
delete mode 100644 tools/perf/util/string.c
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 7acb91d..df60156 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -11,6 +11,7 @@ LIB_H += lk/types.h
LIB_H += lk/pstack.h
LIB_H += lk/strbuf.h
LIB_H += lk/color.h
+LIB_H += lk/debug.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -23,6 +24,8 @@ LIB_OBJS += $(OUTPUT)lk/strbuf.o
LIB_OBJS += $(OUTPUT)lk/usage.o
LIB_OBJS += $(OUTPUT)lk/color.o
LIB_OBJS += $(OUTPUT)lk/config.o
+LIB_OBJS += $(OUTPUT)lk/debug.o
+LIB_OBJS += $(OUTPUT)lk/string.o
LIBFILE = lklib.a
diff --git a/tools/lib/lk/debug.c b/tools/lib/lk/debug.c
new file mode 100644
index 0000000..73edbb8
--- /dev/null
+++ b/tools/lib/lk/debug.c
@@ -0,0 +1,108 @@
+/* For general debugging purposes */
+
+#include <perf.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "event.h"
+#include "debug.h"
+#include <lk/util.h>
+#include <lk/color.h>
+
+/*
+ * will move to tools/perf/perf.c
+ */
+int use_browser = -1;
+
+int verbose = 0;
+bool dump_trace = false;
+
+/*
+ * overridden by perf/util/newt.c:_browser__show_help()
+ */
+int __weak _browser__show_help(const char *format __used, va_list ap __used)
+{
+ return 0;
+}
+
+int eprintf(int level, const char *fmt, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (verbose >= level) {
+ va_start(args, fmt);
+ if (use_browser > 0)
+ ret = browser__show_help(fmt, args);
+ else
+ ret = vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
+int dump_printf(const char *fmt, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (dump_trace) {
+ va_start(args, fmt);
+ ret = vprintf(fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
+static int dump_printf_color(const char *fmt, const char *color, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ if (dump_trace) {
+ va_start(args, color);
+ ret = color_vfprintf(stdout, color, fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
+
+void trace_event(event_t *event)
+{
+ unsigned char *raw_event = (void *)event;
+ const char *color = LK_COLOR_BLUE;
+ int i, j;
+
+ if (!dump_trace)
+ return;
+
+ dump_printf(".");
+ dump_printf_color("\n. ... raw event: size %d bytes\n", color,
+ event->header.size);
+
+ for (i = 0; i < event->header.size; i++) {
+ if ((i & 15) == 0) {
+ dump_printf(".");
+ dump_printf_color(" %04x: ", color, i);
+ }
+
+ dump_printf_color(" %02x", color, raw_event[i]);
+
+ if (((i & 15) == 15) || i == event->header.size-1) {
+ dump_printf_color(" ", color);
+ for (j = 0; j < 15-(i & 15); j++)
+ dump_printf_color(" ", color);
+ for (j = i & ~15; j <= i; j++) {
+ dump_printf_color("%c", color,
+ isprint(raw_event[j]) ?
+ raw_event[j] : '.');
+ }
+ dump_printf_color("\n", color);
+ }
+ }
+ dump_printf(".\n");
+}
diff --git a/tools/lib/lk/debug.h b/tools/lib/lk/debug.h
new file mode 100644
index 0000000..6df0457
--- /dev/null
+++ b/tools/lib/lk/debug.h
@@ -0,0 +1,50 @@
+/* For debugging general purposes */
+#ifndef __LK_DEBUG_H
+#define __LK_DEBUG_H
+
+#include <stdbool.h>
+#include <util/event.h>
+
+extern int verbose;
+extern bool dump_trace;
+
+extern int use_browser;
+
+int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void trace_event(event_t *event);
+
+struct ui_progress;
+
+extern int eprintf(int level,
+ const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+extern int __weak _browser__show_help(const char *format, va_list ap);
+
+#ifdef NO_NEWT_SUPPORT
+static inline int browser__show_help(const char *format __used, va_list ap __used)
+{
+ return 0;
+}
+
+static inline struct ui_progress *ui_progress__new(const char *title __used,
+ u64 total __used)
+{
+ return (struct ui_progress *)1;
+}
+
+static inline void ui_progress__update(struct ui_progress *self __used,
+ u64 curr __used) {}
+
+static inline void ui_progress__delete(struct ui_progress *self __used) {}
+#else
+static inline int browser__show_help(const char *format, va_list ap)
+{
+ return _browser__show_help(format, ap);
+}
+
+struct ui_progress *ui_progress__new(const char *title, u64 total);
+void ui_progress__update(struct ui_progress *self, u64 curr);
+void ui_progress__delete(struct ui_progress *self);
+#endif
+
+#endif /* __LK_DEBUG_H */
diff --git a/tools/lib/lk/pstack.c b/tools/lib/lk/pstack.c
index 13d36fa..acb758b 100644
--- a/tools/lib/lk/pstack.c
+++ b/tools/lib/lk/pstack.c
@@ -5,8 +5,8 @@
*/
#include "util.h"
+#include "debug.h"
#include "pstack.h"
-#include <linux/kernel.h>
#include <stdlib.h>
struct pstack {
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 4db0f3f..ec87421 100644
--- a/tools/lib/lk/util.h
+++ b/tools/lib/lk/util.h
@@ -277,7 +277,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 42a1756..8770ff4 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -355,7 +355,6 @@ LIB_H += perf.h
LIB_H += util/cache.h
LIB_H += util/callchain.h
LIB_H += util/build-id.h
-LIB_H += util/debug.h
LIB_H += util/event.h
LIB_H += util/exec_cmd.h
LIB_H += util/levenshtein.h
@@ -394,7 +393,6 @@ LIB_OBJS += $(OUTPUT)util/path.o
LIB_OBJS += $(OUTPUT)util/rbtree.o
LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.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
@@ -403,7 +401,6 @@ 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/session.o
LIB_OBJS += $(OUTPUT)util/thread.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/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 42caf9c..04afd8a 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -16,7 +16,7 @@
#include "util/symbol.h"
#include "perf.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/event.h"
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 29ad20e..5621017 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -9,7 +9,7 @@
#include "builtin.h"
#include "perf.h"
#include "util/cache.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/header.h"
#include "util/parse-options.h"
#include "util/strlist.h"
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 9989072..b767dde 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -10,7 +10,7 @@
#include "perf.h"
#include "util/build-id.h"
#include "util/cache.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/parse-options.h"
#include "util/session.h"
#include "util/symbol.h"
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 2f5a711..b346e7f 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -6,7 +6,7 @@
*/
#include "builtin.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/event.h"
#include "util/hist.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 8e3e47b..c9127a9 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -9,7 +9,7 @@
#include "perf.h"
#include "util/session.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 68a6ec0..a31c848 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -11,7 +11,7 @@
#include "util/parse-options.h"
#include "util/trace-event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include <linux/rbtree.h>
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 3e5d0fc..173dd9f 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -11,7 +11,7 @@
#include "util/parse-options.h"
#include "util/trace-event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include <sys/prctl.h>
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index bafb8c5..80327f1 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -10,7 +10,7 @@
#include "util/parse-options.h"
#include "util/trace-event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/session.h"
#include <sys/types.h>
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index be24b54..8679663 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -37,7 +37,7 @@
#include <lk/util.h>
#include "util/strlist.h"
#include "util/symbol.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include <lk/debugfs.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 b92e009..1dee3a0 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -18,7 +18,7 @@
#include "util/header.h"
#include "util/event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/session.h"
#include "util/symbol.h"
#include <lk/cpumap.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 9a618ac..e87180f 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -19,7 +19,7 @@
#include "util/values.h"
#include "perf.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/header.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index c47cf08..6bbc31a 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -11,7 +11,7 @@
#include "util/parse-options.h"
#include "util/trace-event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include <sys/prctl.h>
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 1f60239..b4cc93e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -43,7 +43,7 @@
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/event.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/header.h"
#include <lk/cpumap.h>
#include "util/thread.h"
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 035b9fa..1be7ad9 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -6,7 +6,7 @@
#include "builtin.h"
#include "util/cache.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/parse-options.h"
#include "util/session.h"
#include "util/symbol.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 5440b11..c90a68f 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -15,7 +15,7 @@
#include "builtin.h"
#include <lk/util.h>
-
+#include <lk/debug.h>
#include <lk/color.h>
#include <linux/list.h>
#include "util/cache.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 2b4dce1..e402ba4 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -30,7 +30,7 @@
#include "util/parse-events.h"
#include <lk/cpumap.h>
-#include "util/debug.h"
+#include <lk/debug.h>
#include <assert.h>
#include <fcntl.h>
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 3f9308a..be67b73 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include <lk/util.h>
+#include <lk/debug.h>
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
@@ -61,7 +62,7 @@ static int cleanup_scripting(void)
#include "util/parse-options.h"
#include "perf.h"
-#include "util/debug.h"
+#include <lk/debug.h>
#include "util/trace-event.h"
#include "util/exec_cmd.h"
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 9600a2d..b92b3fd 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -17,6 +17,7 @@
#include "util/parse-events.h"
#include <lk/debugfs.h>
#include <lk/config.h>
+#include <lk/debug.h>
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -24,7 +25,6 @@ const char perf_usage_string[] =
const char perf_more_info_string[] =
"See 'perf help COMMAND' for more information on a specific command.";
-int use_browser = -1;
static int use_pager = -1;
struct pager_config {
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 44f2878..a338745 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -7,6 +7,7 @@
* Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include <lk/util.h>
+#include <lk/debug.h>
#include <lk/config.h>
#include <lk/strbuf.h>
#include <stdio.h>
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index b0902b8..4ae67b4 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -21,8 +21,6 @@
extern void setup_pager(void);
extern const char *pager_program;
-extern int use_browser;
-
#ifdef NO_NEWT_SUPPORT
static inline void setup_browser(void)
{
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index a6c99a3..dcc0254 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -16,6 +16,7 @@
#include <math.h>
#include <lk/util.h>
+#include <lk/debug.h>
#include "callchain.h"
bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
deleted file mode 100644
index 56635de..0000000
--- a/tools/perf/util/debug.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* For general debugging purposes */
-
-#include "../perf.h"
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "cache.h"
-#include "event.h"
-#include "debug.h"
-#include <lk/util.h>
-#include <lk/color.h>
-
-int verbose = 0;
-bool dump_trace = false;
-
-int eprintf(int level, const char *fmt, ...)
-{
- va_list args;
- int ret = 0;
-
- if (verbose >= level) {
- va_start(args, fmt);
- if (use_browser > 0)
- ret = browser__show_help(fmt, args);
- else
- ret = vfprintf(stderr, fmt, args);
- va_end(args);
- }
-
- return ret;
-}
-
-int dump_printf(const char *fmt, ...)
-{
- va_list args;
- int ret = 0;
-
- if (dump_trace) {
- va_start(args, fmt);
- ret = vprintf(fmt, args);
- va_end(args);
- }
-
- return ret;
-}
-
-static int dump_printf_color(const char *fmt, const char *color, ...)
-{
- va_list args;
- int ret = 0;
-
- if (dump_trace) {
- va_start(args, color);
- ret = color_vfprintf(stdout, color, fmt, args);
- va_end(args);
- }
-
- return ret;
-}
-
-
-void trace_event(event_t *event)
-{
- unsigned char *raw_event = (void *)event;
- const char *color = LK_COLOR_BLUE;
- int i, j;
-
- if (!dump_trace)
- return;
-
- dump_printf(".");
- dump_printf_color("\n. ... raw event: size %d bytes\n", color,
- event->header.size);
-
- for (i = 0; i < event->header.size; i++) {
- if ((i & 15) == 0) {
- dump_printf(".");
- dump_printf_color(" %04x: ", color, i);
- }
-
- dump_printf_color(" %02x", color, raw_event[i]);
-
- if (((i & 15) == 15) || i == event->header.size-1) {
- dump_printf_color(" ", color);
- for (j = 0; j < 15-(i & 15); j++)
- dump_printf_color(" ", color);
- for (j = i & ~15; j <= i; j++) {
- dump_printf_color("%c", color,
- isprint(raw_event[j]) ?
- raw_event[j] : '.');
- }
- dump_printf_color("\n", color);
- }
- }
- dump_printf(".\n");
-}
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
deleted file mode 100644
index 047ac33..0000000
--- a/tools/perf/util/debug.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* For debugging general purposes */
-#ifndef __PERF_DEBUG_H
-#define __PERF_DEBUG_H
-
-#include <stdbool.h>
-#include "event.h"
-
-extern int verbose;
-extern bool dump_trace;
-
-int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
-void trace_event(event_t *event);
-
-struct ui_progress;
-
-#ifdef NO_NEWT_SUPPORT
-static inline int browser__show_help(const char *format __used, va_list ap __used)
-{
- return 0;
-}
-
-static inline struct ui_progress *ui_progress__new(const char *title __used,
- u64 total __used)
-{
- return (struct ui_progress *)1;
-}
-
-static inline void ui_progress__update(struct ui_progress *self __used,
- u64 curr __used) {}
-
-static inline void ui_progress__delete(struct ui_progress *self __used) {}
-#else
-int browser__show_help(const char *format, va_list ap);
-struct ui_progress *ui_progress__new(const char *title, u64 total);
-void ui_progress__update(struct ui_progress *self, u64 curr);
-void ui_progress__delete(struct ui_progress *self);
-#endif
-
-#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index a746086..928d3de 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1,6 +1,6 @@
#include <linux/types.h>
#include "event.h"
-#include "debug.h"
+#include <lk/debug.h>
#include "session.h"
#include "sort.h"
#include "string.h"
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 0ebd301..2177cfb 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -14,7 +14,7 @@
#include "trace-event.h"
#include "session.h"
#include "symbol.h"
-#include "debug.h"
+#include <lk/debug.h>
static bool no_buildid_cache = false;
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h
index 791f9dd..8a9ca88 100644
--- a/tools/perf/util/include/linux/compiler.h
+++ b/tools/perf/util/include/linux/compiler.h
@@ -8,5 +8,6 @@
#define __attribute_const__
#define __used __attribute__((__unused__))
+#define __weak __attribute__((weak))
#endif
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
index 1eb804f..614bb72 100644
--- a/tools/perf/util/include/linux/kernel.h
+++ b/tools/perf/util/include/linux/kernel.h
@@ -87,9 +87,6 @@ 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
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index e672f2f..fa82eba 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -5,6 +5,8 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
+#include <lk/debug.h>
+
#include "map.h"
const char *map_type__name[MAP__NR_TYPES] = {
@@ -309,7 +311,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
}
size_t __map_groups__fprintf_maps(struct map_groups *self,
- enum map_type type, int verbose, FILE *fp)
+ enum map_type type, int _verbose, FILE *fp)
{
size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
struct rb_node *nd;
@@ -318,7 +320,7 @@ size_t __map_groups__fprintf_maps(struct map_groups *self,
struct map *pos = rb_entry(nd, struct map, rb_node);
printed += fprintf(fp, "Map:");
printed += map__fprintf(pos, fp);
- if (verbose > 2) {
+ if (_verbose > 2) {
printed += dso__fprintf(pos->dso, type, fp);
printed += fprintf(fp, "--\n");
}
@@ -327,17 +329,17 @@ size_t __map_groups__fprintf_maps(struct map_groups *self,
return printed;
}
-size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp)
+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);
+ 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)
+ int _verbose, FILE *fp)
{
struct map *pos;
size_t printed = 0;
@@ -345,7 +347,7 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
list_for_each_entry(pos, &self->removed_maps[type], node) {
printed += fprintf(fp, "Map:");
printed += map__fprintf(pos, fp);
- if (verbose > 1) {
+ if (_verbose > 1) {
printed += dso__fprintf(pos->dso, type, fp);
printed += fprintf(fp, "--\n");
}
@@ -354,23 +356,23 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
}
static size_t map_groups__fprintf_removed_maps(struct map_groups *self,
- int verbose, FILE *fp)
+ 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);
+ 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 map_groups__fprintf(struct map_groups *self, int _verbose, FILE *fp)
{
- size_t printed = map_groups__fprintf_maps(self, verbose, 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);
+ 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)
+ int _verbose, FILE *fp)
{
struct rb_root *root = &self->maps[map->type];
struct rb_node *next = rb_first(root);
@@ -382,7 +384,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
if (!map__overlap(pos, map))
continue;
- if (verbose >= 2) {
+ if (_verbose >= 2) {
fputs("overlapping maps:\n", fp);
map__fprintf(map, fp);
map__fprintf(pos, fp);
@@ -407,7 +409,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
before->end = map->start - 1;
map_groups__insert(self, before);
- if (verbose >= 2)
+ if (_verbose >= 2)
map__fprintf(before, fp);
}
@@ -419,7 +421,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
after->start = map->end + 1;
map_groups__insert(self, after);
- if (verbose >= 2)
+ if (_verbose >= 2)
map__fprintf(after, fp);
}
}
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index cf182ca..3918f22 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -117,7 +117,7 @@ static void ui_helpline__puts(const char *msg)
static char browser__last_msg[1024];
-int browser__show_help(const char *format, va_list ap)
+int _browser__show_help(const char *format, va_list ap)
{
int ret;
static int backlog;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 3a76c8e..f298a43 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,5 +1,6 @@
#include "../../../include/linux/hw_breakpoint.h"
#include <lk/util.h>
+#include <lk/debug.h>
#include "../perf.h"
#include "parse-options.h"
#include "parse-events.h"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 74efa4a..c33e17d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -37,7 +37,7 @@
#include "event.h"
#include "string.h"
#include "strlist.h"
-#include "debug.h"
+#include <lk/debug.h>
#include "cache.h"
#include <lk/color.h>
#include "symbol.h"
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index eb4eb26..8ac178d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -35,7 +35,7 @@
#include "string.h"
#include "event.h"
-#include "debug.h"
+#include <lk/debug.h>
#include <lk/util.h>
#include "symbol.h"
#include "probe-finder.h"
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 4dd264a..174b9c1 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -15,7 +15,7 @@
#include "values.h"
#include "../perf.h"
-#include "debug.h"
+#include <lk/debug.h>
#include "header.h"
#include "parse-options.h"
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);
-}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 971d0a0..7012560 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -21,6 +21,8 @@
#include <limits.h>
#include <sys/utsname.h>
+#include <lk/debug.h>
+
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index deb30a8..c2652fd 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -5,7 +5,7 @@
#include "session.h"
#include "thread.h"
#include <lk/util.h>
-#include "debug.h"
+#include <lk/debug.h>
int find_all_tid(int pid, pid_t ** all_tid)
{
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 10/21] perf: rewire generic library stuff, p6
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (8 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 09/21] perf: rewire generic library stuff, p5 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 11/21] perf: Export /proc/mounts parser Borislav Petkov
` (10 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out strlist.[ch] and rbtree.c and make them generic.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 6 +
tools/lib/lk/strlist.c | 200 ++++++++++++++++++++++++++++++++++++
tools/lib/lk/strlist.h | 78 ++++++++++++++
tools/perf/Makefile | 6 -
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 | 78 --------------
tools/perf/util/symbol.c | 2 +-
15 files changed, 293 insertions(+), 293 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/Makefile b/tools/lib/Makefile
index df60156..818be02 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -12,6 +12,7 @@ LIB_H += lk/pstack.h
LIB_H += lk/strbuf.h
LIB_H += lk/color.h
LIB_H += lk/debug.h
+LIB_H += lk/strlist.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -26,6 +27,8 @@ LIB_OBJS += $(OUTPUT)lk/color.o
LIB_OBJS += $(OUTPUT)lk/config.o
LIB_OBJS += $(OUTPUT)lk/debug.o
LIB_OBJS += $(OUTPUT)lk/string.o
+LIB_OBJS += $(OUTPUT)lk/rbtree.o
+LIB_OBJS += $(OUTPUT)lk/strlist.o
LIBFILE = lklib.a
@@ -42,6 +45,9 @@ $(LIBFILE): $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H)
+$(OUTPUT)lk/rbtree.o: ../../lib/rbtree.c
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+
$(OUTPUT)%.o: %.c
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
$(OUTPUT)%.s: %.c
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..562eed6
--- /dev/null
+++ b/tools/lib/lk/strlist.h
@@ -0,0 +1,78 @@
+#ifndef __LK_STRLIST_H
+#define __LK_STRLIST_H
+
+#include <linux/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 8770ff4..65a8a7b 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -365,7 +365,6 @@ LIB_H += util/quote.h
LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
-LIB_H += util/strlist.h
LIB_H += util/svghelper.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
@@ -390,10 +389,8 @@ 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/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.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
@@ -921,9 +918,6 @@ $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/newt.o: util/newt.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-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 5621017..3cac2d6 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -12,7 +12,7 @@
#include <lk/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 8679663..f53edba 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -35,7 +35,7 @@
#include "perf.h"
#include "builtin.h"
#include <lk/util.h>
-#include "util/strlist.h"
+#include <lk/strlist.h>
#include "util/symbol.h"
#include <lk/debug.h>
#include <lk/debugfs.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index e87180f..57fe707 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -15,7 +15,7 @@
#include <linux/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 c90a68f..6e2dd8f 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -22,7 +22,7 @@
#include <linux/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 928d3de..fda3406 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 c33e17d..26f29ca 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -36,7 +36,7 @@
#include <lk/util.h>
#include "event.h"
#include "string.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include <lk/debug.h>
#include "cache.h"
#include <lk/color.h>
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e9db1a2..22a6f17 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 174b9c1..7a266b8 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 3ba8390..0000000
--- a/tools/perf/util/strlist.h
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef __PERF_STRLIST_H
-#define __PERF_STRLIST_H
-
-#include <linux/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 7012560..edd8202 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -13,7 +13,7 @@
#include <unistd.h>
#include "build-id.h"
#include "symbol.h"
-#include "strlist.h"
+#include <lk/strlist.h>
#include <libelf.h>
#include <gelf.h>
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 11/21] perf: Export /proc/mounts parser
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (9 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 10/21] perf: rewire generic library stuff, p6 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 12/21] perf: Carve out perf bits for general usage, p1 Borislav Petkov
` (9 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Carve out relevant debugfs setup utils for general use.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/lk/debugfs.c | 25 +++++++++++++++++++++++++
tools/lib/lk/debugfs.h | 5 +++++
tools/perf/perf.c | 27 ++++-----------------------
tools/perf/util/parse-events.c | 2 --
tools/perf/util/parse-events.h | 1 -
5 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c
index bbe3a83..8ef3fa6 100644
--- a/tools/lib/lk/debugfs.c
+++ b/tools/lib/lk/debugfs.c
@@ -3,6 +3,8 @@
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/",
@@ -237,3 +239,26 @@ int debugfs_read(const char *entry, char *buffer, size_t size)
/* return the number of chars read */
return ret;
}
+
+void set_debugfs_path(void)
+{
+ char *path;
+
+ path = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
+ "tracing/events");
+}
+
+/* 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
index 8c02e72..1187fa8 100644
--- a/tools/lib/lk/debugfs.h
+++ b/tools/lib/lk/debugfs.h
@@ -14,6 +14,9 @@
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
+extern char debugfs_path[];
+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);
@@ -23,5 +26,7 @@ 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);
+extern void set_debugfs_path(void);
#endif /* __DEBUGFS_H__ */
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index b92b3fd..58c1a56 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -32,8 +32,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;
@@ -84,15 +82,6 @@ static void commit_pager_choice(void)
}
}
-static void set_debugfs_path(void)
-{
- char *path;
-
- path = getenv(PERF_DEBUGFS_ENVIRONMENT);
- snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
- "tracing/events");
-}
-
static int handle_options(const char ***argv, int *argc, int *envchanged)
{
int handled = 0;
@@ -418,17 +407,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;
@@ -436,8 +414,11 @@ int main(int argc, const char **argv)
cmd = perf_extract_argv0_path(argv[0]);
if (!cmd)
cmd = "perf-help";
+
/* get debugfs mount point from /proc/mounts */
- get_debugfs_mntpt();
+ if (get_debugfs_mntpt())
+ die("Can't establish valid debugfs mountpoint!\n");
+
/*
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
*
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index f298a43..215d4f3 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -29,8 +29,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
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index fc4ab3f..436c831 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -30,7 +30,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);
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 12/21] perf: Carve out perf bits for general usage, p1
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (10 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 11/21] perf: Export /proc/mounts parser Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 13/21] perf: Carve out perf bits for general usage, p2 Borislav Petkov
` (8 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
That is, take perf-specific compilation units used by other tools and
put them in tools/lib/perf/.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 17 +-
tools/lib/Makefile | 4 +
| 1199 ++++++++++++++++++++++++++++++++++++
| 110 ++++
tools/lib/perf/parse-events.c | 957 ++++++++++++++++++++++++++++
tools/lib/perf/parse-events.h | 38 ++
tools/perf/Makefile | 21 +-
tools/perf/bench/mem-memcpy.c | 2 +-
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-buildid-cache.c | 2 +-
tools/perf/builtin-inject.c | 1 +
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-kvm.c | 2 +-
tools/perf/builtin-list.c | 2 +-
tools/perf/builtin-lock.c | 2 +-
tools/perf/builtin-record.c | 4 +-
tools/perf/builtin-report.c | 4 +-
tools/perf/builtin-sched.c | 2 +-
tools/perf/builtin-stat.c | 4 +-
tools/perf/builtin-timechart.c | 4 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 2 +-
tools/perf/perf.c | 2 +-
| 1199 ------------------------------------
| 127 ----
tools/perf/util/parse-events.c | 957 ----------------------------
tools/perf/util/parse-events.h | 36 --
tools/perf/util/session.h | 17 +-
tools/perf/util/sort.h | 4 +-
tools/perf/util/trace-event.h | 2 +-
30 files changed, 2364 insertions(+), 2363 deletions(-)
create mode 100644 tools/lib/perf/header.c
create mode 100644 tools/lib/perf/header.h
create mode 100644 tools/lib/perf/parse-events.c
create mode 100644 tools/lib/perf/parse-events.h
delete mode 100644 tools/perf/util/header.c
delete mode 100644 tools/perf/util/header.h
delete mode 100644 tools/perf/util/parse-events.c
delete mode 100644 tools/perf/util/parse-events.h
diff --git a/tools/Makefile b/tools/Makefile
index e645761..9949133 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -43,7 +43,22 @@ endif
# lib includes for submake
BASIC_CFLAGS = -I$(CURDIR)/lib -I$(CURDIR)/perf -I$(CURDIR)/perf/util/include
-export BASIC_CFLAGS
+ifdef NO_NEWT
+ BASIC_CFLAGS += -DNO_NEWT_SUPPORT
+else
+ FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
+ ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
+ msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
+ BASIC_CFLAGS += -DNO_NEWT_SUPPORT
+ else
+ # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
+ BASIC_CFLAGS += -I/usr/include/slang
+ EXTLIBS += -lnewt -lslang
+ LIB_OBJS += $(OUTPUT)util/newt.o
+ endif
+endif
+
+export BASIC_CFLAGS EXTLIBS
perf: lib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 818be02..ed81953 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -13,6 +13,8 @@ LIB_H += lk/strbuf.h
LIB_H += lk/color.h
LIB_H += lk/debug.h
LIB_H += lk/strlist.h
+LIB_H += perf/parse-events.h
+LIB_H += perf/header.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -29,6 +31,8 @@ LIB_OBJS += $(OUTPUT)lk/debug.o
LIB_OBJS += $(OUTPUT)lk/string.o
LIB_OBJS += $(OUTPUT)lk/rbtree.o
LIB_OBJS += $(OUTPUT)lk/strlist.o
+LIB_OBJS += $(OUTPUT)perf/parse-events.o
+LIB_OBJS += $(OUTPUT)perf/header.o
LIBFILE = lklib.a
--git a/tools/lib/perf/header.c b/tools/lib/perf/header.c
new file mode 100644
index 0000000..6804546
--- /dev/null
+++ b/tools/lib/perf/header.c
@@ -0,0 +1,1199 @@
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <byteswap.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+
+#include <lk/util.h>
+#include "header.h"
+#include <perf.h>
+#include <util/trace-event.h>
+#include <util/session.h>
+#include <util/symbol.h>
+#include <lk/debug.h>
+
+static bool no_buildid_cache = false;
+
+/*
+ * Create new perf.data header attribute:
+ */
+struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
+{
+ struct perf_header_attr *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->attr = *attr;
+ self->ids = 0;
+ self->size = 1;
+ self->id = malloc(sizeof(u64));
+ if (self->id == NULL) {
+ free(self);
+ self = NULL;
+ }
+ }
+
+ return self;
+}
+
+void perf_header_attr__delete(struct perf_header_attr *self)
+{
+ free(self->id);
+ free(self);
+}
+
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
+{
+ int pos = self->ids;
+
+ self->ids++;
+ if (self->ids > self->size) {
+ int nsize = self->size * 2;
+ u64 *nid = realloc(self->id, nsize * sizeof(u64));
+
+ if (nid == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->id = nid;
+ }
+ self->id[pos] = id;
+ return 0;
+}
+
+int perf_header__init(struct perf_header *self)
+{
+ self->size = 1;
+ self->attr = malloc(sizeof(void *));
+ return self->attr == NULL ? -ENOMEM : 0;
+}
+
+void perf_header__exit(struct perf_header *self)
+{
+ int i;
+ for (i = 0; i < self->attrs; ++i)
+ perf_header_attr__delete(self->attr[i]);
+ free(self->attr);
+}
+
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr)
+{
+ if (self->frozen)
+ return -1;
+
+ if (self->attrs == self->size) {
+ int nsize = self->size * 2;
+ struct perf_header_attr **nattr;
+
+ nattr = realloc(self->attr, nsize * sizeof(void *));
+ if (nattr == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->attr = nattr;
+ }
+
+ self->attr[self->attrs++] = attr;
+ return 0;
+}
+
+static int event_count;
+static struct perf_trace_event_type *events;
+
+int perf_header__push_event(u64 id, const char *name)
+{
+ if (strlen(name) > MAX_EVENT_NAME)
+ pr_warning("Event %s will be truncated\n", name);
+
+ if (!events) {
+ events = malloc(sizeof(struct perf_trace_event_type));
+ if (events == NULL)
+ return -ENOMEM;
+ } else {
+ struct perf_trace_event_type *nevents;
+
+ nevents = realloc(events, (event_count + 1) * sizeof(*events));
+ if (nevents == NULL)
+ return -ENOMEM;
+ events = nevents;
+ }
+ memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
+ events[event_count].event_id = id;
+ strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
+ event_count++;
+ return 0;
+}
+
+char *perf_header__find_event(u64 id)
+{
+ int i;
+ for (i = 0 ; i < event_count; i++) {
+ if (events[i].event_id == id)
+ return events[i].name;
+ }
+ return NULL;
+}
+
+static const char *__perf_magic = "PERFFILE";
+
+#define PERF_MAGIC (*(u64 *)__perf_magic)
+
+struct perf_file_attr {
+ struct perf_event_attr attr;
+ struct perf_file_section ids;
+};
+
+void perf_header__set_feat(struct perf_header *self, int feat)
+{
+ set_bit(feat, self->adds_features);
+}
+
+bool perf_header__has_feat(const struct perf_header *self, int feat)
+{
+ return test_bit(feat, self->adds_features);
+}
+
+static int do_write(int fd, const void *buf, size_t size)
+{
+ while (size) {
+ int ret = write(fd, buf, size);
+
+ if (ret < 0)
+ return -errno;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+#define NAME_ALIGN 64
+
+static int write_padded(int fd, const void *bf, size_t count,
+ size_t count_aligned)
+{
+ static const char zero_buf[NAME_ALIGN];
+ int err = do_write(fd, bf, count);
+
+ if (!err)
+ err = do_write(fd, zero_buf, count_aligned - count);
+
+ return err;
+}
+
+#define dsos__for_each_with_build_id(pos, head) \
+ list_for_each_entry(pos, head, node) \
+ if (!pos->has_build_id) \
+ continue; \
+ else
+
+static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
+ u16 misc, int fd)
+{
+ struct dso *pos;
+
+ dsos__for_each_with_build_id(pos, head) {
+ int err;
+ struct build_id_event b;
+ size_t len;
+
+ if (!pos->hit)
+ continue;
+ len = pos->long_name_len + 1;
+ len = ALIGN(len, NAME_ALIGN);
+ memset(&b, 0, sizeof(b));
+ memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
+ b.pid = pid;
+ b.header.misc = misc;
+ b.header.size = sizeof(b) + len;
+ err = do_write(fd, &b, sizeof(b));
+ if (err < 0)
+ return err;
+ err = write_padded(fd, pos->long_name,
+ pos->long_name_len + 1, len);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int machine__write_buildid_table(struct machine *self, int fd)
+{
+ int err;
+ u16 kmisc = PERF_RECORD_MISC_KERNEL,
+ umisc = PERF_RECORD_MISC_USER;
+
+ if (!machine__is_host(self)) {
+ kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+ umisc = PERF_RECORD_MISC_GUEST_USER;
+ }
+
+ err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid,
+ kmisc, fd);
+ if (err == 0)
+ err = __dsos__write_buildid_table(&self->user_dsos,
+ self->pid, umisc, fd);
+ return err;
+}
+
+static int dsos__write_buildid_table(struct perf_header *header, int fd)
+{
+ struct perf_session *session = container_of(header,
+ struct perf_session, header);
+ struct rb_node *nd;
+ int err = machine__write_buildid_table(&session->host_machine, fd);
+
+ if (err)
+ return err;
+
+ for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ err = machine__write_buildid_table(pos, fd);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms)
+{
+ const size_t size = PATH_MAX;
+ char *filename = malloc(size),
+ *linkname = malloc(size), *targetname;
+ int len, err = -1;
+
+ if (filename == NULL || linkname == NULL)
+ goto out_free;
+
+ len = snprintf(filename, size, "%s%s%s",
+ debugdir, is_kallsyms ? "/" : "", name);
+ if (mkdir_p(filename, 0755))
+ goto out_free;
+
+ snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
+
+ if (access(filename, F_OK)) {
+ if (is_kallsyms) {
+ if (copyfile("/proc/kallsyms", filename))
+ goto out_free;
+ } else if (link(name, filename) && copyfile(name, filename))
+ goto out_free;
+ }
+
+ len = snprintf(linkname, size, "%s/.build-id/%.2s",
+ debugdir, sbuild_id);
+
+ if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
+ goto out_free;
+
+ snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
+ targetname = filename + strlen(debugdir) - 5;
+ memcpy(targetname, "../..", 5);
+
+ if (symlink(targetname, linkname) == 0)
+ err = 0;
+out_free:
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
+ const char *name, const char *debugdir,
+ bool is_kallsyms)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(build_id, build_id_size, sbuild_id);
+
+ return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
+}
+
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
+{
+ const size_t size = PATH_MAX;
+ char *filename = malloc(size),
+ *linkname = malloc(size);
+ int err = -1;
+
+ if (filename == NULL || linkname == NULL)
+ goto out_free;
+
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, sbuild_id + 2);
+
+ if (access(linkname, F_OK))
+ goto out_free;
+
+ if (readlink(linkname, filename, size) < 0)
+ goto out_free;
+
+ if (unlink(linkname))
+ goto out_free;
+
+ /*
+ * Since the link is relative, we must make it absolute:
+ */
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, filename);
+
+ if (unlink(linkname))
+ goto out_free;
+
+ err = 0;
+out_free:
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int dso__cache_build_id(struct dso *self, const char *debugdir)
+{
+ bool is_kallsyms = self->kernel && self->long_name[0] != '/';
+
+ return build_id_cache__add_b(self->build_id, sizeof(self->build_id),
+ self->long_name, debugdir, is_kallsyms);
+}
+
+static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+{
+ struct dso *pos;
+ int err = 0;
+
+ dsos__for_each_with_build_id(pos, head)
+ if (dso__cache_build_id(pos, debugdir))
+ err = -1;
+
+ return err;
+}
+
+static int machine__cache_build_ids(struct machine *self, const char *debugdir)
+{
+ int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir);
+ ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir);
+ return ret;
+}
+
+static int perf_session__cache_build_ids(struct perf_session *self)
+{
+ struct rb_node *nd;
+ int ret;
+ char debugdir[PATH_MAX];
+
+ snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+
+ if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
+ return -1;
+
+ ret = machine__cache_build_ids(&self->host_machine, debugdir);
+
+ for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__cache_build_ids(pos, debugdir);
+ }
+ return ret ? -1 : 0;
+}
+
+static bool machine__read_build_ids(struct machine *self, bool with_hits)
+{
+ bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits);
+ ret |= __dsos__read_build_ids(&self->user_dsos, with_hits);
+ return ret;
+}
+
+static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits)
+{
+ struct rb_node *nd;
+ bool ret = machine__read_build_ids(&self->host_machine, with_hits);
+
+ for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__read_build_ids(pos, with_hits);
+ }
+
+ return ret;
+}
+
+static int perf_header__adds_write(struct perf_header *self, int fd)
+{
+ int nr_sections;
+ struct perf_session *session;
+ struct perf_file_section *feat_sec;
+ int sec_size;
+ u64 sec_start;
+ int idx = 0, err;
+
+ session = container_of(self, struct perf_session, header);
+ if (perf_session__read_build_ids(session, true))
+ perf_header__set_feat(self, HEADER_BUILD_ID);
+
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return 0;
+
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (feat_sec == NULL)
+ return -ENOMEM;
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ sec_start = self->data_offset + self->data_size;
+ lseek(fd, sec_start + sec_size, SEEK_SET);
+
+ if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
+ struct perf_file_section *trace_sec;
+
+ trace_sec = &feat_sec[idx++];
+
+ /* Write trace info */
+ trace_sec->offset = lseek(fd, 0, SEEK_CUR);
+ read_tracing_data(fd, attrs, nr_counters);
+ trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
+ }
+
+ if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
+ struct perf_file_section *buildid_sec;
+
+ buildid_sec = &feat_sec[idx++];
+
+ /* Write build-ids */
+ buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
+ err = dsos__write_buildid_table(self, fd);
+ if (err < 0) {
+ pr_debug("failed to write buildid table\n");
+ goto out_free;
+ }
+ buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
+ buildid_sec->offset;
+ if (!no_buildid_cache)
+ perf_session__cache_build_ids(session);
+ }
+
+ lseek(fd, sec_start, SEEK_SET);
+ err = do_write(fd, feat_sec, sec_size);
+ if (err < 0)
+ pr_debug("failed to write feature section\n");
+out_free:
+ free(feat_sec);
+ return err;
+}
+
+int perf_header__write_pipe(int fd)
+{
+ struct perf_pipe_file_header f_header;
+ int err;
+
+ f_header = (struct perf_pipe_file_header){
+ .magic = PERF_MAGIC,
+ .size = sizeof(f_header),
+ };
+
+ err = do_write(fd, &f_header, sizeof(f_header));
+ if (err < 0) {
+ pr_debug("failed to write perf pipe header\n");
+ return err;
+ }
+
+ return 0;
+}
+
+int perf_header__write(struct perf_header *self, int fd, bool at_exit)
+{
+ struct perf_file_header f_header;
+ struct perf_file_attr f_attr;
+ struct perf_header_attr *attr;
+ int i, err;
+
+ lseek(fd, sizeof(f_header), SEEK_SET);
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ attr->id_offset = lseek(fd, 0, SEEK_CUR);
+ err = do_write(fd, attr->id, attr->ids * sizeof(u64));
+ if (err < 0) {
+ pr_debug("failed to write perf header\n");
+ return err;
+ }
+ }
+
+
+ self->attr_offset = lseek(fd, 0, SEEK_CUR);
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ f_attr = (struct perf_file_attr){
+ .attr = attr->attr,
+ .ids = {
+ .offset = attr->id_offset,
+ .size = attr->ids * sizeof(u64),
+ }
+ };
+ err = do_write(fd, &f_attr, sizeof(f_attr));
+ if (err < 0) {
+ pr_debug("failed to write perf header attribute\n");
+ return err;
+ }
+ }
+
+ self->event_offset = lseek(fd, 0, SEEK_CUR);
+ self->event_size = event_count * sizeof(struct perf_trace_event_type);
+ if (events) {
+ err = do_write(fd, events, self->event_size);
+ if (err < 0) {
+ pr_debug("failed to write perf header events\n");
+ return err;
+ }
+ }
+
+ self->data_offset = lseek(fd, 0, SEEK_CUR);
+
+ if (at_exit) {
+ err = perf_header__adds_write(self, fd);
+ if (err < 0)
+ return err;
+ }
+
+ f_header = (struct perf_file_header){
+ .magic = PERF_MAGIC,
+ .size = sizeof(f_header),
+ .attr_size = sizeof(f_attr),
+ .attrs = {
+ .offset = self->attr_offset,
+ .size = self->attrs * sizeof(f_attr),
+ },
+ .data = {
+ .offset = self->data_offset,
+ .size = self->data_size,
+ },
+ .event_types = {
+ .offset = self->event_offset,
+ .size = self->event_size,
+ },
+ };
+
+ memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
+
+ lseek(fd, 0, SEEK_SET);
+ err = do_write(fd, &f_header, sizeof(f_header));
+ if (err < 0) {
+ pr_debug("failed to write perf header\n");
+ return err;
+ }
+ lseek(fd, self->data_offset + self->data_size, SEEK_SET);
+
+ self->frozen = 1;
+ return 0;
+}
+
+static int perf_header__getbuffer64(struct perf_header *self,
+ int fd, void *buf, size_t size)
+{
+ if (do_read(fd, buf, size) <= 0)
+ return -1;
+
+ if (self->needs_swap)
+ mem_bswap_64(buf, size);
+
+ return 0;
+}
+
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd))
+{
+ struct perf_file_section *feat_sec;
+ int nr_sections;
+ int sec_size;
+ int idx = 0;
+ int err = -1, feat = 1;
+
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return 0;
+
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (!feat_sec)
+ return -1;
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ lseek(fd, self->data_offset + self->data_size, SEEK_SET);
+
+ if (perf_header__getbuffer64(self, fd, feat_sec, sec_size))
+ goto out_free;
+
+ err = 0;
+ while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
+ if (perf_header__has_feat(self, feat)) {
+ struct perf_file_section *sec = &feat_sec[idx++];
+
+ err = process(sec, self, feat, fd);
+ if (err < 0)
+ break;
+ }
+ ++feat;
+ }
+out_free:
+ free(feat_sec);
+ return err;
+}
+
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd)
+{
+ lseek(fd, 0, SEEK_SET);
+
+ if (do_read(fd, self, sizeof(*self)) <= 0 ||
+ memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
+ return -1;
+
+ if (self->attr_size != sizeof(struct perf_file_attr)) {
+ u64 attr_size = bswap_64(self->attr_size);
+
+ if (attr_size != sizeof(struct perf_file_attr))
+ return -1;
+
+ mem_bswap_64(self, offsetof(struct perf_file_header,
+ adds_features));
+ ph->needs_swap = true;
+ }
+
+ if (self->size != sizeof(*self)) {
+ /* Support the previous format */
+ if (self->size == offsetof(typeof(*self), adds_features))
+ bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
+ else
+ return -1;
+ }
+
+ memcpy(&ph->adds_features, &self->adds_features,
+ sizeof(ph->adds_features));
+ /*
+ * FIXME: hack that assumes that if we need swap the perf.data file
+ * may be coming from an arch with a different word-size, ergo different
+ * DEFINE_BITMAP format, investigate more later, but for now its mostly
+ * safe to assume that we have a build-id section. Trace files probably
+ * have several other issues in this realm anyway...
+ */
+ if (ph->needs_swap) {
+ memset(&ph->adds_features, 0, sizeof(ph->adds_features));
+ perf_header__set_feat(ph, HEADER_BUILD_ID);
+ }
+
+ ph->event_offset = self->event_types.offset;
+ ph->event_size = self->event_types.size;
+ ph->data_offset = self->data.offset;
+ ph->data_size = self->data.size;
+ return 0;
+}
+
+static int __event_process_build_id(struct build_id_event *bev,
+ char *filename,
+ struct perf_session *session)
+{
+ int err = -1;
+ struct list_head *head;
+ struct machine *machine;
+ u16 misc;
+ struct dso *dso;
+ enum dso_kernel_type dso_type;
+
+ machine = perf_session__findnew_machine(session, bev->pid);
+ if (!machine)
+ goto out;
+
+ misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+ switch (misc) {
+ case PERF_RECORD_MISC_KERNEL:
+ dso_type = DSO_TYPE_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ dso_type = DSO_TYPE_GUEST_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_USER:
+ case PERF_RECORD_MISC_GUEST_USER:
+ dso_type = DSO_TYPE_USER;
+ head = &machine->user_dsos;
+ break;
+ default:
+ goto out;
+ }
+
+ dso = __dsos__findnew(head, filename);
+ if (dso != NULL) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ dso__set_build_id(dso, &bev->build_id);
+
+ if (filename[0] == '[')
+ dso->kernel = dso_type;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id),
+ sbuild_id);
+ pr_debug("build id event received for %s: %s\n",
+ dso->long_name, sbuild_id);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+static int perf_header__read_build_ids(struct perf_header *self,
+ int input, u64 offset, u64 size)
+{
+ struct perf_session *session = container_of(self,
+ struct perf_session, header);
+ struct build_id_event bev;
+ char filename[PATH_MAX];
+ u64 limit = offset + size;
+ int err = -1;
+
+ while (offset < limit) {
+ ssize_t len;
+
+ if (read(input, &bev, sizeof(bev)) != sizeof(bev))
+ goto out;
+
+ if (self->needs_swap)
+ perf_event_header__bswap(&bev.header);
+
+ len = bev.header.size - sizeof(bev);
+ if (read(input, filename, len) != len)
+ goto out;
+
+ __event_process_build_id(&bev, filename, session);
+
+ offset += bev.header.size;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+static int perf_file_section__process(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd)
+{
+ if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) {
+ pr_debug("Failed to lseek to %Ld offset for feature %d, "
+ "continuing...\n", self->offset, feat);
+ return 0;
+ }
+
+ switch (feat) {
+ case HEADER_TRACE_INFO:
+ trace_report(fd, false);
+ break;
+
+ case HEADER_BUILD_ID:
+ if (perf_header__read_build_ids(ph, fd, self->offset, self->size))
+ pr_debug("Failed to read buildids, continuing...\n");
+ break;
+ default:
+ pr_debug("unknown feature %d, continuing...\n", feat);
+ }
+
+ return 0;
+}
+
+static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
+ struct perf_header *ph, int fd,
+ bool repipe)
+{
+ if (do_read(fd, self, sizeof(*self)) <= 0 ||
+ memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
+ return -1;
+
+ if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0)
+ return -1;
+
+ if (self->size != sizeof(*self)) {
+ u64 size = bswap_64(self->size);
+
+ if (size != sizeof(*self))
+ return -1;
+
+ ph->needs_swap = true;
+ }
+
+ return 0;
+}
+
+static int perf_header__read_pipe(struct perf_session *session, int fd)
+{
+ struct perf_header *self = &session->header;
+ struct perf_pipe_file_header f_header;
+
+ if (perf_file_header__read_pipe(&f_header, self, fd,
+ session->repipe) < 0) {
+ pr_debug("incompatible file format\n");
+ return -EINVAL;
+ }
+
+ session->fd = fd;
+
+ return 0;
+}
+
+int perf_header__read(struct perf_session *session, int fd)
+{
+ struct perf_header *self = &session->header;
+ struct perf_file_header f_header;
+ struct perf_file_attr f_attr;
+ u64 f_id;
+ int nr_attrs, nr_ids, i, j;
+
+ if (session->fd_pipe)
+ return perf_header__read_pipe(session, fd);
+
+ if (perf_file_header__read(&f_header, self, fd) < 0) {
+ pr_debug("incompatible file format\n");
+ return -EINVAL;
+ }
+
+ nr_attrs = f_header.attrs.size / sizeof(f_attr);
+ lseek(fd, f_header.attrs.offset, SEEK_SET);
+
+ for (i = 0; i < nr_attrs; i++) {
+ struct perf_header_attr *attr;
+ off_t tmp;
+
+ if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr)))
+ goto out_errno;
+
+ tmp = lseek(fd, 0, SEEK_CUR);
+
+ attr = perf_header_attr__new(&f_attr.attr);
+ if (attr == NULL)
+ return -ENOMEM;
+
+ nr_ids = f_attr.ids.size / sizeof(u64);
+ lseek(fd, f_attr.ids.offset, SEEK_SET);
+
+ for (j = 0; j < nr_ids; j++) {
+ if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id)))
+ goto out_errno;
+
+ if (perf_header_attr__add_id(attr, f_id) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+ }
+ if (perf_header__add_attr(self, attr) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+
+ lseek(fd, tmp, SEEK_SET);
+ }
+
+ if (f_header.event_types.size) {
+ lseek(fd, f_header.event_types.offset, SEEK_SET);
+ events = malloc(f_header.event_types.size);
+ if (events == NULL)
+ return -ENOMEM;
+ if (perf_header__getbuffer64(self, fd, events,
+ f_header.event_types.size))
+ goto out_errno;
+ event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
+ }
+
+ perf_header__process_sections(self, fd, perf_file_section__process);
+
+ lseek(fd, self->data_offset, SEEK_SET);
+
+ self->frozen = 1;
+ return 0;
+out_errno:
+ return -errno;
+}
+
+u64 perf_header__sample_type(struct perf_header *header)
+{
+ u64 type = 0;
+ int i;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+
+ if (!type)
+ type = attr->attr.sample_type;
+ else if (type != attr->attr.sample_type)
+ die("non matching sample_type");
+ }
+
+ return type;
+}
+
+struct perf_event_attr *
+perf_header__find_attr(u64 id, struct perf_header *header)
+{
+ int i;
+
+ /*
+ * We set id to -1 if the data file doesn't contain sample
+ * ids. Check for this and avoid walking through the entire
+ * list of ids which may be large.
+ */
+ if (id == -1ULL)
+ return NULL;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+ int j;
+
+ for (j = 0; j < attr->ids; j++) {
+ if (attr->id[j] == id)
+ return &attr->attr;
+ }
+ }
+
+ return NULL;
+}
+
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t *ev;
+ size_t size;
+ int err;
+
+ size = sizeof(struct perf_event_attr);
+ size = ALIGN(size, sizeof(u64));
+ size += sizeof(struct perf_event_header);
+ size += ids * sizeof(u64);
+
+ ev = malloc(size);
+
+ ev->attr.attr = *attr;
+ memcpy(ev->attr.id, id, ids * sizeof(u64));
+
+ ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
+ ev->attr.header.size = size;
+
+ err = process(ev, session);
+
+ free(ev);
+
+ return err;
+}
+
+int event__synthesize_attrs(struct perf_header *self,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ int i, err = 0;
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header attribute\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_attr(event_t *self, struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ unsigned int i, ids, n_ids;
+
+ attr = perf_header_attr__new(&self->attr.attr);
+ if (attr == NULL)
+ return -ENOMEM;
+
+ ids = self->header.size;
+ ids -= (void *)&self->attr.id - (void *)self;
+ n_ids = ids / sizeof(u64);
+
+ for (i = 0; i < n_ids; i++) {
+ if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+ }
+
+ if (perf_header__add_attr(&session->header, attr) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+
+ perf_session__update_sample_type(session);
+
+ return 0;
+}
+
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t size = 0;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.event_type.event_type.event_id = event_id;
+ memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
+ strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
+
+ ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
+ size = strlen(name);
+ size = ALIGN(size, sizeof(u64));
+ ev.event_type.header.size = sizeof(ev.event_type) -
+ (sizeof(ev.event_type.event_type.name) - size);
+
+ err = process(&ev, session);
+
+ return err;
+}
+
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_trace_event_type *type;
+ int i, err = 0;
+
+ for (i = 0; i < event_count; i++) {
+ type = &events[i];
+
+ err = event__synthesize_event_type(type->event_id, type->name,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header event type\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_event_type(event_t *self,
+ struct perf_session *session __unused)
+{
+ if (perf_header__push_event(self->event_type.event_type.event_id,
+ self->event_type.event_type.name) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
+ int nb_events,
+ event__handler_t process,
+ struct perf_session *session __unused)
+{
+ event_t ev;
+ ssize_t size = 0, aligned_size = 0, padding;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
+ size = read_tracing_data_size(fd, pattrs, nb_events);
+ if (size <= 0)
+ return size;
+ aligned_size = ALIGN(size, sizeof(u64));
+ padding = aligned_size - size;
+ ev.tracing_data.header.size = sizeof(ev.tracing_data);
+ ev.tracing_data.size = aligned_size;
+
+ process(&ev, session);
+
+ err = read_tracing_data(fd, pattrs, nb_events);
+ write_padded(fd, NULL, 0, padding);
+
+ return aligned_size;
+}
+
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session)
+{
+ ssize_t size_read, padding, size = self->tracing_data.size;
+ off_t offset = lseek(session->fd, 0, SEEK_CUR);
+ char buf[BUFSIZ];
+
+ /* setup for reading amidst mmap */
+ lseek(session->fd, offset + sizeof(struct tracing_data_event),
+ SEEK_SET);
+
+ size_read = trace_report(session->fd, session->repipe);
+
+ padding = ALIGN(size_read, sizeof(u64)) - size_read;
+
+ if (read(session->fd, buf, padding) < 0)
+ die("reading input file");
+ if (session->repipe) {
+ int retw = write(STDOUT_FILENO, buf, padding);
+ if (retw <= 0 || retw != padding)
+ die("repiping tracing data padding");
+ }
+
+ if (size_read + padding != size)
+ die("tracing data size mismatch");
+
+ return size_read + padding;
+}
+
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct machine *machine,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t len;
+ int err = 0;
+
+ if (!pos->hit)
+ return err;
+
+ memset(&ev, 0, sizeof(ev));
+
+ len = pos->long_name_len + 1;
+ len = ALIGN(len, NAME_ALIGN);
+ memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
+ ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
+ ev.build_id.header.misc = misc;
+ ev.build_id.pid = machine->pid;
+ ev.build_id.header.size = sizeof(ev.build_id) + len;
+ memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
+
+ err = process(&ev, session);
+
+ return err;
+}
+
+int event__process_build_id(event_t *self,
+ struct perf_session *session)
+{
+ __event_process_build_id(&self->build_id,
+ self->build_id.filename,
+ session);
+ return 0;
+}
+
+void disable_buildid_cache(void)
+{
+ no_buildid_cache = true;
+}
--git a/tools/lib/perf/header.h b/tools/lib/perf/header.h
new file mode 100644
index 0000000..e8cdb86
--- /dev/null
+++ b/tools/lib/perf/header.h
@@ -0,0 +1,110 @@
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+
+#include "../../../include/linux/perf_event.h"
+#include <sys/types.h>
+#include <stdbool.h>
+#include <lk/types.h>
+#include <util/event.h>
+#include <util/session.h>
+
+#include <linux/bitmap.h>
+
+struct perf_header_attr {
+ struct perf_event_attr attr;
+ int ids, size;
+ u64 *id;
+ off_t id_offset;
+};
+
+enum {
+ HEADER_TRACE_INFO = 1,
+ HEADER_BUILD_ID,
+ HEADER_LAST_FEATURE,
+};
+
+struct perf_file_section {
+ u64 offset;
+ u64 size;
+};
+
+struct perf_file_header {
+ u64 magic;
+ u64 size;
+ u64 attr_size;
+ struct perf_file_section attrs;
+ struct perf_file_section data;
+ struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct perf_pipe_file_header {
+ u64 magic;
+ u64 size;
+};
+
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd);
+int perf_header__init(struct perf_header *self);
+void perf_header__exit(struct perf_header *self);
+
+int perf_header__read(struct perf_session *session, int fd);
+int perf_header__write(struct perf_header *self, int fd, bool at_exit);
+int perf_header__write_pipe(int fd);
+
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr);
+
+int perf_header__push_event(u64 id, const char *name);
+char *perf_header__find_event(u64 id);
+
+struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr);
+void perf_header_attr__delete(struct perf_header_attr *self);
+
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
+
+u64 perf_header__sample_type(struct perf_header *header);
+struct perf_event_attr *
+perf_header__find_attr(u64 id, struct perf_header *header);
+void perf_header__set_feat(struct perf_header *self, int feat);
+bool perf_header__has_feat(const struct perf_header *self, int feat);
+
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ struct perf_header *ph,
+ int feat, int fd));
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms);
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_attrs(struct perf_header *self,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_attr(event_t *self, struct perf_session *session);
+
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session);
+int event__process_event_type(event_t *self,
+ struct perf_session *session);
+
+int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
+ int nb_events,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session);
+
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct machine *machine,
+ struct perf_session *session);
+int event__process_build_id(event_t *self, struct perf_session *session);
+
+#endif /* __PERF_HEADER_H */
diff --git a/tools/lib/perf/parse-events.c b/tools/lib/perf/parse-events.c
new file mode 100644
index 0000000..f028838
--- /dev/null
+++ b/tools/lib/perf/parse-events.c
@@ -0,0 +1,957 @@
+#include "../../../include/linux/hw_breakpoint.h"
+#include <lk/util.h>
+#include <lk/debug.h>
+#include <perf.h>
+#include <util/parse-options.h>
+#include "parse-events.h"
+#include <util/exec_cmd.h>
+#include "string.h"
+#include <util/symbol.h>
+#include <util/cache.h>
+#include "header.h"
+#include <lk/debugfs.h>
+
+int nr_counters;
+
+struct perf_event_attr attrs[MAX_COUNTERS];
+char *filters[MAX_COUNTERS];
+
+struct event_symbol {
+ u8 type;
+ u64 config;
+ const char *symbol;
+ const char *alias;
+};
+
+enum event_result {
+ EVT_FAILED,
+ EVT_HANDLED,
+ EVT_HANDLED_ALL
+};
+
+#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
+#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
+
+static struct event_symbol event_symbols[] = {
+ { CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
+ { CHW(INSTRUCTIONS), "instructions", "" },
+ { CHW(CACHE_REFERENCES), "cache-references", "" },
+ { CHW(CACHE_MISSES), "cache-misses", "" },
+ { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
+ { CHW(BRANCH_MISSES), "branch-misses", "" },
+ { CHW(BUS_CYCLES), "bus-cycles", "" },
+
+ { CSW(CPU_CLOCK), "cpu-clock", "" },
+ { CSW(TASK_CLOCK), "task-clock", "" },
+ { CSW(PAGE_FAULTS), "page-faults", "faults" },
+ { CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
+ { CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
+ { CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
+ { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
+ { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
+ { CSW(EMULATION_FAULTS), "emulation-faults", "" },
+};
+
+#define __PERF_EVENT_FIELD(config, name) \
+ ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT)
+
+#define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW)
+#define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG)
+#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
+#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
+
+static const char *hw_event_names[] = {
+ "cycles",
+ "instructions",
+ "cache-references",
+ "cache-misses",
+ "branches",
+ "branch-misses",
+ "bus-cycles",
+};
+
+static const char *sw_event_names[] = {
+ "cpu-clock-msecs",
+ "task-clock-msecs",
+ "page-faults",
+ "context-switches",
+ "CPU-migrations",
+ "minor-faults",
+ "major-faults",
+ "alignment-faults",
+ "emulation-faults",
+};
+
+#define MAX_ALIASES 8
+
+static const char *hw_cache[][MAX_ALIASES] = {
+ { "L1-dcache", "l1-d", "l1d", "L1-data", },
+ { "L1-icache", "l1-i", "l1i", "L1-instruction", },
+ { "LLC", "L2" },
+ { "dTLB", "d-tlb", "Data-TLB", },
+ { "iTLB", "i-tlb", "Instruction-TLB", },
+ { "branch", "branches", "bpu", "btb", "bpc", },
+};
+
+static const char *hw_cache_op[][MAX_ALIASES] = {
+ { "load", "loads", "read", },
+ { "store", "stores", "write", },
+ { "prefetch", "prefetches", "speculative-read", "speculative-load", },
+};
+
+static const char *hw_cache_result[][MAX_ALIASES] = {
+ { "refs", "Reference", "ops", "access", },
+ { "misses", "miss", },
+};
+
+#define C(x) PERF_COUNT_HW_CACHE_##x
+#define CACHE_READ (1 << C(OP_READ))
+#define CACHE_WRITE (1 << C(OP_WRITE))
+#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
+#define COP(x) (1 << x)
+
+/*
+ * cache operartion stat
+ * L1I : Read and prefetch only
+ * ITLB and BPU : Read-only
+ */
+static unsigned long hw_cache_stat[C(MAX)] = {
+ [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
+ [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
+ [C(ITLB)] = (CACHE_READ),
+ [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];
+ struct tracepoint_path *path;
+
+ path = tracepoint_id_to_path(config);
+ if (path) {
+ snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
+ free(path->name);
+ free(path->system);
+ free(path);
+ } else
+ snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
+
+ return buf;
+}
+
+static int is_cache_op_valid(u8 cache_type, u8 cache_op)
+{
+ if (hw_cache_stat[cache_type] & COP(cache_op))
+ return 1; /* valid */
+ else
+ return 0; /* invalid */
+}
+
+static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
+{
+ static char name[50];
+
+ if (cache_result) {
+ sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
+ hw_cache_op[cache_op][0],
+ hw_cache_result[cache_result][0]);
+ } else {
+ sprintf(name, "%s-%s", hw_cache[cache_type][0],
+ hw_cache_op[cache_op][1]);
+ }
+
+ return name;
+}
+
+const char *event_name(int counter)
+{
+ u64 config = attrs[counter].config;
+ int type = attrs[counter].type;
+
+ return __event_name(type, config);
+}
+
+const char *__event_name(int type, u64 config)
+{
+ static char buf[32];
+
+ if (type == PERF_TYPE_RAW) {
+ sprintf(buf, "raw 0x%llx", config);
+ return buf;
+ }
+
+ switch (type) {
+ case PERF_TYPE_HARDWARE:
+ if (config < PERF_COUNT_HW_MAX)
+ return hw_event_names[config];
+ return "unknown-hardware";
+
+ case PERF_TYPE_HW_CACHE: {
+ u8 cache_type, cache_op, cache_result;
+
+ cache_type = (config >> 0) & 0xff;
+ if (cache_type > PERF_COUNT_HW_CACHE_MAX)
+ return "unknown-ext-hardware-cache-type";
+
+ cache_op = (config >> 8) & 0xff;
+ if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
+ return "unknown-ext-hardware-cache-op";
+
+ cache_result = (config >> 16) & 0xff;
+ if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return "unknown-ext-hardware-cache-result";
+
+ if (!is_cache_op_valid(cache_type, cache_op))
+ return "invalid-cache";
+
+ return event_cache_name(cache_type, cache_op, cache_result);
+ }
+
+ case PERF_TYPE_SOFTWARE:
+ if (config < PERF_COUNT_SW_MAX)
+ return sw_event_names[config];
+ return "unknown-software";
+
+ case PERF_TYPE_TRACEPOINT:
+ return tracepoint_id_to_name(config);
+
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
+{
+ int i, j;
+ int n, longest = -1;
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
+ n = strlen(names[i][j]);
+ if (n > longest && !strncasecmp(*str, names[i][j], n))
+ longest = n;
+ }
+ if (longest > 0) {
+ *str += longest;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static enum event_result
+parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
+{
+ const char *s = *str;
+ int cache_type = -1, cache_op = -1, cache_result = -1;
+
+ cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
+ /*
+ * No fallback - if we cannot get a clear cache type
+ * then bail out:
+ */
+ if (cache_type == -1)
+ return EVT_FAILED;
+
+ while ((cache_op == -1 || cache_result == -1) && *s == '-') {
+ ++s;
+
+ if (cache_op == -1) {
+ cache_op = parse_aliases(&s, hw_cache_op,
+ PERF_COUNT_HW_CACHE_OP_MAX);
+ if (cache_op >= 0) {
+ if (!is_cache_op_valid(cache_type, cache_op))
+ return 0;
+ continue;
+ }
+ }
+
+ if (cache_result == -1) {
+ cache_result = parse_aliases(&s, hw_cache_result,
+ PERF_COUNT_HW_CACHE_RESULT_MAX);
+ if (cache_result >= 0)
+ continue;
+ }
+
+ /*
+ * Can't parse this as a cache op or result, so back up
+ * to the '-'.
+ */
+ --s;
+ break;
+ }
+
+ /*
+ * Fall back to reads:
+ */
+ if (cache_op == -1)
+ cache_op = PERF_COUNT_HW_CACHE_OP_READ;
+
+ /*
+ * Fall back to accesses:
+ */
+ if (cache_result == -1)
+ cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
+
+ attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
+ attr->type = PERF_TYPE_HW_CACHE;
+
+ *str = s;
+ return EVT_HANDLED;
+}
+
+static enum event_result
+parse_single_tracepoint_event(char *sys_name,
+ const char *evt_name,
+ unsigned int evt_length,
+ struct perf_event_attr *attr,
+ const char **strp)
+{
+ char evt_path[MAXPATHLEN];
+ char id_buf[4];
+ u64 id;
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_name, evt_name);
+
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return EVT_FAILED;
+
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ return EVT_FAILED;
+ }
+
+ close(fd);
+ id = atoll(id_buf);
+ attr->config = id;
+ attr->type = PERF_TYPE_TRACEPOINT;
+ *strp = evt_name + evt_length;
+
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ attr->sample_type |= PERF_SAMPLE_TIME;
+ attr->sample_type |= PERF_SAMPLE_CPU;
+
+ attr->sample_period = 1;
+
+
+ return EVT_HANDLED;
+}
+
+/* sys + ':' + event + ':' + flags*/
+#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
+static enum event_result
+parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
+ char *flags)
+{
+ char evt_path[MAXPATHLEN];
+ struct dirent *evt_ent;
+ DIR *evt_dir;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
+ evt_dir = opendir(evt_path);
+
+ if (!evt_dir) {
+ perror("Can't open event dir");
+ return EVT_FAILED;
+ }
+
+ while ((evt_ent = readdir(evt_dir))) {
+ char event_opt[MAX_EVOPT_LEN + 1];
+ int len;
+
+ if (!strcmp(evt_ent->d_name, ".")
+ || !strcmp(evt_ent->d_name, "..")
+ || !strcmp(evt_ent->d_name, "enable")
+ || !strcmp(evt_ent->d_name, "filter"))
+ continue;
+
+ if (!strglobmatch(evt_ent->d_name, evt_exp))
+ continue;
+
+ len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
+ evt_ent->d_name, flags ? ":" : "",
+ flags ?: "");
+ if (len < 0)
+ return EVT_FAILED;
+
+ if (parse_events(NULL, event_opt, 0))
+ return EVT_FAILED;
+ }
+
+ return EVT_HANDLED_ALL;
+}
+
+
+static enum event_result parse_tracepoint_event(const char **strp,
+ struct perf_event_attr *attr)
+{
+ const char *evt_name;
+ char *flags;
+ char sys_name[MAX_EVENT_LENGTH];
+ unsigned int sys_length, evt_length;
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return 0;
+
+ evt_name = strchr(*strp, ':');
+ if (!evt_name)
+ return EVT_FAILED;
+
+ sys_length = evt_name - *strp;
+ if (sys_length >= MAX_EVENT_LENGTH)
+ return 0;
+
+ strncpy(sys_name, *strp, sys_length);
+ sys_name[sys_length] = '\0';
+ evt_name = evt_name + 1;
+
+ flags = strchr(evt_name, ':');
+ if (flags) {
+ /* split it out: */
+ evt_name = strndup(evt_name, flags - evt_name);
+ flags++;
+ }
+
+ evt_length = strlen(evt_name);
+ if (evt_length >= MAX_EVENT_LENGTH)
+ return EVT_FAILED;
+
+ if (strpbrk(evt_name, "*?")) {
+ *strp = evt_name + evt_length;
+ return parse_multiple_tracepoint_event(sys_name, evt_name,
+ flags);
+ } else
+ return parse_single_tracepoint_event(sys_name, evt_name,
+ evt_length, attr, strp);
+}
+
+static enum event_result
+parse_breakpoint_type(const char *type, const char **strp,
+ struct perf_event_attr *attr)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (!type[i])
+ break;
+
+ switch (type[i]) {
+ case 'r':
+ attr->bp_type |= HW_BREAKPOINT_R;
+ break;
+ case 'w':
+ attr->bp_type |= HW_BREAKPOINT_W;
+ break;
+ case 'x':
+ attr->bp_type |= HW_BREAKPOINT_X;
+ break;
+ default:
+ return EVT_FAILED;
+ }
+ }
+ if (!attr->bp_type) /* Default */
+ attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
+
+ *strp = type + i;
+
+ return EVT_HANDLED;
+}
+
+static enum event_result
+parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *target;
+ const char *type;
+ char *endaddr;
+ u64 addr;
+ enum event_result err;
+
+ target = strchr(*strp, ':');
+ if (!target)
+ return EVT_FAILED;
+
+ if (strncmp(*strp, "mem", target - *strp) != 0)
+ return EVT_FAILED;
+
+ target++;
+
+ addr = strtoull(target, &endaddr, 0);
+ if (target == endaddr)
+ return EVT_FAILED;
+
+ attr->bp_addr = addr;
+ *strp = endaddr;
+
+ type = strchr(target, ':');
+
+ /* If no type is defined, just rw as default */
+ if (!type) {
+ attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
+ } else {
+ err = parse_breakpoint_type(++type, strp, attr);
+ if (err == EVT_FAILED)
+ return EVT_FAILED;
+ }
+
+ /* We should find a nice way to override the access type */
+ attr->bp_len = HW_BREAKPOINT_LEN_4;
+ attr->type = PERF_TYPE_BREAKPOINT;
+
+ return EVT_HANDLED;
+}
+
+static int check_events(const char *str, unsigned int i)
+{
+ int n;
+
+ n = strlen(event_symbols[i].symbol);
+ if (!strncmp(str, event_symbols[i].symbol, n))
+ return n;
+
+ n = strlen(event_symbols[i].alias);
+ if (n)
+ if (!strncmp(str, event_symbols[i].alias, n))
+ return n;
+ return 0;
+}
+
+static enum event_result
+parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ unsigned int i;
+ int n;
+
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+ n = check_events(str, i);
+ if (n > 0) {
+ attr->type = event_symbols[i].type;
+ attr->config = event_symbols[i].config;
+ *strp = str + n;
+ return EVT_HANDLED;
+ }
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_raw_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ u64 config;
+ int n;
+
+ if (*str != 'r')
+ return EVT_FAILED;
+ n = hex2u64(str + 1, &config);
+ if (n > 0) {
+ *strp = str + n + 1;
+ attr->type = PERF_TYPE_RAW;
+ attr->config = config;
+ return EVT_HANDLED;
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_numeric_event(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ char *endp;
+ unsigned long type;
+ u64 config;
+
+ type = strtoul(str, &endp, 0);
+ if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
+ str = endp + 1;
+ config = strtoul(str, &endp, 0);
+ if (endp > str) {
+ attr->type = type;
+ attr->config = config;
+ *strp = endp;
+ return EVT_HANDLED;
+ }
+ }
+ return EVT_FAILED;
+}
+
+static enum event_result
+parse_event_modifier(const char **strp, struct perf_event_attr *attr)
+{
+ const char *str = *strp;
+ int exclude = 0;
+ int eu = 0, ek = 0, eh = 0, precise = 0;
+
+ if (*str++ != ':')
+ return 0;
+ while (*str) {
+ if (*str == 'u') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ eu = 0;
+ } else if (*str == 'k') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ ek = 0;
+ } else if (*str == 'h') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
+ eh = 0;
+ } else if (*str == 'p') {
+ precise++;
+ } else
+ break;
+
+ ++str;
+ }
+ if (str >= *strp + 2) {
+ *strp = str;
+ attr->exclude_user = eu;
+ attr->exclude_kernel = ek;
+ attr->exclude_hv = eh;
+ attr->precise_ip = precise;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static enum event_result
+parse_event_symbols(const char **str, struct perf_event_attr *attr)
+{
+ enum event_result ret;
+
+ ret = parse_tracepoint_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_raw_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_numeric_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_symbolic_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_generic_hw_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ ret = parse_breakpoint_event(str, attr);
+ if (ret != EVT_FAILED)
+ goto modifier;
+
+ fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
+ return EVT_FAILED;
+
+modifier:
+ parse_event_modifier(str, attr);
+
+ return ret;
+}
+
+static int store_event_type(const char *orgname)
+{
+ char filename[PATH_MAX], *c;
+ FILE *file;
+ int id, n;
+
+ sprintf(filename, "%s/", debugfs_path);
+ strncat(filename, orgname, strlen(orgname));
+ strcat(filename, "/id");
+
+ c = strchr(filename, ':');
+ if (c)
+ *c = '/';
+
+ file = fopen(filename, "r");
+ if (!file)
+ return 0;
+ n = fscanf(file, "%i", &id);
+ fclose(file);
+ if (n < 1) {
+ pr_err("cannot store event ID\n");
+ return -EINVAL;
+ }
+ return perf_header__push_event(id, orgname);
+}
+
+int parse_events(const struct option *opt __used, const char *str, int unset __used)
+{
+ struct perf_event_attr attr;
+ enum event_result ret;
+
+ if (strchr(str, ':'))
+ if (store_event_type(str) < 0)
+ return -1;
+
+ for (;;) {
+ if (nr_counters == MAX_COUNTERS)
+ return -1;
+
+ memset(&attr, 0, sizeof(attr));
+ ret = parse_event_symbols(&str, &attr);
+ if (ret == EVT_FAILED)
+ return -1;
+
+ if (!(*str == 0 || *str == ',' || isspace(*str)))
+ return -1;
+
+ if (ret != EVT_HANDLED_ALL) {
+ attrs[nr_counters] = attr;
+ nr_counters++;
+ }
+
+ if (*str == 0)
+ break;
+ if (*str == ',')
+ ++str;
+ while (isspace(*str))
+ ++str;
+ }
+
+ return 0;
+}
+
+int parse_filter(const struct option *opt __used, const char *str,
+ int unset __used)
+{
+ int i = nr_counters - 1;
+ int len = strlen(str);
+
+ if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) {
+ fprintf(stderr,
+ "-F option should follow a -e tracepoint option\n");
+ return -1;
+ }
+
+ filters[i] = malloc(len + 1);
+ if (!filters[i]) {
+ fprintf(stderr, "not enough memory to hold filter string\n");
+ return -1;
+ }
+ strcpy(filters[i], str);
+
+ return 0;
+}
+
+static const char * const event_type_descriptors[] = {
+ "Hardware event",
+ "Software event",
+ "Tracepoint event",
+ "Hardware cache event",
+ "Raw hardware event descriptor",
+ "Hardware breakpoint",
+};
+
+/*
+ * Print the events from <debugfs_mount_point>/tracing/events
+ */
+
+static void print_tracepoint_events(void)
+{
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ char evt_path[MAXPATHLEN];
+ char dir_path[MAXPATHLEN];
+
+ if (debugfs_valid_mountpoint(debugfs_path))
+ return;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ return;
+
+ 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",
+ sys_dirent.d_name, evt_dirent.d_name);
+ printf(" %-42s [%s]\n", evt_path,
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ }
+ closedir(evt_dir);
+ }
+ closedir(sys_dir);
+}
+
+/*
+ * Print the help text for the event symbols:
+ */
+void print_events(void)
+{
+ struct event_symbol *syms = event_symbols;
+ unsigned int i, type, op, prev_type = -1;
+ char name[40];
+
+ printf("\n");
+ printf("List of pre-defined events (to be used in -e):\n");
+
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
+ type = syms->type;
+
+ if (type != prev_type)
+ printf("\n");
+
+ if (strlen(syms->alias))
+ sprintf(name, "%s OR %s", syms->symbol, syms->alias);
+ else
+ strcpy(name, syms->symbol);
+ printf(" %-42s [%s]\n", name,
+ event_type_descriptors[type]);
+
+ prev_type = type;
+ }
+
+ printf("\n");
+ for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
+ for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
+ /* skip invalid cache type */
+ if (!is_cache_op_valid(type, op))
+ continue;
+
+ for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
+ printf(" %-42s [%s]\n",
+ event_cache_name(type, op, i),
+ event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ }
+ }
+ }
+
+ printf("\n");
+ printf(" %-42s [%s]\n",
+ "rNNN (see 'perf list --help' on how to encode it)",
+ event_type_descriptors[PERF_TYPE_RAW]);
+ printf("\n");
+
+ printf(" %-42s [%s]\n",
+ "mem:<addr>[:access]",
+ event_type_descriptors[PERF_TYPE_BREAKPOINT]);
+ printf("\n");
+
+ print_tracepoint_events();
+
+ exit(129);
+}
diff --git a/tools/lib/perf/parse-events.h b/tools/lib/perf/parse-events.h
new file mode 100644
index 0000000..8c55d6c
--- /dev/null
+++ b/tools/lib/perf/parse-events.h
@@ -0,0 +1,38 @@
+#ifndef __PERF_PARSE_EVENTS_H
+#define __PERF_PARSE_EVENTS_H
+/*
+ * Parse symbolic events/counts passed in as options:
+ */
+#include "../../../include/linux/perf_event.h"
+#include <perf.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;
+
+extern struct perf_event_attr attrs[MAX_COUNTERS];
+extern char *filters[MAX_COUNTERS];
+
+extern const char *event_name(int ctr);
+extern const char *__event_name(int type, u64 config);
+
+extern int parse_events(const struct option *opt, const char *str, int unset);
+extern int parse_filter(const struct option *opt, const char *str, int unset);
+
+#define EVENTS_HELP_MAX (128*1024)
+
+extern void print_events(void);
+
+extern int valid_debugfs_mount(const char *debugfs);
+
+
+#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 65a8a7b..0a5b00f 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -197,7 +197,7 @@ ifndef PERF_DEBUG
endif
CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
-EXTLIBS = -lpthread -lrt -lelf -lm
+EXTLIBS += -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ALL_LDFLAGS = $(LDFLAGS)
STRIP ?= strip
@@ -360,9 +360,7 @@ 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
-LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
LIB_H += util/svghelper.h
@@ -387,7 +385,6 @@ LIB_OBJS += $(OUTPUT)util/exec_cmd.o
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
@@ -395,7 +392,6 @@ 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/map.o
@@ -508,21 +504,6 @@ else
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
-ifdef NO_NEWT
- BASIC_CFLAGS += -DNO_NEWT_SUPPORT
-else
- FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
- ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
- msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
- BASIC_CFLAGS += -DNO_NEWT_SUPPORT
- else
- # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
- BASIC_CFLAGS += -I/usr/include/slang
- EXTLIBS += -lnewt -lslang
- LIB_OBJS += $(OUTPUT)util/newt.o
- endif
-endif
-
ifdef NO_LIBPERL
BASIC_CFLAGS += -DNO_LIBPERL
else
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index 86db09e..4bda7d1 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -10,7 +10,7 @@
#include "../perf.h"
#include <lk/util.h>
#include "../util/parse-options.h"
-#include "../util/header.h"
+#include <perf/header.h>
#include "bench.h"
#include <stdio.h>
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 04afd8a..a4d9620 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -20,7 +20,7 @@
#include "util/event.h"
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 3cac2d6..5fe42b6 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -10,7 +10,7 @@
#include "perf.h"
#include "util/cache.h"
#include <lk/debug.h>
-#include "util/header.h"
+#include <perf/header.h>
#include "util/parse-options.h"
#include <lk/strlist.h>
#include "util/symbol.h"
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index c9127a9..a6c3caa 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -10,6 +10,7 @@
#include "perf.h"
#include "util/session.h"
#include <lk/debug.h>
+#include <perf/header.h>
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index a31c848..6003678 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -5,7 +5,7 @@
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/session.h"
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 173dd9f..1a110fa 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -5,7 +5,7 @@
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/session.h"
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index d88c696..c1a802b 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -10,7 +10,7 @@
#include "perf.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include "util/cache.h"
int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 80327f1..dc229ab 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -5,7 +5,7 @@
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/parse-options.h"
#include "util/trace-event.h"
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 1dee3a0..34bc049 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -14,9 +14,9 @@
#include "util/build-id.h"
#include <lk/util.h>
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
-#include "util/header.h"
+#include <perf/header.h>
#include "util/event.h"
#include <lk/debug.h>
#include "util/session.h"
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 57fe707..266f721 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -20,11 +20,11 @@
#include "perf.h"
#include <lk/debug.h>
-#include "util/header.h"
+#include <perf/header.h>
#include "util/session.h"
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include "util/thread.h"
#include "util/sort.h"
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 6bbc31a..6af08bf 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -5,7 +5,7 @@
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/session.h"
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index b4cc93e..56c47bc 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -41,10 +41,10 @@
#include "builtin.h"
#include <lk/util.h>
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include "util/event.h"
#include <lk/debug.h>
-#include "util/header.h"
+#include <perf/header.h>
#include <lk/cpumap.h>
#include "util/thread.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 6e2dd8f..1699cf6 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -25,9 +25,9 @@
#include <lk/strlist.h>
#include "perf.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include "util/event.h"
#include "util/session.h"
#include "util/svghelper.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index e402ba4..e4a5783 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -27,7 +27,7 @@
#include <lk/util.h>
#include <linux/rbtree.h>
#include "util/parse-options.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include <lk/cpumap.h>
#include <lk/debug.h>
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index be67b73..6c3bc42 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -5,7 +5,7 @@
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
-#include "util/header.h"
+#include <perf/header.h>
#include "util/exec_cmd.h"
#include "util/trace-event.h"
#include "util/session.h"
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 58c1a56..4d57b28 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -14,7 +14,7 @@
#include "util/quote.h"
#include "util/build-id.h"
#include "util/run-command.h"
-#include "util/parse-events.h"
+#include <perf/parse-events.h>
#include <lk/debugfs.h>
#include <lk/config.h>
#include <lk/debug.h>
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
deleted file mode 100644
index 2177cfb..0000000
--- a/tools/perf/util/header.c
+++ /dev/null
@@ -1,1199 +0,0 @@
-#define _FILE_OFFSET_BITS 64
-
-#include <sys/types.h>
-#include <byteswap.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <linux/list.h>
-#include <linux/kernel.h>
-
-#include <lk/util.h>
-#include "header.h"
-#include "../perf.h"
-#include "trace-event.h"
-#include "session.h"
-#include "symbol.h"
-#include <lk/debug.h>
-
-static bool no_buildid_cache = false;
-
-/*
- * Create new perf.data header attribute:
- */
-struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
-{
- struct perf_header_attr *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- self->attr = *attr;
- self->ids = 0;
- self->size = 1;
- self->id = malloc(sizeof(u64));
- if (self->id == NULL) {
- free(self);
- self = NULL;
- }
- }
-
- return self;
-}
-
-void perf_header_attr__delete(struct perf_header_attr *self)
-{
- free(self->id);
- free(self);
-}
-
-int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
-{
- int pos = self->ids;
-
- self->ids++;
- if (self->ids > self->size) {
- int nsize = self->size * 2;
- u64 *nid = realloc(self->id, nsize * sizeof(u64));
-
- if (nid == NULL)
- return -1;
-
- self->size = nsize;
- self->id = nid;
- }
- self->id[pos] = id;
- return 0;
-}
-
-int perf_header__init(struct perf_header *self)
-{
- self->size = 1;
- self->attr = malloc(sizeof(void *));
- return self->attr == NULL ? -ENOMEM : 0;
-}
-
-void perf_header__exit(struct perf_header *self)
-{
- int i;
- for (i = 0; i < self->attrs; ++i)
- perf_header_attr__delete(self->attr[i]);
- free(self->attr);
-}
-
-int perf_header__add_attr(struct perf_header *self,
- struct perf_header_attr *attr)
-{
- if (self->frozen)
- return -1;
-
- if (self->attrs == self->size) {
- int nsize = self->size * 2;
- struct perf_header_attr **nattr;
-
- nattr = realloc(self->attr, nsize * sizeof(void *));
- if (nattr == NULL)
- return -1;
-
- self->size = nsize;
- self->attr = nattr;
- }
-
- self->attr[self->attrs++] = attr;
- return 0;
-}
-
-static int event_count;
-static struct perf_trace_event_type *events;
-
-int perf_header__push_event(u64 id, const char *name)
-{
- if (strlen(name) > MAX_EVENT_NAME)
- pr_warning("Event %s will be truncated\n", name);
-
- if (!events) {
- events = malloc(sizeof(struct perf_trace_event_type));
- if (events == NULL)
- return -ENOMEM;
- } else {
- struct perf_trace_event_type *nevents;
-
- nevents = realloc(events, (event_count + 1) * sizeof(*events));
- if (nevents == NULL)
- return -ENOMEM;
- events = nevents;
- }
- memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
- events[event_count].event_id = id;
- strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
- event_count++;
- return 0;
-}
-
-char *perf_header__find_event(u64 id)
-{
- int i;
- for (i = 0 ; i < event_count; i++) {
- if (events[i].event_id == id)
- return events[i].name;
- }
- return NULL;
-}
-
-static const char *__perf_magic = "PERFFILE";
-
-#define PERF_MAGIC (*(u64 *)__perf_magic)
-
-struct perf_file_attr {
- struct perf_event_attr attr;
- struct perf_file_section ids;
-};
-
-void perf_header__set_feat(struct perf_header *self, int feat)
-{
- set_bit(feat, self->adds_features);
-}
-
-bool perf_header__has_feat(const struct perf_header *self, int feat)
-{
- return test_bit(feat, self->adds_features);
-}
-
-static int do_write(int fd, const void *buf, size_t size)
-{
- while (size) {
- int ret = write(fd, buf, size);
-
- if (ret < 0)
- return -errno;
-
- size -= ret;
- buf += ret;
- }
-
- return 0;
-}
-
-#define NAME_ALIGN 64
-
-static int write_padded(int fd, const void *bf, size_t count,
- size_t count_aligned)
-{
- static const char zero_buf[NAME_ALIGN];
- int err = do_write(fd, bf, count);
-
- if (!err)
- err = do_write(fd, zero_buf, count_aligned - count);
-
- return err;
-}
-
-#define dsos__for_each_with_build_id(pos, head) \
- list_for_each_entry(pos, head, node) \
- if (!pos->has_build_id) \
- continue; \
- else
-
-static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
- u16 misc, int fd)
-{
- struct dso *pos;
-
- dsos__for_each_with_build_id(pos, head) {
- int err;
- struct build_id_event b;
- size_t len;
-
- if (!pos->hit)
- continue;
- len = pos->long_name_len + 1;
- len = ALIGN(len, NAME_ALIGN);
- memset(&b, 0, sizeof(b));
- memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
- b.pid = pid;
- b.header.misc = misc;
- b.header.size = sizeof(b) + len;
- err = do_write(fd, &b, sizeof(b));
- if (err < 0)
- return err;
- err = write_padded(fd, pos->long_name,
- pos->long_name_len + 1, len);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int machine__write_buildid_table(struct machine *self, int fd)
-{
- int err;
- u16 kmisc = PERF_RECORD_MISC_KERNEL,
- umisc = PERF_RECORD_MISC_USER;
-
- if (!machine__is_host(self)) {
- kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
- umisc = PERF_RECORD_MISC_GUEST_USER;
- }
-
- err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid,
- kmisc, fd);
- if (err == 0)
- err = __dsos__write_buildid_table(&self->user_dsos,
- self->pid, umisc, fd);
- return err;
-}
-
-static int dsos__write_buildid_table(struct perf_header *header, int fd)
-{
- struct perf_session *session = container_of(header,
- struct perf_session, header);
- struct rb_node *nd;
- int err = machine__write_buildid_table(&session->host_machine, fd);
-
- if (err)
- return err;
-
- for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- err = machine__write_buildid_table(pos, fd);
- if (err)
- break;
- }
- return err;
-}
-
-int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms)
-{
- const size_t size = PATH_MAX;
- char *filename = malloc(size),
- *linkname = malloc(size), *targetname;
- int len, err = -1;
-
- if (filename == NULL || linkname == NULL)
- goto out_free;
-
- len = snprintf(filename, size, "%s%s%s",
- debugdir, is_kallsyms ? "/" : "", name);
- if (mkdir_p(filename, 0755))
- goto out_free;
-
- snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
-
- if (access(filename, F_OK)) {
- if (is_kallsyms) {
- if (copyfile("/proc/kallsyms", filename))
- goto out_free;
- } else if (link(name, filename) && copyfile(name, filename))
- goto out_free;
- }
-
- len = snprintf(linkname, size, "%s/.build-id/%.2s",
- debugdir, sbuild_id);
-
- if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
- goto out_free;
-
- snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
- targetname = filename + strlen(debugdir) - 5;
- memcpy(targetname, "../..", 5);
-
- if (symlink(targetname, linkname) == 0)
- err = 0;
-out_free:
- free(filename);
- free(linkname);
- return err;
-}
-
-static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
- const char *name, const char *debugdir,
- bool is_kallsyms)
-{
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(build_id, build_id_size, sbuild_id);
-
- return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
-}
-
-int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
-{
- const size_t size = PATH_MAX;
- char *filename = malloc(size),
- *linkname = malloc(size);
- int err = -1;
-
- if (filename == NULL || linkname == NULL)
- goto out_free;
-
- snprintf(linkname, size, "%s/.build-id/%.2s/%s",
- debugdir, sbuild_id, sbuild_id + 2);
-
- if (access(linkname, F_OK))
- goto out_free;
-
- if (readlink(linkname, filename, size) < 0)
- goto out_free;
-
- if (unlink(linkname))
- goto out_free;
-
- /*
- * Since the link is relative, we must make it absolute:
- */
- snprintf(linkname, size, "%s/.build-id/%.2s/%s",
- debugdir, sbuild_id, filename);
-
- if (unlink(linkname))
- goto out_free;
-
- err = 0;
-out_free:
- free(filename);
- free(linkname);
- return err;
-}
-
-static int dso__cache_build_id(struct dso *self, const char *debugdir)
-{
- bool is_kallsyms = self->kernel && self->long_name[0] != '/';
-
- return build_id_cache__add_b(self->build_id, sizeof(self->build_id),
- self->long_name, debugdir, is_kallsyms);
-}
-
-static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
-{
- struct dso *pos;
- int err = 0;
-
- dsos__for_each_with_build_id(pos, head)
- if (dso__cache_build_id(pos, debugdir))
- err = -1;
-
- return err;
-}
-
-static int machine__cache_build_ids(struct machine *self, const char *debugdir)
-{
- int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir);
- ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir);
- return ret;
-}
-
-static int perf_session__cache_build_ids(struct perf_session *self)
-{
- struct rb_node *nd;
- int ret;
- char debugdir[PATH_MAX];
-
- snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
-
- if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
- return -1;
-
- ret = machine__cache_build_ids(&self->host_machine, debugdir);
-
- for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret |= machine__cache_build_ids(pos, debugdir);
- }
- return ret ? -1 : 0;
-}
-
-static bool machine__read_build_ids(struct machine *self, bool with_hits)
-{
- bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits);
- ret |= __dsos__read_build_ids(&self->user_dsos, with_hits);
- return ret;
-}
-
-static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits)
-{
- struct rb_node *nd;
- bool ret = machine__read_build_ids(&self->host_machine, with_hits);
-
- for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret |= machine__read_build_ids(pos, with_hits);
- }
-
- return ret;
-}
-
-static int perf_header__adds_write(struct perf_header *self, int fd)
-{
- int nr_sections;
- struct perf_session *session;
- struct perf_file_section *feat_sec;
- int sec_size;
- u64 sec_start;
- int idx = 0, err;
-
- session = container_of(self, struct perf_session, header);
- if (perf_session__read_build_ids(session, true))
- perf_header__set_feat(self, HEADER_BUILD_ID);
-
- nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
- if (!nr_sections)
- return 0;
-
- feat_sec = calloc(sizeof(*feat_sec), nr_sections);
- if (feat_sec == NULL)
- return -ENOMEM;
-
- sec_size = sizeof(*feat_sec) * nr_sections;
-
- sec_start = self->data_offset + self->data_size;
- lseek(fd, sec_start + sec_size, SEEK_SET);
-
- if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
- struct perf_file_section *trace_sec;
-
- trace_sec = &feat_sec[idx++];
-
- /* Write trace info */
- trace_sec->offset = lseek(fd, 0, SEEK_CUR);
- read_tracing_data(fd, attrs, nr_counters);
- trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
- }
-
- if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
- struct perf_file_section *buildid_sec;
-
- buildid_sec = &feat_sec[idx++];
-
- /* Write build-ids */
- buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
- err = dsos__write_buildid_table(self, fd);
- if (err < 0) {
- pr_debug("failed to write buildid table\n");
- goto out_free;
- }
- buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
- buildid_sec->offset;
- if (!no_buildid_cache)
- perf_session__cache_build_ids(session);
- }
-
- lseek(fd, sec_start, SEEK_SET);
- err = do_write(fd, feat_sec, sec_size);
- if (err < 0)
- pr_debug("failed to write feature section\n");
-out_free:
- free(feat_sec);
- return err;
-}
-
-int perf_header__write_pipe(int fd)
-{
- struct perf_pipe_file_header f_header;
- int err;
-
- f_header = (struct perf_pipe_file_header){
- .magic = PERF_MAGIC,
- .size = sizeof(f_header),
- };
-
- err = do_write(fd, &f_header, sizeof(f_header));
- if (err < 0) {
- pr_debug("failed to write perf pipe header\n");
- return err;
- }
-
- return 0;
-}
-
-int perf_header__write(struct perf_header *self, int fd, bool at_exit)
-{
- struct perf_file_header f_header;
- struct perf_file_attr f_attr;
- struct perf_header_attr *attr;
- int i, err;
-
- lseek(fd, sizeof(f_header), SEEK_SET);
-
- for (i = 0; i < self->attrs; i++) {
- attr = self->attr[i];
-
- attr->id_offset = lseek(fd, 0, SEEK_CUR);
- err = do_write(fd, attr->id, attr->ids * sizeof(u64));
- if (err < 0) {
- pr_debug("failed to write perf header\n");
- return err;
- }
- }
-
-
- self->attr_offset = lseek(fd, 0, SEEK_CUR);
-
- for (i = 0; i < self->attrs; i++) {
- attr = self->attr[i];
-
- f_attr = (struct perf_file_attr){
- .attr = attr->attr,
- .ids = {
- .offset = attr->id_offset,
- .size = attr->ids * sizeof(u64),
- }
- };
- err = do_write(fd, &f_attr, sizeof(f_attr));
- if (err < 0) {
- pr_debug("failed to write perf header attribute\n");
- return err;
- }
- }
-
- self->event_offset = lseek(fd, 0, SEEK_CUR);
- self->event_size = event_count * sizeof(struct perf_trace_event_type);
- if (events) {
- err = do_write(fd, events, self->event_size);
- if (err < 0) {
- pr_debug("failed to write perf header events\n");
- return err;
- }
- }
-
- self->data_offset = lseek(fd, 0, SEEK_CUR);
-
- if (at_exit) {
- err = perf_header__adds_write(self, fd);
- if (err < 0)
- return err;
- }
-
- f_header = (struct perf_file_header){
- .magic = PERF_MAGIC,
- .size = sizeof(f_header),
- .attr_size = sizeof(f_attr),
- .attrs = {
- .offset = self->attr_offset,
- .size = self->attrs * sizeof(f_attr),
- },
- .data = {
- .offset = self->data_offset,
- .size = self->data_size,
- },
- .event_types = {
- .offset = self->event_offset,
- .size = self->event_size,
- },
- };
-
- memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
-
- lseek(fd, 0, SEEK_SET);
- err = do_write(fd, &f_header, sizeof(f_header));
- if (err < 0) {
- pr_debug("failed to write perf header\n");
- return err;
- }
- lseek(fd, self->data_offset + self->data_size, SEEK_SET);
-
- self->frozen = 1;
- return 0;
-}
-
-static int perf_header__getbuffer64(struct perf_header *self,
- int fd, void *buf, size_t size)
-{
- if (do_read(fd, buf, size) <= 0)
- return -1;
-
- if (self->needs_swap)
- mem_bswap_64(buf, size);
-
- return 0;
-}
-
-int perf_header__process_sections(struct perf_header *self, int fd,
- int (*process)(struct perf_file_section *self,
- struct perf_header *ph,
- int feat, int fd))
-{
- struct perf_file_section *feat_sec;
- int nr_sections;
- int sec_size;
- int idx = 0;
- int err = -1, feat = 1;
-
- nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
- if (!nr_sections)
- return 0;
-
- feat_sec = calloc(sizeof(*feat_sec), nr_sections);
- if (!feat_sec)
- return -1;
-
- sec_size = sizeof(*feat_sec) * nr_sections;
-
- lseek(fd, self->data_offset + self->data_size, SEEK_SET);
-
- if (perf_header__getbuffer64(self, fd, feat_sec, sec_size))
- goto out_free;
-
- err = 0;
- while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
- if (perf_header__has_feat(self, feat)) {
- struct perf_file_section *sec = &feat_sec[idx++];
-
- err = process(sec, self, feat, fd);
- if (err < 0)
- break;
- }
- ++feat;
- }
-out_free:
- free(feat_sec);
- return err;
-}
-
-int perf_file_header__read(struct perf_file_header *self,
- struct perf_header *ph, int fd)
-{
- lseek(fd, 0, SEEK_SET);
-
- if (do_read(fd, self, sizeof(*self)) <= 0 ||
- memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
- return -1;
-
- if (self->attr_size != sizeof(struct perf_file_attr)) {
- u64 attr_size = bswap_64(self->attr_size);
-
- if (attr_size != sizeof(struct perf_file_attr))
- return -1;
-
- mem_bswap_64(self, offsetof(struct perf_file_header,
- adds_features));
- ph->needs_swap = true;
- }
-
- if (self->size != sizeof(*self)) {
- /* Support the previous format */
- if (self->size == offsetof(typeof(*self), adds_features))
- bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
- else
- return -1;
- }
-
- memcpy(&ph->adds_features, &self->adds_features,
- sizeof(ph->adds_features));
- /*
- * FIXME: hack that assumes that if we need swap the perf.data file
- * may be coming from an arch with a different word-size, ergo different
- * DEFINE_BITMAP format, investigate more later, but for now its mostly
- * safe to assume that we have a build-id section. Trace files probably
- * have several other issues in this realm anyway...
- */
- if (ph->needs_swap) {
- memset(&ph->adds_features, 0, sizeof(ph->adds_features));
- perf_header__set_feat(ph, HEADER_BUILD_ID);
- }
-
- ph->event_offset = self->event_types.offset;
- ph->event_size = self->event_types.size;
- ph->data_offset = self->data.offset;
- ph->data_size = self->data.size;
- return 0;
-}
-
-static int __event_process_build_id(struct build_id_event *bev,
- char *filename,
- struct perf_session *session)
-{
- int err = -1;
- struct list_head *head;
- struct machine *machine;
- u16 misc;
- struct dso *dso;
- enum dso_kernel_type dso_type;
-
- machine = perf_session__findnew_machine(session, bev->pid);
- if (!machine)
- goto out;
-
- misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
-
- switch (misc) {
- case PERF_RECORD_MISC_KERNEL:
- dso_type = DSO_TYPE_KERNEL;
- head = &machine->kernel_dsos;
- break;
- case PERF_RECORD_MISC_GUEST_KERNEL:
- dso_type = DSO_TYPE_GUEST_KERNEL;
- head = &machine->kernel_dsos;
- break;
- case PERF_RECORD_MISC_USER:
- case PERF_RECORD_MISC_GUEST_USER:
- dso_type = DSO_TYPE_USER;
- head = &machine->user_dsos;
- break;
- default:
- goto out;
- }
-
- dso = __dsos__findnew(head, filename);
- if (dso != NULL) {
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- dso__set_build_id(dso, &bev->build_id);
-
- if (filename[0] == '[')
- dso->kernel = dso_type;
-
- build_id__sprintf(dso->build_id, sizeof(dso->build_id),
- sbuild_id);
- pr_debug("build id event received for %s: %s\n",
- dso->long_name, sbuild_id);
- }
-
- err = 0;
-out:
- return err;
-}
-
-static int perf_header__read_build_ids(struct perf_header *self,
- int input, u64 offset, u64 size)
-{
- struct perf_session *session = container_of(self,
- struct perf_session, header);
- struct build_id_event bev;
- char filename[PATH_MAX];
- u64 limit = offset + size;
- int err = -1;
-
- while (offset < limit) {
- ssize_t len;
-
- if (read(input, &bev, sizeof(bev)) != sizeof(bev))
- goto out;
-
- if (self->needs_swap)
- perf_event_header__bswap(&bev.header);
-
- len = bev.header.size - sizeof(bev);
- if (read(input, filename, len) != len)
- goto out;
-
- __event_process_build_id(&bev, filename, session);
-
- offset += bev.header.size;
- }
- err = 0;
-out:
- return err;
-}
-
-static int perf_file_section__process(struct perf_file_section *self,
- struct perf_header *ph,
- int feat, int fd)
-{
- if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) {
- pr_debug("Failed to lseek to %Ld offset for feature %d, "
- "continuing...\n", self->offset, feat);
- return 0;
- }
-
- switch (feat) {
- case HEADER_TRACE_INFO:
- trace_report(fd, false);
- break;
-
- case HEADER_BUILD_ID:
- if (perf_header__read_build_ids(ph, fd, self->offset, self->size))
- pr_debug("Failed to read buildids, continuing...\n");
- break;
- default:
- pr_debug("unknown feature %d, continuing...\n", feat);
- }
-
- return 0;
-}
-
-static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
- struct perf_header *ph, int fd,
- bool repipe)
-{
- if (do_read(fd, self, sizeof(*self)) <= 0 ||
- memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
- return -1;
-
- if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0)
- return -1;
-
- if (self->size != sizeof(*self)) {
- u64 size = bswap_64(self->size);
-
- if (size != sizeof(*self))
- return -1;
-
- ph->needs_swap = true;
- }
-
- return 0;
-}
-
-static int perf_header__read_pipe(struct perf_session *session, int fd)
-{
- struct perf_header *self = &session->header;
- struct perf_pipe_file_header f_header;
-
- if (perf_file_header__read_pipe(&f_header, self, fd,
- session->repipe) < 0) {
- pr_debug("incompatible file format\n");
- return -EINVAL;
- }
-
- session->fd = fd;
-
- return 0;
-}
-
-int perf_header__read(struct perf_session *session, int fd)
-{
- struct perf_header *self = &session->header;
- struct perf_file_header f_header;
- struct perf_file_attr f_attr;
- u64 f_id;
- int nr_attrs, nr_ids, i, j;
-
- if (session->fd_pipe)
- return perf_header__read_pipe(session, fd);
-
- if (perf_file_header__read(&f_header, self, fd) < 0) {
- pr_debug("incompatible file format\n");
- return -EINVAL;
- }
-
- nr_attrs = f_header.attrs.size / sizeof(f_attr);
- lseek(fd, f_header.attrs.offset, SEEK_SET);
-
- for (i = 0; i < nr_attrs; i++) {
- struct perf_header_attr *attr;
- off_t tmp;
-
- if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr)))
- goto out_errno;
-
- tmp = lseek(fd, 0, SEEK_CUR);
-
- attr = perf_header_attr__new(&f_attr.attr);
- if (attr == NULL)
- return -ENOMEM;
-
- nr_ids = f_attr.ids.size / sizeof(u64);
- lseek(fd, f_attr.ids.offset, SEEK_SET);
-
- for (j = 0; j < nr_ids; j++) {
- if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id)))
- goto out_errno;
-
- if (perf_header_attr__add_id(attr, f_id) < 0) {
- perf_header_attr__delete(attr);
- return -ENOMEM;
- }
- }
- if (perf_header__add_attr(self, attr) < 0) {
- perf_header_attr__delete(attr);
- return -ENOMEM;
- }
-
- lseek(fd, tmp, SEEK_SET);
- }
-
- if (f_header.event_types.size) {
- lseek(fd, f_header.event_types.offset, SEEK_SET);
- events = malloc(f_header.event_types.size);
- if (events == NULL)
- return -ENOMEM;
- if (perf_header__getbuffer64(self, fd, events,
- f_header.event_types.size))
- goto out_errno;
- event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
- }
-
- perf_header__process_sections(self, fd, perf_file_section__process);
-
- lseek(fd, self->data_offset, SEEK_SET);
-
- self->frozen = 1;
- return 0;
-out_errno:
- return -errno;
-}
-
-u64 perf_header__sample_type(struct perf_header *header)
-{
- u64 type = 0;
- int i;
-
- for (i = 0; i < header->attrs; i++) {
- struct perf_header_attr *attr = header->attr[i];
-
- if (!type)
- type = attr->attr.sample_type;
- else if (type != attr->attr.sample_type)
- die("non matching sample_type");
- }
-
- return type;
-}
-
-struct perf_event_attr *
-perf_header__find_attr(u64 id, struct perf_header *header)
-{
- int i;
-
- /*
- * We set id to -1 if the data file doesn't contain sample
- * ids. Check for this and avoid walking through the entire
- * list of ids which may be large.
- */
- if (id == -1ULL)
- return NULL;
-
- for (i = 0; i < header->attrs; i++) {
- struct perf_header_attr *attr = header->attr[i];
- int j;
-
- for (j = 0; j < attr->ids; j++) {
- if (attr->id[j] == id)
- return &attr->attr;
- }
- }
-
- return NULL;
-}
-
-int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
- event__handler_t process,
- struct perf_session *session)
-{
- event_t *ev;
- size_t size;
- int err;
-
- size = sizeof(struct perf_event_attr);
- size = ALIGN(size, sizeof(u64));
- size += sizeof(struct perf_event_header);
- size += ids * sizeof(u64);
-
- ev = malloc(size);
-
- ev->attr.attr = *attr;
- memcpy(ev->attr.id, id, ids * sizeof(u64));
-
- ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
- ev->attr.header.size = size;
-
- err = process(ev, session);
-
- free(ev);
-
- return err;
-}
-
-int event__synthesize_attrs(struct perf_header *self,
- event__handler_t process,
- struct perf_session *session)
-{
- struct perf_header_attr *attr;
- int i, err = 0;
-
- for (i = 0; i < self->attrs; i++) {
- attr = self->attr[i];
-
- err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
- process, session);
- if (err) {
- pr_debug("failed to create perf header attribute\n");
- return err;
- }
- }
-
- return err;
-}
-
-int event__process_attr(event_t *self, struct perf_session *session)
-{
- struct perf_header_attr *attr;
- unsigned int i, ids, n_ids;
-
- attr = perf_header_attr__new(&self->attr.attr);
- if (attr == NULL)
- return -ENOMEM;
-
- ids = self->header.size;
- ids -= (void *)&self->attr.id - (void *)self;
- n_ids = ids / sizeof(u64);
-
- for (i = 0; i < n_ids; i++) {
- if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
- perf_header_attr__delete(attr);
- return -ENOMEM;
- }
- }
-
- if (perf_header__add_attr(&session->header, attr) < 0) {
- perf_header_attr__delete(attr);
- return -ENOMEM;
- }
-
- perf_session__update_sample_type(session);
-
- return 0;
-}
-
-int event__synthesize_event_type(u64 event_id, char *name,
- event__handler_t process,
- struct perf_session *session)
-{
- event_t ev;
- size_t size = 0;
- int err = 0;
-
- memset(&ev, 0, sizeof(ev));
-
- ev.event_type.event_type.event_id = event_id;
- memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
- strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
-
- ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
- size = strlen(name);
- size = ALIGN(size, sizeof(u64));
- ev.event_type.header.size = sizeof(ev.event_type) -
- (sizeof(ev.event_type.event_type.name) - size);
-
- err = process(&ev, session);
-
- return err;
-}
-
-int event__synthesize_event_types(event__handler_t process,
- struct perf_session *session)
-{
- struct perf_trace_event_type *type;
- int i, err = 0;
-
- for (i = 0; i < event_count; i++) {
- type = &events[i];
-
- err = event__synthesize_event_type(type->event_id, type->name,
- process, session);
- if (err) {
- pr_debug("failed to create perf header event type\n");
- return err;
- }
- }
-
- return err;
-}
-
-int event__process_event_type(event_t *self,
- struct perf_session *session __unused)
-{
- if (perf_header__push_event(self->event_type.event_type.event_id,
- self->event_type.event_type.name) < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
- int nb_events,
- event__handler_t process,
- struct perf_session *session __unused)
-{
- event_t ev;
- ssize_t size = 0, aligned_size = 0, padding;
- int err = 0;
-
- memset(&ev, 0, sizeof(ev));
-
- ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
- size = read_tracing_data_size(fd, pattrs, nb_events);
- if (size <= 0)
- return size;
- aligned_size = ALIGN(size, sizeof(u64));
- padding = aligned_size - size;
- ev.tracing_data.header.size = sizeof(ev.tracing_data);
- ev.tracing_data.size = aligned_size;
-
- process(&ev, session);
-
- err = read_tracing_data(fd, pattrs, nb_events);
- write_padded(fd, NULL, 0, padding);
-
- return aligned_size;
-}
-
-int event__process_tracing_data(event_t *self,
- struct perf_session *session)
-{
- ssize_t size_read, padding, size = self->tracing_data.size;
- off_t offset = lseek(session->fd, 0, SEEK_CUR);
- char buf[BUFSIZ];
-
- /* setup for reading amidst mmap */
- lseek(session->fd, offset + sizeof(struct tracing_data_event),
- SEEK_SET);
-
- size_read = trace_report(session->fd, session->repipe);
-
- padding = ALIGN(size_read, sizeof(u64)) - size_read;
-
- if (read(session->fd, buf, padding) < 0)
- die("reading input file");
- if (session->repipe) {
- int retw = write(STDOUT_FILENO, buf, padding);
- if (retw <= 0 || retw != padding)
- die("repiping tracing data padding");
- }
-
- if (size_read + padding != size)
- die("tracing data size mismatch");
-
- return size_read + padding;
-}
-
-int event__synthesize_build_id(struct dso *pos, u16 misc,
- event__handler_t process,
- struct machine *machine,
- struct perf_session *session)
-{
- event_t ev;
- size_t len;
- int err = 0;
-
- if (!pos->hit)
- return err;
-
- memset(&ev, 0, sizeof(ev));
-
- len = pos->long_name_len + 1;
- len = ALIGN(len, NAME_ALIGN);
- memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
- ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
- ev.build_id.header.misc = misc;
- ev.build_id.pid = machine->pid;
- ev.build_id.header.size = sizeof(ev.build_id) + len;
- memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
-
- err = process(&ev, session);
-
- return err;
-}
-
-int event__process_build_id(event_t *self,
- struct perf_session *session)
-{
- __event_process_build_id(&self->build_id,
- self->build_id.filename,
- session);
- return 0;
-}
-
-void disable_buildid_cache(void)
-{
- no_buildid_cache = true;
-}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
deleted file mode 100644
index fb6f0eb..0000000
--- a/tools/perf/util/header.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef __PERF_HEADER_H
-#define __PERF_HEADER_H
-
-#include "../../../include/linux/perf_event.h"
-#include <sys/types.h>
-#include <stdbool.h>
-#include <lk/types.h>
-#include "event.h"
-
-#include <linux/bitmap.h>
-
-struct perf_header_attr {
- struct perf_event_attr attr;
- int ids, size;
- u64 *id;
- off_t id_offset;
-};
-
-enum {
- HEADER_TRACE_INFO = 1,
- HEADER_BUILD_ID,
- HEADER_LAST_FEATURE,
-};
-
-#define HEADER_FEAT_BITS 256
-
-struct perf_file_section {
- u64 offset;
- u64 size;
-};
-
-struct perf_file_header {
- u64 magic;
- u64 size;
- u64 attr_size;
- struct perf_file_section attrs;
- struct perf_file_section data;
- struct perf_file_section event_types;
- DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
-};
-
-struct perf_pipe_file_header {
- u64 magic;
- u64 size;
-};
-
-struct perf_header;
-
-int perf_file_header__read(struct perf_file_header *self,
- struct perf_header *ph, int fd);
-
-struct perf_header {
- int frozen;
- int attrs, size;
- bool needs_swap;
- struct perf_header_attr **attr;
- s64 attr_offset;
- u64 data_offset;
- u64 data_size;
- u64 event_offset;
- u64 event_size;
- DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
-};
-
-int perf_header__init(struct perf_header *self);
-void perf_header__exit(struct perf_header *self);
-
-int perf_header__read(struct perf_session *session, int fd);
-int perf_header__write(struct perf_header *self, int fd, bool at_exit);
-int perf_header__write_pipe(int fd);
-
-int perf_header__add_attr(struct perf_header *self,
- struct perf_header_attr *attr);
-
-int perf_header__push_event(u64 id, const char *name);
-char *perf_header__find_event(u64 id);
-
-struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr);
-void perf_header_attr__delete(struct perf_header_attr *self);
-
-int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
-
-u64 perf_header__sample_type(struct perf_header *header);
-struct perf_event_attr *
-perf_header__find_attr(u64 id, struct perf_header *header);
-void perf_header__set_feat(struct perf_header *self, int feat);
-bool perf_header__has_feat(const struct perf_header *self, int feat);
-
-int perf_header__process_sections(struct perf_header *self, int fd,
- int (*process)(struct perf_file_section *self,
- struct perf_header *ph,
- int feat, int fd));
-
-int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms);
-int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
-
-int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
- event__handler_t process,
- struct perf_session *session);
-int event__synthesize_attrs(struct perf_header *self,
- event__handler_t process,
- struct perf_session *session);
-int event__process_attr(event_t *self, struct perf_session *session);
-
-int event__synthesize_event_type(u64 event_id, char *name,
- event__handler_t process,
- struct perf_session *session);
-int event__synthesize_event_types(event__handler_t process,
- struct perf_session *session);
-int event__process_event_type(event_t *self,
- struct perf_session *session);
-
-int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
- int nb_events,
- event__handler_t process,
- struct perf_session *session);
-int event__process_tracing_data(event_t *self,
- struct perf_session *session);
-
-int event__synthesize_build_id(struct dso *pos, u16 misc,
- event__handler_t process,
- struct machine *machine,
- struct perf_session *session);
-int event__process_build_id(event_t *self, struct perf_session *session);
-
-#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
deleted file mode 100644
index 215d4f3..0000000
--- a/tools/perf/util/parse-events.c
+++ /dev/null
@@ -1,957 +0,0 @@
-#include "../../../include/linux/hw_breakpoint.h"
-#include <lk/util.h>
-#include <lk/debug.h>
-#include "../perf.h"
-#include "parse-options.h"
-#include "parse-events.h"
-#include "exec_cmd.h"
-#include "string.h"
-#include "symbol.h"
-#include "cache.h"
-#include "header.h"
-#include <lk/debugfs.h>
-
-int nr_counters;
-
-struct perf_event_attr attrs[MAX_COUNTERS];
-char *filters[MAX_COUNTERS];
-
-struct event_symbol {
- u8 type;
- u64 config;
- const char *symbol;
- const char *alias;
-};
-
-enum event_result {
- EVT_FAILED,
- EVT_HANDLED,
- EVT_HANDLED_ALL
-};
-
-#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
-#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
-
-static struct event_symbol event_symbols[] = {
- { CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
- { CHW(INSTRUCTIONS), "instructions", "" },
- { CHW(CACHE_REFERENCES), "cache-references", "" },
- { CHW(CACHE_MISSES), "cache-misses", "" },
- { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
- { CHW(BRANCH_MISSES), "branch-misses", "" },
- { CHW(BUS_CYCLES), "bus-cycles", "" },
-
- { CSW(CPU_CLOCK), "cpu-clock", "" },
- { CSW(TASK_CLOCK), "task-clock", "" },
- { CSW(PAGE_FAULTS), "page-faults", "faults" },
- { CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
- { CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
- { CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
- { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
- { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
- { CSW(EMULATION_FAULTS), "emulation-faults", "" },
-};
-
-#define __PERF_EVENT_FIELD(config, name) \
- ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT)
-
-#define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW)
-#define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG)
-#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
-#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
-
-static const char *hw_event_names[] = {
- "cycles",
- "instructions",
- "cache-references",
- "cache-misses",
- "branches",
- "branch-misses",
- "bus-cycles",
-};
-
-static const char *sw_event_names[] = {
- "cpu-clock-msecs",
- "task-clock-msecs",
- "page-faults",
- "context-switches",
- "CPU-migrations",
- "minor-faults",
- "major-faults",
- "alignment-faults",
- "emulation-faults",
-};
-
-#define MAX_ALIASES 8
-
-static const char *hw_cache[][MAX_ALIASES] = {
- { "L1-dcache", "l1-d", "l1d", "L1-data", },
- { "L1-icache", "l1-i", "l1i", "L1-instruction", },
- { "LLC", "L2" },
- { "dTLB", "d-tlb", "Data-TLB", },
- { "iTLB", "i-tlb", "Instruction-TLB", },
- { "branch", "branches", "bpu", "btb", "bpc", },
-};
-
-static const char *hw_cache_op[][MAX_ALIASES] = {
- { "load", "loads", "read", },
- { "store", "stores", "write", },
- { "prefetch", "prefetches", "speculative-read", "speculative-load", },
-};
-
-static const char *hw_cache_result[][MAX_ALIASES] = {
- { "refs", "Reference", "ops", "access", },
- { "misses", "miss", },
-};
-
-#define C(x) PERF_COUNT_HW_CACHE_##x
-#define CACHE_READ (1 << C(OP_READ))
-#define CACHE_WRITE (1 << C(OP_WRITE))
-#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
-#define COP(x) (1 << x)
-
-/*
- * cache operartion stat
- * L1I : Read and prefetch only
- * ITLB and BPU : Read-only
- */
-static unsigned long hw_cache_stat[C(MAX)] = {
- [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
- [C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
- [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
- [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
- [C(ITLB)] = (CACHE_READ),
- [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];
- struct tracepoint_path *path;
-
- path = tracepoint_id_to_path(config);
- if (path) {
- snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
- free(path->name);
- free(path->system);
- free(path);
- } else
- snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
-
- return buf;
-}
-
-static int is_cache_op_valid(u8 cache_type, u8 cache_op)
-{
- if (hw_cache_stat[cache_type] & COP(cache_op))
- return 1; /* valid */
- else
- return 0; /* invalid */
-}
-
-static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
-{
- static char name[50];
-
- if (cache_result) {
- sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
- hw_cache_op[cache_op][0],
- hw_cache_result[cache_result][0]);
- } else {
- sprintf(name, "%s-%s", hw_cache[cache_type][0],
- hw_cache_op[cache_op][1]);
- }
-
- return name;
-}
-
-const char *event_name(int counter)
-{
- u64 config = attrs[counter].config;
- int type = attrs[counter].type;
-
- return __event_name(type, config);
-}
-
-const char *__event_name(int type, u64 config)
-{
- static char buf[32];
-
- if (type == PERF_TYPE_RAW) {
- sprintf(buf, "raw 0x%llx", config);
- return buf;
- }
-
- switch (type) {
- case PERF_TYPE_HARDWARE:
- if (config < PERF_COUNT_HW_MAX)
- return hw_event_names[config];
- return "unknown-hardware";
-
- case PERF_TYPE_HW_CACHE: {
- u8 cache_type, cache_op, cache_result;
-
- cache_type = (config >> 0) & 0xff;
- if (cache_type > PERF_COUNT_HW_CACHE_MAX)
- return "unknown-ext-hardware-cache-type";
-
- cache_op = (config >> 8) & 0xff;
- if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
- return "unknown-ext-hardware-cache-op";
-
- cache_result = (config >> 16) & 0xff;
- if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
- return "unknown-ext-hardware-cache-result";
-
- if (!is_cache_op_valid(cache_type, cache_op))
- return "invalid-cache";
-
- return event_cache_name(cache_type, cache_op, cache_result);
- }
-
- case PERF_TYPE_SOFTWARE:
- if (config < PERF_COUNT_SW_MAX)
- return sw_event_names[config];
- return "unknown-software";
-
- case PERF_TYPE_TRACEPOINT:
- return tracepoint_id_to_name(config);
-
- default:
- break;
- }
-
- return "unknown";
-}
-
-static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
-{
- int i, j;
- int n, longest = -1;
-
- for (i = 0; i < size; i++) {
- for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
- n = strlen(names[i][j]);
- if (n > longest && !strncasecmp(*str, names[i][j], n))
- longest = n;
- }
- if (longest > 0) {
- *str += longest;
- return i;
- }
- }
-
- return -1;
-}
-
-static enum event_result
-parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
-{
- const char *s = *str;
- int cache_type = -1, cache_op = -1, cache_result = -1;
-
- cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
- /*
- * No fallback - if we cannot get a clear cache type
- * then bail out:
- */
- if (cache_type == -1)
- return EVT_FAILED;
-
- while ((cache_op == -1 || cache_result == -1) && *s == '-') {
- ++s;
-
- if (cache_op == -1) {
- cache_op = parse_aliases(&s, hw_cache_op,
- PERF_COUNT_HW_CACHE_OP_MAX);
- if (cache_op >= 0) {
- if (!is_cache_op_valid(cache_type, cache_op))
- return 0;
- continue;
- }
- }
-
- if (cache_result == -1) {
- cache_result = parse_aliases(&s, hw_cache_result,
- PERF_COUNT_HW_CACHE_RESULT_MAX);
- if (cache_result >= 0)
- continue;
- }
-
- /*
- * Can't parse this as a cache op or result, so back up
- * to the '-'.
- */
- --s;
- break;
- }
-
- /*
- * Fall back to reads:
- */
- if (cache_op == -1)
- cache_op = PERF_COUNT_HW_CACHE_OP_READ;
-
- /*
- * Fall back to accesses:
- */
- if (cache_result == -1)
- cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
-
- attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
- attr->type = PERF_TYPE_HW_CACHE;
-
- *str = s;
- return EVT_HANDLED;
-}
-
-static enum event_result
-parse_single_tracepoint_event(char *sys_name,
- const char *evt_name,
- unsigned int evt_length,
- struct perf_event_attr *attr,
- const char **strp)
-{
- char evt_path[MAXPATHLEN];
- char id_buf[4];
- u64 id;
- int fd;
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
- sys_name, evt_name);
-
- fd = open(evt_path, O_RDONLY);
- if (fd < 0)
- return EVT_FAILED;
-
- if (read(fd, id_buf, sizeof(id_buf)) < 0) {
- close(fd);
- return EVT_FAILED;
- }
-
- close(fd);
- id = atoll(id_buf);
- attr->config = id;
- attr->type = PERF_TYPE_TRACEPOINT;
- *strp = evt_name + evt_length;
-
- attr->sample_type |= PERF_SAMPLE_RAW;
- attr->sample_type |= PERF_SAMPLE_TIME;
- attr->sample_type |= PERF_SAMPLE_CPU;
-
- attr->sample_period = 1;
-
-
- return EVT_HANDLED;
-}
-
-/* sys + ':' + event + ':' + flags*/
-#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
-static enum event_result
-parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
- char *flags)
-{
- char evt_path[MAXPATHLEN];
- struct dirent *evt_ent;
- DIR *evt_dir;
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
- evt_dir = opendir(evt_path);
-
- if (!evt_dir) {
- perror("Can't open event dir");
- return EVT_FAILED;
- }
-
- while ((evt_ent = readdir(evt_dir))) {
- char event_opt[MAX_EVOPT_LEN + 1];
- int len;
-
- if (!strcmp(evt_ent->d_name, ".")
- || !strcmp(evt_ent->d_name, "..")
- || !strcmp(evt_ent->d_name, "enable")
- || !strcmp(evt_ent->d_name, "filter"))
- continue;
-
- if (!strglobmatch(evt_ent->d_name, evt_exp))
- continue;
-
- len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
- evt_ent->d_name, flags ? ":" : "",
- flags ?: "");
- if (len < 0)
- return EVT_FAILED;
-
- if (parse_events(NULL, event_opt, 0))
- return EVT_FAILED;
- }
-
- return EVT_HANDLED_ALL;
-}
-
-
-static enum event_result parse_tracepoint_event(const char **strp,
- struct perf_event_attr *attr)
-{
- const char *evt_name;
- char *flags;
- char sys_name[MAX_EVENT_LENGTH];
- unsigned int sys_length, evt_length;
-
- if (debugfs_valid_mountpoint(debugfs_path))
- return 0;
-
- evt_name = strchr(*strp, ':');
- if (!evt_name)
- return EVT_FAILED;
-
- sys_length = evt_name - *strp;
- if (sys_length >= MAX_EVENT_LENGTH)
- return 0;
-
- strncpy(sys_name, *strp, sys_length);
- sys_name[sys_length] = '\0';
- evt_name = evt_name + 1;
-
- flags = strchr(evt_name, ':');
- if (flags) {
- /* split it out: */
- evt_name = strndup(evt_name, flags - evt_name);
- flags++;
- }
-
- evt_length = strlen(evt_name);
- if (evt_length >= MAX_EVENT_LENGTH)
- return EVT_FAILED;
-
- if (strpbrk(evt_name, "*?")) {
- *strp = evt_name + evt_length;
- return parse_multiple_tracepoint_event(sys_name, evt_name,
- flags);
- } else
- return parse_single_tracepoint_event(sys_name, evt_name,
- evt_length, attr, strp);
-}
-
-static enum event_result
-parse_breakpoint_type(const char *type, const char **strp,
- struct perf_event_attr *attr)
-{
- int i;
-
- for (i = 0; i < 3; i++) {
- if (!type[i])
- break;
-
- switch (type[i]) {
- case 'r':
- attr->bp_type |= HW_BREAKPOINT_R;
- break;
- case 'w':
- attr->bp_type |= HW_BREAKPOINT_W;
- break;
- case 'x':
- attr->bp_type |= HW_BREAKPOINT_X;
- break;
- default:
- return EVT_FAILED;
- }
- }
- if (!attr->bp_type) /* Default */
- attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
-
- *strp = type + i;
-
- return EVT_HANDLED;
-}
-
-static enum event_result
-parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
-{
- const char *target;
- const char *type;
- char *endaddr;
- u64 addr;
- enum event_result err;
-
- target = strchr(*strp, ':');
- if (!target)
- return EVT_FAILED;
-
- if (strncmp(*strp, "mem", target - *strp) != 0)
- return EVT_FAILED;
-
- target++;
-
- addr = strtoull(target, &endaddr, 0);
- if (target == endaddr)
- return EVT_FAILED;
-
- attr->bp_addr = addr;
- *strp = endaddr;
-
- type = strchr(target, ':');
-
- /* If no type is defined, just rw as default */
- if (!type) {
- attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
- } else {
- err = parse_breakpoint_type(++type, strp, attr);
- if (err == EVT_FAILED)
- return EVT_FAILED;
- }
-
- /* We should find a nice way to override the access type */
- attr->bp_len = HW_BREAKPOINT_LEN_4;
- attr->type = PERF_TYPE_BREAKPOINT;
-
- return EVT_HANDLED;
-}
-
-static int check_events(const char *str, unsigned int i)
-{
- int n;
-
- n = strlen(event_symbols[i].symbol);
- if (!strncmp(str, event_symbols[i].symbol, n))
- return n;
-
- n = strlen(event_symbols[i].alias);
- if (n)
- if (!strncmp(str, event_symbols[i].alias, n))
- return n;
- return 0;
-}
-
-static enum event_result
-parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
-{
- const char *str = *strp;
- unsigned int i;
- int n;
-
- for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
- n = check_events(str, i);
- if (n > 0) {
- attr->type = event_symbols[i].type;
- attr->config = event_symbols[i].config;
- *strp = str + n;
- return EVT_HANDLED;
- }
- }
- return EVT_FAILED;
-}
-
-static enum event_result
-parse_raw_event(const char **strp, struct perf_event_attr *attr)
-{
- const char *str = *strp;
- u64 config;
- int n;
-
- if (*str != 'r')
- return EVT_FAILED;
- n = hex2u64(str + 1, &config);
- if (n > 0) {
- *strp = str + n + 1;
- attr->type = PERF_TYPE_RAW;
- attr->config = config;
- return EVT_HANDLED;
- }
- return EVT_FAILED;
-}
-
-static enum event_result
-parse_numeric_event(const char **strp, struct perf_event_attr *attr)
-{
- const char *str = *strp;
- char *endp;
- unsigned long type;
- u64 config;
-
- type = strtoul(str, &endp, 0);
- if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
- str = endp + 1;
- config = strtoul(str, &endp, 0);
- if (endp > str) {
- attr->type = type;
- attr->config = config;
- *strp = endp;
- return EVT_HANDLED;
- }
- }
- return EVT_FAILED;
-}
-
-static enum event_result
-parse_event_modifier(const char **strp, struct perf_event_attr *attr)
-{
- const char *str = *strp;
- int exclude = 0;
- int eu = 0, ek = 0, eh = 0, precise = 0;
-
- if (*str++ != ':')
- return 0;
- while (*str) {
- if (*str == 'u') {
- if (!exclude)
- exclude = eu = ek = eh = 1;
- eu = 0;
- } else if (*str == 'k') {
- if (!exclude)
- exclude = eu = ek = eh = 1;
- ek = 0;
- } else if (*str == 'h') {
- if (!exclude)
- exclude = eu = ek = eh = 1;
- eh = 0;
- } else if (*str == 'p') {
- precise++;
- } else
- break;
-
- ++str;
- }
- if (str >= *strp + 2) {
- *strp = str;
- attr->exclude_user = eu;
- attr->exclude_kernel = ek;
- attr->exclude_hv = eh;
- attr->precise_ip = precise;
- return 1;
- }
- return 0;
-}
-
-/*
- * Each event can have multiple symbolic names.
- * Symbolic names are (almost) exactly matched.
- */
-static enum event_result
-parse_event_symbols(const char **str, struct perf_event_attr *attr)
-{
- enum event_result ret;
-
- ret = parse_tracepoint_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_raw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_numeric_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_symbolic_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_generic_hw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_breakpoint_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
- fprintf(stderr, "Run 'perf list' for a list of valid events\n");
- return EVT_FAILED;
-
-modifier:
- parse_event_modifier(str, attr);
-
- return ret;
-}
-
-static int store_event_type(const char *orgname)
-{
- char filename[PATH_MAX], *c;
- FILE *file;
- int id, n;
-
- sprintf(filename, "%s/", debugfs_path);
- strncat(filename, orgname, strlen(orgname));
- strcat(filename, "/id");
-
- c = strchr(filename, ':');
- if (c)
- *c = '/';
-
- file = fopen(filename, "r");
- if (!file)
- return 0;
- n = fscanf(file, "%i", &id);
- fclose(file);
- if (n < 1) {
- pr_err("cannot store event ID\n");
- return -EINVAL;
- }
- return perf_header__push_event(id, orgname);
-}
-
-int parse_events(const struct option *opt __used, const char *str, int unset __used)
-{
- struct perf_event_attr attr;
- enum event_result ret;
-
- if (strchr(str, ':'))
- if (store_event_type(str) < 0)
- return -1;
-
- for (;;) {
- if (nr_counters == MAX_COUNTERS)
- return -1;
-
- memset(&attr, 0, sizeof(attr));
- ret = parse_event_symbols(&str, &attr);
- if (ret == EVT_FAILED)
- return -1;
-
- if (!(*str == 0 || *str == ',' || isspace(*str)))
- return -1;
-
- if (ret != EVT_HANDLED_ALL) {
- attrs[nr_counters] = attr;
- nr_counters++;
- }
-
- if (*str == 0)
- break;
- if (*str == ',')
- ++str;
- while (isspace(*str))
- ++str;
- }
-
- return 0;
-}
-
-int parse_filter(const struct option *opt __used, const char *str,
- int unset __used)
-{
- int i = nr_counters - 1;
- int len = strlen(str);
-
- if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) {
- fprintf(stderr,
- "-F option should follow a -e tracepoint option\n");
- return -1;
- }
-
- filters[i] = malloc(len + 1);
- if (!filters[i]) {
- fprintf(stderr, "not enough memory to hold filter string\n");
- return -1;
- }
- strcpy(filters[i], str);
-
- return 0;
-}
-
-static const char * const event_type_descriptors[] = {
- "Hardware event",
- "Software event",
- "Tracepoint event",
- "Hardware cache event",
- "Raw hardware event descriptor",
- "Hardware breakpoint",
-};
-
-/*
- * Print the events from <debugfs_mount_point>/tracing/events
- */
-
-static void print_tracepoint_events(void)
-{
- DIR *sys_dir, *evt_dir;
- struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
- char evt_path[MAXPATHLEN];
- char dir_path[MAXPATHLEN];
-
- if (debugfs_valid_mountpoint(debugfs_path))
- return;
-
- sys_dir = opendir(debugfs_path);
- if (!sys_dir)
- return;
-
- 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",
- sys_dirent.d_name, evt_dirent.d_name);
- printf(" %-42s [%s]\n", evt_path,
- event_type_descriptors[PERF_TYPE_TRACEPOINT]);
- }
- closedir(evt_dir);
- }
- closedir(sys_dir);
-}
-
-/*
- * Print the help text for the event symbols:
- */
-void print_events(void)
-{
- struct event_symbol *syms = event_symbols;
- unsigned int i, type, op, prev_type = -1;
- char name[40];
-
- printf("\n");
- printf("List of pre-defined events (to be used in -e):\n");
-
- for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
- type = syms->type;
-
- if (type != prev_type)
- printf("\n");
-
- if (strlen(syms->alias))
- sprintf(name, "%s OR %s", syms->symbol, syms->alias);
- else
- strcpy(name, syms->symbol);
- printf(" %-42s [%s]\n", name,
- event_type_descriptors[type]);
-
- prev_type = type;
- }
-
- printf("\n");
- for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
- for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
- /* skip invalid cache type */
- if (!is_cache_op_valid(type, op))
- continue;
-
- for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
- printf(" %-42s [%s]\n",
- event_cache_name(type, op, i),
- event_type_descriptors[PERF_TYPE_HW_CACHE]);
- }
- }
- }
-
- printf("\n");
- printf(" %-42s [%s]\n",
- "rNNN (see 'perf list --help' on how to encode it)",
- event_type_descriptors[PERF_TYPE_RAW]);
- printf("\n");
-
- printf(" %-42s [%s]\n",
- "mem:<addr>[:access]",
- event_type_descriptors[PERF_TYPE_BREAKPOINT]);
- printf("\n");
-
- print_tracepoint_events();
-
- exit(129);
-}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
deleted file mode 100644
index 436c831..0000000
--- a/tools/perf/util/parse-events.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef __PERF_PARSE_EVENTS_H
-#define __PERF_PARSE_EVENTS_H
-/*
- * Parse symbolic events/counts passed in as options:
- */
-
-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;
-
-extern struct perf_event_attr attrs[MAX_COUNTERS];
-extern char *filters[MAX_COUNTERS];
-
-extern const char *event_name(int ctr);
-extern const char *__event_name(int type, u64 config);
-
-extern int parse_events(const struct option *opt, const char *str, int unset);
-extern int parse_filter(const struct option *opt, const char *str, int unset);
-
-#define EVENTS_HELP_MAX (128*1024)
-
-extern void print_events(void);
-
-extern int valid_debugfs_mount(const char *debugfs);
-
-
-#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 55c6881..bf3b5c4 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -3,16 +3,31 @@
#include "hist.h"
#include "event.h"
-#include "header.h"
#include "symbol.h"
#include "thread.h"
+#include <linux/bitops.h>
#include <linux/rbtree.h>
#include "../../../include/linux/perf_event.h"
+#define HEADER_FEAT_BITS 256
+
struct sample_queue;
struct ip_callchain;
struct thread;
+struct perf_header {
+ int frozen;
+ int attrs, size;
+ bool needs_swap;
+ struct perf_header_attr **attr;
+ s64 attr_offset;
+ u64 data_offset;
+ u64 data_size;
+ u64 event_offset;
+ u64 event_size;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
struct ordered_samples {
u64 last_flush;
u64 next_flush;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 7a266b8..0d6ed4c 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -16,10 +16,10 @@
#include "../perf.h"
#include <lk/debug.h>
-#include "header.h"
+#include <perf/header.h>
#include "parse-options.h"
-#include "parse-events.h"
+#include <perf/parse-events.h>
#include "thread.h"
#include "sort.h"
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index b3e86b1..428383b 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -2,7 +2,7 @@
#define __PERF_TRACE_EVENTS_H
#include <stdbool.h>
-#include "parse-events.h"
+#include <perf/parse-events.h>
#define __unused __attribute__((unused))
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 13/21] perf: Carve out perf bits for general usage, p2
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (11 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 12/21] perf: Carve out perf bits for general usage, p1 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 14/21] perf: Carve out perf bits for general usage, p3 Borislav Petkov
` (7 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Those are pulled in when linking parse-events.c with other tools like
the RAS daemon so export them into tools/lib/perf/ too.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 39 +-
tools/lib/Makefile | 6 +
| 4 +-
| 2 +-
tools/lib/perf/map.c | 630 ++++++++++
tools/lib/perf/map.h | 217 ++++
tools/lib/perf/parse-events.c | 2 +-
tools/lib/perf/session.c | 906 ++++++++++++++
tools/lib/perf/session.h | 158 +++
tools/lib/perf/symbol.c | 2346 +++++++++++++++++++++++++++++++++++
tools/lib/perf/symbol.h | 221 ++++
tools/perf/Makefile | 41 -
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-buildid-cache.c | 6 +-
tools/perf/builtin-buildid-list.c | 4 +-
tools/perf/builtin-diff.c | 4 +-
tools/perf/builtin-inject.c | 2 +-
tools/perf/builtin-kmem.c | 4 +-
tools/perf/builtin-kvm.c | 4 +-
tools/perf/builtin-lock.c | 4 +-
tools/perf/builtin-probe.c | 2 +-
tools/perf/builtin-record.c | 4 +-
tools/perf/builtin-report.c | 4 +-
tools/perf/builtin-sched.c | 4 +-
tools/perf/builtin-test.c | 4 +-
tools/perf/builtin-timechart.c | 4 +-
tools/perf/builtin-top.c | 4 +-
tools/perf/builtin-trace.c | 4 +-
tools/perf/util/build-id.c | 2 +-
tools/perf/util/build-id.h | 2 +-
tools/perf/util/callchain.h | 2 +-
tools/perf/util/event.c | 2 +-
tools/perf/util/event.h | 2 +-
tools/perf/util/hist.c | 2 +-
tools/perf/util/map.c | 630 ----------
tools/perf/util/map.h | 217 ----
tools/perf/util/newt.c | 4 +-
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-finder.c | 2 +-
tools/perf/util/session.c | 906 --------------
tools/perf/util/session.h | 158 ---
tools/perf/util/sort.h | 2 +-
tools/perf/util/symbol.c | 2347 ------------------------------------
tools/perf/util/symbol.h | 221 ----
tools/perf/util/thread.c | 2 +-
tools/perf/util/thread.h | 2 +-
46 files changed, 4570 insertions(+), 4569 deletions(-)
create mode 100644 tools/lib/perf/map.c
create mode 100644 tools/lib/perf/map.h
create mode 100644 tools/lib/perf/session.c
create mode 100644 tools/lib/perf/session.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/session.c
delete mode 100644 tools/perf/util/session.h
delete mode 100644 tools/perf/util/symbol.c
delete mode 100644 tools/perf/util/symbol.h
diff --git a/tools/Makefile b/tools/Makefile
index 9949133..54dea0e 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -2,6 +2,7 @@
#
include Makefile.lib
+-include perf/feature-tests.mak
#
# Include saner warnings here, which can catch bugs:
@@ -58,7 +59,43 @@ else
endif
endif
-export BASIC_CFLAGS EXTLIBS
+ifdef NO_DEMANGLE
+ BASIC_CFLAGS += -DNO_DEMANGLE
+else ifdef HAVE_CPLUS_DEMANGLE
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+else
+ FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
+ has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
+ ifeq ($(has_bfd),y)
+ EXTLIBS += -lbfd
+ else
+ FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
+ has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
+ ifeq ($(has_bfd_iberty),y)
+ EXTLIBS += -lbfd -liberty
+ else
+ FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
+ has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
+ ifeq ($(has_bfd_iberty_z),y)
+ EXTLIBS += -lbfd -liberty -lz
+ else
+ FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
+ has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
+ ifeq ($(has_cplus_demangle),y)
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+ else
+ msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)
+ BASIC_CFLAGS += -DNO_DEMANGLE
+ endif
+ endif
+ endif
+ endif
+endif
+
+
+export BASIC_CFLAGS EXTLIBS
perf: lib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index ed81953..dd87c43 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -15,6 +15,9 @@ LIB_H += lk/debug.h
LIB_H += lk/strlist.h
LIB_H += perf/parse-events.h
LIB_H += perf/header.h
+LIB_H += perf/symbol.h
+LIB_H += perf/map.h
+LIB_H += perf/session.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -33,6 +36,9 @@ LIB_OBJS += $(OUTPUT)lk/rbtree.o
LIB_OBJS += $(OUTPUT)lk/strlist.o
LIB_OBJS += $(OUTPUT)perf/parse-events.o
LIB_OBJS += $(OUTPUT)perf/header.o
+LIB_OBJS += $(OUTPUT)perf/symbol.o
+LIB_OBJS += $(OUTPUT)perf/map.o
+LIB_OBJS += $(OUTPUT)perf/session.o
LIBFILE = lklib.a
--git a/tools/lib/perf/header.c b/tools/lib/perf/header.c
index 6804546..572a243 100644
--- a/tools/lib/perf/header.c
+++ b/tools/lib/perf/header.c
@@ -12,8 +12,8 @@
#include "header.h"
#include <perf.h>
#include <util/trace-event.h>
-#include <util/session.h>
-#include <util/symbol.h>
+#include "session.h"
+#include "symbol.h"
#include <lk/debug.h>
static bool no_buildid_cache = false;
--git a/tools/lib/perf/header.h b/tools/lib/perf/header.h
index e8cdb86..e930814 100644
--- a/tools/lib/perf/header.h
+++ b/tools/lib/perf/header.h
@@ -6,7 +6,7 @@
#include <stdbool.h>
#include <lk/types.h>
#include <util/event.h>
-#include <util/session.h>
+#include "session.h"
#include <linux/bitmap.h>
diff --git a/tools/lib/perf/map.c b/tools/lib/perf/map.c
new file mode 100644
index 0000000..fa82eba
--- /dev/null
+++ b/tools/lib/perf/map.c
@@ -0,0 +1,630 @@
+#include "symbol.h"
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <lk/debug.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;
+}
+
+static int strcommon(const char *pathname, char *cwd, int cwdlen)
+{
+ int n = 0;
+
+ while (n < cwdlen && pathname[n] == cwd[n])
+ ++n;
+
+ return n;
+}
+
+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;
+}
+
+struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
+ u64 pgoff, u32 pid, char *filename,
+ enum map_type type, char *cwd, int cwdlen)
+{
+ struct map *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ char newfilename[PATH_MAX];
+ struct dso *dso;
+ int anon;
+
+ if (cwd) {
+ int n = strcommon(filename, cwd, cwdlen);
+
+ if (n == cwdlen) {
+ snprintf(newfilename, sizeof(newfilename),
+ ".%s", filename + n);
+ filename = newfilename;
+ }
+ }
+
+ 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;
+}
+
+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);
+
+ 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);
+ /*
+ * 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[map->type]);
+ /*
+ * 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)
+ return -ENOMEM;
+
+ 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)
+ return -ENOMEM;
+
+ after->start = map->end + 1;
+ map_groups__insert(self, after);
+ if (_verbose >= 2)
+ map__fprintf(after, fp);
+ }
+ }
+
+ 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);
+}
+
+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;
+}
+
+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..c7ed844
--- /dev/null
+++ b/tools/lib/perf/map.h
@@ -0,0 +1,217 @@
+#ifndef __PERF_MAP_H
+#define __PERF_MAP_H
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <lk/types.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;
+ enum map_type type;
+ 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, char *cwd, int cwdlen);
+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);
+struct map *maps__find(struct rb_root *maps, u64 addr);
+void map_groups__init(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);
+
+/*
+ * 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 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/parse-events.c b/tools/lib/perf/parse-events.c
index f028838..629114e 100644
--- a/tools/lib/perf/parse-events.c
+++ b/tools/lib/perf/parse-events.c
@@ -6,7 +6,7 @@
#include "parse-events.h"
#include <util/exec_cmd.h>
#include "string.h"
-#include <util/symbol.h>
+#include "symbol.h"
#include <util/cache.h>
#include "header.h"
#include <lk/debugfs.h>
diff --git a/tools/lib/perf/session.c b/tools/lib/perf/session.c
new file mode 100644
index 0000000..5e9998c
--- /dev/null
+++ b/tools/lib/perf/session.c
@@ -0,0 +1,906 @@
+#define _FILE_OFFSET_BITS 64
+
+#include <linux/kernel.h>
+
+#include <byteswap.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "session.h"
+#include <util/sort.h>
+#include <lk/util.h>
+
+static int perf_session__open(struct perf_session *self, bool force)
+{
+ struct stat input_stat;
+
+ if (!strcmp(self->filename, "-")) {
+ self->fd_pipe = true;
+ self->fd = STDIN_FILENO;
+
+ if (perf_header__read(self, self->fd) < 0)
+ pr_err("incompatible file format");
+
+ return 0;
+ }
+
+ self->fd = open(self->filename, O_RDONLY);
+ if (self->fd < 0) {
+ int err = errno;
+
+ pr_err("failed to open %s: %s", self->filename, strerror(err));
+ if (err == ENOENT && !strcmp(self->filename, "perf.data"))
+ pr_err(" (try 'perf record' first)");
+ pr_err("\n");
+ return -errno;
+ }
+
+ if (fstat(self->fd, &input_stat) < 0)
+ goto out_close;
+
+ if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
+ pr_err("file %s not owned by current user or root\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (!input_stat.st_size) {
+ pr_info("zero-sized file (%s), nothing to do!\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (perf_header__read(self, self->fd) < 0) {
+ pr_err("incompatible file format");
+ goto out_close;
+ }
+
+ self->size = input_stat.st_size;
+ return 0;
+
+out_close:
+ close(self->fd);
+ self->fd = -1;
+ return -1;
+}
+
+void perf_session__update_sample_type(struct perf_session *self)
+{
+ self->sample_type = perf_header__sample_type(&self->header);
+}
+
+int perf_session__create_kernel_maps(struct perf_session *self)
+{
+ int ret = machine__create_kernel_maps(&self->host_machine);
+
+ if (ret >= 0)
+ ret = machines__create_guest_kernel_maps(&self->machines);
+ return ret;
+}
+
+struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
+{
+ size_t len = filename ? strlen(filename) + 1 : 0;
+ struct perf_session *self = zalloc(sizeof(*self) + len);
+
+ if (self == NULL)
+ goto out;
+
+ if (perf_header__init(&self->header) < 0)
+ goto out_free;
+
+ memcpy(self->filename, filename, len);
+ self->threads = RB_ROOT;
+ self->hists_tree = RB_ROOT;
+ self->last_match = NULL;
+ self->mmap_window = 32;
+ self->cwd = NULL;
+ self->cwdlen = 0;
+ self->machines = RB_ROOT;
+ self->repipe = repipe;
+ INIT_LIST_HEAD(&self->ordered_samples.samples_head);
+ machine__init(&self->host_machine, "", HOST_KERNEL_ID);
+
+ if (mode == O_RDONLY) {
+ if (perf_session__open(self, force) < 0)
+ goto out_delete;
+ } else if (mode == O_WRONLY) {
+ /*
+ * In O_RDONLY mode this will be performed when reading the
+ * kernel MMAP event, in event__process_mmap().
+ */
+ if (perf_session__create_kernel_maps(self) < 0)
+ goto out_delete;
+ }
+
+ perf_session__update_sample_type(self);
+out:
+ return self;
+out_free:
+ free(self);
+ return NULL;
+out_delete:
+ perf_session__delete(self);
+ return NULL;
+}
+
+void perf_session__delete(struct perf_session *self)
+{
+ perf_header__exit(&self->header);
+ close(self->fd);
+ free(self->cwd);
+ free(self);
+}
+
+static bool symbol__match_parent_regex(struct symbol *sym)
+{
+ if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
+ return 1;
+
+ return 0;
+}
+
+struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent)
+{
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ unsigned int i;
+ struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
+
+ if (!syms)
+ return NULL;
+
+ for (i = 0; i < chain->nr; i++) {
+ u64 ip = chain->ips[i];
+ struct addr_location al;
+
+ if (ip >= PERF_CONTEXT_MAX) {
+ switch (ip) {
+ case PERF_CONTEXT_HV:
+ cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
+ case PERF_CONTEXT_KERNEL:
+ cpumode = PERF_RECORD_MISC_KERNEL; break;
+ case PERF_CONTEXT_USER:
+ cpumode = PERF_RECORD_MISC_USER; break;
+ default:
+ break;
+ }
+ continue;
+ }
+
+ al.filtered = false;
+ thread__find_addr_location(thread, self, cpumode,
+ MAP__FUNCTION, thread->pid, ip, &al, NULL);
+ if (al.sym != NULL) {
+ if (sort__has_parent && !*parent &&
+ symbol__match_parent_regex(al.sym))
+ *parent = al.sym;
+ if (!symbol_conf.use_callchain)
+ break;
+ syms[i].map = al.map;
+ syms[i].sym = al.sym;
+ }
+ }
+
+ return syms;
+}
+
+static int process_event_stub(event_t *event __used,
+ struct perf_session *session __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_finished_round_stub(event_t *event __used,
+ struct perf_session *session __used,
+ struct perf_event_ops *ops __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_finished_round(event_t *event,
+ struct perf_session *session,
+ struct perf_event_ops *ops);
+
+static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
+{
+ if (handler->sample == NULL)
+ handler->sample = process_event_stub;
+ if (handler->mmap == NULL)
+ handler->mmap = process_event_stub;
+ if (handler->comm == NULL)
+ handler->comm = process_event_stub;
+ if (handler->fork == NULL)
+ handler->fork = process_event_stub;
+ if (handler->exit == NULL)
+ handler->exit = process_event_stub;
+ if (handler->lost == NULL)
+ handler->lost = process_event_stub;
+ if (handler->read == NULL)
+ handler->read = process_event_stub;
+ if (handler->throttle == NULL)
+ handler->throttle = process_event_stub;
+ if (handler->unthrottle == NULL)
+ handler->unthrottle = process_event_stub;
+ if (handler->attr == NULL)
+ handler->attr = process_event_stub;
+ if (handler->event_type == NULL)
+ handler->event_type = process_event_stub;
+ if (handler->tracing_data == NULL)
+ handler->tracing_data = process_event_stub;
+ if (handler->build_id == NULL)
+ handler->build_id = process_event_stub;
+ if (handler->finished_round == NULL) {
+ if (handler->ordered_samples)
+ handler->finished_round = process_finished_round;
+ else
+ handler->finished_round = process_finished_round_stub;
+ }
+}
+
+void mem_bswap_64(void *src, int byte_size)
+{
+ u64 *m = src;
+
+ while (byte_size > 0) {
+ *m = bswap_64(*m);
+ byte_size -= sizeof(u64);
+ ++m;
+ }
+}
+
+static void event__all64_swap(event_t *self)
+{
+ struct perf_event_header *hdr = &self->header;
+ mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
+}
+
+static void event__comm_swap(event_t *self)
+{
+ self->comm.pid = bswap_32(self->comm.pid);
+ self->comm.tid = bswap_32(self->comm.tid);
+}
+
+static void event__mmap_swap(event_t *self)
+{
+ self->mmap.pid = bswap_32(self->mmap.pid);
+ self->mmap.tid = bswap_32(self->mmap.tid);
+ self->mmap.start = bswap_64(self->mmap.start);
+ self->mmap.len = bswap_64(self->mmap.len);
+ self->mmap.pgoff = bswap_64(self->mmap.pgoff);
+}
+
+static void event__task_swap(event_t *self)
+{
+ self->fork.pid = bswap_32(self->fork.pid);
+ self->fork.tid = bswap_32(self->fork.tid);
+ self->fork.ppid = bswap_32(self->fork.ppid);
+ self->fork.ptid = bswap_32(self->fork.ptid);
+ self->fork.time = bswap_64(self->fork.time);
+}
+
+static void event__read_swap(event_t *self)
+{
+ self->read.pid = bswap_32(self->read.pid);
+ self->read.tid = bswap_32(self->read.tid);
+ self->read.value = bswap_64(self->read.value);
+ self->read.time_enabled = bswap_64(self->read.time_enabled);
+ self->read.time_running = bswap_64(self->read.time_running);
+ self->read.id = bswap_64(self->read.id);
+}
+
+static void event__attr_swap(event_t *self)
+{
+ size_t size;
+
+ self->attr.attr.type = bswap_32(self->attr.attr.type);
+ self->attr.attr.size = bswap_32(self->attr.attr.size);
+ self->attr.attr.config = bswap_64(self->attr.attr.config);
+ self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period);
+ self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type);
+ self->attr.attr.read_format = bswap_64(self->attr.attr.read_format);
+ self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events);
+ self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type);
+ self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr);
+ self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len);
+
+ size = self->header.size;
+ size -= (void *)&self->attr.id - (void *)self;
+ mem_bswap_64(self->attr.id, size);
+}
+
+static void event__event_type_swap(event_t *self)
+{
+ self->event_type.event_type.event_id =
+ bswap_64(self->event_type.event_type.event_id);
+}
+
+static void event__tracing_data_swap(event_t *self)
+{
+ self->tracing_data.size = bswap_32(self->tracing_data.size);
+}
+
+typedef void (*event__swap_op)(event_t *self);
+
+static event__swap_op event__swap_ops[] = {
+ [PERF_RECORD_MMAP] = event__mmap_swap,
+ [PERF_RECORD_COMM] = event__comm_swap,
+ [PERF_RECORD_FORK] = event__task_swap,
+ [PERF_RECORD_EXIT] = event__task_swap,
+ [PERF_RECORD_LOST] = event__all64_swap,
+ [PERF_RECORD_READ] = event__read_swap,
+ [PERF_RECORD_SAMPLE] = event__all64_swap,
+ [PERF_RECORD_HEADER_ATTR] = event__attr_swap,
+ [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
+ [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
+ [PERF_RECORD_HEADER_BUILD_ID] = NULL,
+ [PERF_RECORD_HEADER_MAX] = NULL,
+};
+
+struct sample_queue {
+ u64 timestamp;
+ struct sample_event *event;
+ struct list_head list;
+};
+
+static void flush_sample_queue(struct perf_session *s,
+ struct perf_event_ops *ops)
+{
+ struct list_head *head = &s->ordered_samples.samples_head;
+ u64 limit = s->ordered_samples.next_flush;
+ struct sample_queue *tmp, *iter;
+
+ if (!ops->ordered_samples || !limit)
+ return;
+
+ list_for_each_entry_safe(iter, tmp, head, list) {
+ if (iter->timestamp > limit)
+ return;
+
+ if (iter == s->ordered_samples.last_inserted)
+ s->ordered_samples.last_inserted = NULL;
+
+ ops->sample((event_t *)iter->event, s);
+
+ s->ordered_samples.last_flush = iter->timestamp;
+ list_del(&iter->list);
+ free(iter->event);
+ free(iter);
+ }
+}
+
+/*
+ * When perf record finishes a pass on every buffers, it records this pseudo
+ * event.
+ * We record the max timestamp t found in the pass n.
+ * Assuming these timestamps are monotonic across cpus, we know that if
+ * a buffer still has events with timestamps below t, they will be all
+ * available and then read in the pass n + 1.
+ * Hence when we start to read the pass n + 2, we can safely flush every
+ * events with timestamps below t.
+ *
+ * ============ PASS n =================
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 1 | 2
+ * 2 | 3
+ * - | 4 <--- max recorded
+ *
+ * ============ PASS n + 1 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 3 | 5
+ * 4 | 6
+ * 5 | 7 <---- max recorded
+ *
+ * Flush every events below timestamp 4
+ *
+ * ============ PASS n + 2 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 6 | 8
+ * 7 | 9
+ * - | 10
+ *
+ * Flush every events below timestamp 7
+ * etc...
+ */
+static int process_finished_round(event_t *event __used,
+ struct perf_session *session,
+ struct perf_event_ops *ops)
+{
+ flush_sample_queue(session, ops);
+ session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
+
+ return 0;
+}
+
+static void __queue_sample_end(struct sample_queue *new, struct list_head *head)
+{
+ struct sample_queue *iter;
+
+ list_for_each_entry_reverse(iter, head, list) {
+ if (iter->timestamp < new->timestamp) {
+ list_add(&new->list, &iter->list);
+ return;
+ }
+ }
+
+ list_add(&new->list, head);
+}
+
+static void __queue_sample_before(struct sample_queue *new,
+ struct sample_queue *iter,
+ struct list_head *head)
+{
+ list_for_each_entry_continue_reverse(iter, head, list) {
+ if (iter->timestamp < new->timestamp) {
+ list_add(&new->list, &iter->list);
+ return;
+ }
+ }
+
+ list_add(&new->list, head);
+}
+
+static void __queue_sample_after(struct sample_queue *new,
+ struct sample_queue *iter,
+ struct list_head *head)
+{
+ list_for_each_entry_continue(iter, head, list) {
+ if (iter->timestamp > new->timestamp) {
+ list_add_tail(&new->list, &iter->list);
+ return;
+ }
+ }
+ list_add_tail(&new->list, head);
+}
+
+/* The queue is ordered by time */
+static void __queue_sample_event(struct sample_queue *new,
+ struct perf_session *s)
+{
+ struct sample_queue *last_inserted = s->ordered_samples.last_inserted;
+ struct list_head *head = &s->ordered_samples.samples_head;
+
+
+ if (!last_inserted) {
+ __queue_sample_end(new, head);
+ return;
+ }
+
+ /*
+ * Most of the time the current event has a timestamp
+ * very close to the last event inserted, unless we just switched
+ * to another event buffer. Having a sorting based on a list and
+ * on the last inserted event that is close to the current one is
+ * probably more efficient than an rbtree based sorting.
+ */
+ if (last_inserted->timestamp >= new->timestamp)
+ __queue_sample_before(new, last_inserted, head);
+ else
+ __queue_sample_after(new, last_inserted, head);
+}
+
+static int queue_sample_event(event_t *event, struct sample_data *data,
+ struct perf_session *s)
+{
+ u64 timestamp = data->time;
+ struct sample_queue *new;
+
+
+ if (timestamp < s->ordered_samples.last_flush) {
+ printf("Warning: Timestamp below last timeslice flush\n");
+ return -EINVAL;
+ }
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return -ENOMEM;
+
+ new->timestamp = timestamp;
+
+ new->event = malloc(event->header.size);
+ if (!new->event) {
+ free(new);
+ return -ENOMEM;
+ }
+
+ memcpy(new->event, event, event->header.size);
+
+ __queue_sample_event(new, s);
+ s->ordered_samples.last_inserted = new;
+
+ if (new->timestamp > s->ordered_samples.max_timestamp)
+ s->ordered_samples.max_timestamp = new->timestamp;
+
+ return 0;
+}
+
+static int perf_session__process_sample(event_t *event, struct perf_session *s,
+ struct perf_event_ops *ops)
+{
+ struct sample_data data;
+
+ if (!ops->ordered_samples)
+ return ops->sample(event, s);
+
+ bzero(&data, sizeof(struct sample_data));
+ event__parse_sample(event, s->sample_type, &data);
+
+ queue_sample_event(event, &data, s);
+
+ return 0;
+}
+
+static int perf_session__process_event(struct perf_session *self,
+ event_t *event,
+ struct perf_event_ops *ops,
+ u64 offset, u64 head)
+{
+ trace_event(event);
+
+ if (event->header.type < PERF_RECORD_HEADER_MAX) {
+ dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
+ offset + head, event->header.size,
+ event__name[event->header.type]);
+ hists__inc_nr_events(&self->hists, event->header.type);
+ }
+
+ if (self->header.needs_swap && event__swap_ops[event->header.type])
+ event__swap_ops[event->header.type](event);
+
+ switch (event->header.type) {
+ case PERF_RECORD_SAMPLE:
+ return perf_session__process_sample(event, self, ops);
+ case PERF_RECORD_MMAP:
+ return ops->mmap(event, self);
+ case PERF_RECORD_COMM:
+ return ops->comm(event, self);
+ case PERF_RECORD_FORK:
+ return ops->fork(event, self);
+ case PERF_RECORD_EXIT:
+ return ops->exit(event, self);
+ case PERF_RECORD_LOST:
+ return ops->lost(event, self);
+ case PERF_RECORD_READ:
+ return ops->read(event, self);
+ case PERF_RECORD_THROTTLE:
+ return ops->throttle(event, self);
+ case PERF_RECORD_UNTHROTTLE:
+ return ops->unthrottle(event, self);
+ case PERF_RECORD_HEADER_ATTR:
+ return ops->attr(event, self);
+ case PERF_RECORD_HEADER_EVENT_TYPE:
+ return ops->event_type(event, self);
+ case PERF_RECORD_HEADER_TRACING_DATA:
+ /* setup for reading amidst mmap */
+ lseek(self->fd, offset + head, SEEK_SET);
+ return ops->tracing_data(event, self);
+ case PERF_RECORD_HEADER_BUILD_ID:
+ return ops->build_id(event, self);
+ case PERF_RECORD_FINISHED_ROUND:
+ return ops->finished_round(event, self, ops);
+ default:
+ ++self->hists.stats.nr_unknown_events;
+ return -1;
+ }
+}
+
+void perf_event_header__bswap(struct perf_event_header *self)
+{
+ self->type = bswap_32(self->type);
+ self->misc = bswap_16(self->misc);
+ self->size = bswap_16(self->size);
+}
+
+static struct thread *perf_session__register_idle_thread(struct perf_session *self)
+{
+ struct thread *thread = perf_session__findnew(self, 0);
+
+ if (thread == NULL || thread__set_comm(thread, "swapper")) {
+ pr_err("problem inserting idle task.\n");
+ thread = NULL;
+ }
+
+ return thread;
+}
+
+int do_read(int fd, void *buf, size_t size)
+{
+ void *buf_start = buf;
+
+ while (size) {
+ int ret = read(fd, buf, size);
+
+ if (ret <= 0)
+ return ret;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return buf - buf_start;
+}
+
+#define session_done() (*(volatile int *)(&session_done))
+volatile int session_done;
+
+static int __perf_session__process_pipe_events(struct perf_session *self,
+ struct perf_event_ops *ops)
+{
+ event_t event;
+ uint32_t size;
+ int skip = 0;
+ u64 head;
+ int err;
+ void *p;
+
+ perf_event_ops__fill_defaults(ops);
+
+ head = 0;
+more:
+ err = do_read(self->fd, &event, sizeof(struct perf_event_header));
+ if (err <= 0) {
+ if (err == 0)
+ goto done;
+
+ pr_err("failed to read event header\n");
+ goto out_err;
+ }
+
+ if (self->header.needs_swap)
+ perf_event_header__bswap(&event.header);
+
+ size = event.header.size;
+ if (size == 0)
+ size = 8;
+
+ p = &event;
+ p += sizeof(struct perf_event_header);
+
+ if (size - sizeof(struct perf_event_header)) {
+ err = do_read(self->fd, p,
+ size - sizeof(struct perf_event_header));
+ if (err <= 0) {
+ if (err == 0) {
+ pr_err("unexpected end of event stream\n");
+ goto done;
+ }
+
+ pr_err("failed to read event data\n");
+ goto out_err;
+ }
+ }
+
+ if (size == 0 ||
+ (skip = perf_session__process_event(self, &event, ops,
+ 0, head)) < 0) {
+ dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
+ head, event.header.size, event.header.type);
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+
+ dump_printf("\n%#Lx [%#x]: event: %d\n",
+ head, event.header.size, event.header.type);
+
+ if (skip > 0)
+ head += skip;
+
+ if (!session_done())
+ goto more;
+done:
+ err = 0;
+out_err:
+ return err;
+}
+
+int __perf_session__process_events(struct perf_session *self,
+ u64 data_offset, u64 data_size,
+ u64 file_size, struct perf_event_ops *ops)
+{
+ int err, mmap_prot, mmap_flags;
+ u64 head, shift;
+ u64 offset = 0;
+ size_t page_size;
+ event_t *event;
+ uint32_t size;
+ char *buf;
+ struct ui_progress *progress = ui_progress__new("Processing events...",
+ self->size);
+ if (progress == NULL)
+ return -1;
+
+ perf_event_ops__fill_defaults(ops);
+
+ page_size = sysconf(_SC_PAGESIZE);
+
+ head = data_offset;
+ shift = page_size * (head / page_size);
+ offset += shift;
+ head -= shift;
+
+ mmap_prot = PROT_READ;
+ mmap_flags = MAP_SHARED;
+
+ if (self->header.needs_swap) {
+ mmap_prot |= PROT_WRITE;
+ mmap_flags = MAP_PRIVATE;
+ }
+remap:
+ buf = mmap(NULL, page_size * self->mmap_window, mmap_prot,
+ mmap_flags, self->fd, offset);
+ if (buf == MAP_FAILED) {
+ pr_err("failed to mmap file\n");
+ err = -errno;
+ goto out_err;
+ }
+
+more:
+ event = (event_t *)(buf + head);
+ ui_progress__update(progress, offset);
+
+ if (self->header.needs_swap)
+ perf_event_header__bswap(&event->header);
+ size = event->header.size;
+ if (size == 0)
+ size = 8;
+
+ if (head + event->header.size >= page_size * self->mmap_window) {
+ int munmap_ret;
+
+ shift = page_size * (head / page_size);
+
+ munmap_ret = munmap(buf, page_size * self->mmap_window);
+ assert(munmap_ret == 0);
+
+ offset += shift;
+ head -= shift;
+ goto remap;
+ }
+
+ size = event->header.size;
+
+ dump_printf("\n%#Lx [%#x]: event: %d\n",
+ offset + head, event->header.size, event->header.type);
+
+ if (size == 0 ||
+ perf_session__process_event(self, event, ops, offset, head) < 0) {
+ dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
+ offset + head, event->header.size,
+ event->header.type);
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+
+ if (offset + head >= data_offset + data_size)
+ goto done;
+
+ if (offset + head < file_size)
+ goto more;
+done:
+ err = 0;
+ /* do the final flush for ordered samples */
+ self->ordered_samples.next_flush = ULLONG_MAX;
+ flush_sample_queue(self, ops);
+out_err:
+ ui_progress__delete(progress);
+ return err;
+}
+
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *ops)
+{
+ int err;
+
+ if (perf_session__register_idle_thread(self) == NULL)
+ return -ENOMEM;
+
+ if (!symbol_conf.full_paths) {
+ char bf[PATH_MAX];
+
+ if (getcwd(bf, sizeof(bf)) == NULL) {
+ err = -errno;
+out_getcwd_err:
+ pr_err("failed to get the current directory\n");
+ goto out_err;
+ }
+ self->cwd = strdup(bf);
+ if (self->cwd == NULL) {
+ err = -ENOMEM;
+ goto out_getcwd_err;
+ }
+ self->cwdlen = strlen(self->cwd);
+ }
+
+ if (!self->fd_pipe)
+ err = __perf_session__process_events(self,
+ self->header.data_offset,
+ self->header.data_size,
+ self->size, ops);
+ else
+ err = __perf_session__process_pipe_events(self, ops);
+out_err:
+ return err;
+}
+
+bool perf_session__has_traces(struct perf_session *self, const char *msg)
+{
+ if (!(self->sample_type & PERF_SAMPLE_RAW)) {
+ pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
+ return false;
+ }
+
+ return true;
+}
+
+int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
+ const char *symbol_name,
+ u64 addr)
+{
+ char *bracket;
+ enum map_type i;
+ struct ref_reloc_sym *ref;
+
+ ref = zalloc(sizeof(struct ref_reloc_sym));
+ if (ref == NULL)
+ return -ENOMEM;
+
+ ref->name = strdup(symbol_name);
+ if (ref->name == NULL) {
+ free(ref);
+ return -ENOMEM;
+ }
+
+ bracket = strchr(ref->name, ']');
+ if (bracket)
+ *bracket = '\0';
+
+ ref->addr = addr;
+
+ for (i = 0; i < MAP__NR_TYPES; ++i) {
+ struct kmap *kmap = map__kmap(maps[i]);
+ kmap->ref_reloc_sym = ref;
+ }
+
+ return 0;
+}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
+{
+ return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
+ __dsos__fprintf(&self->host_machine.user_dsos, fp) +
+ machines__fprintf_dsos(&self->machines, fp);
+}
+
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
+ bool with_hits)
+{
+ size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
+ return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
+}
diff --git a/tools/lib/perf/session.h b/tools/lib/perf/session.h
new file mode 100644
index 0000000..1bbece2
--- /dev/null
+++ b/tools/lib/perf/session.h
@@ -0,0 +1,158 @@
+#ifndef __PERF_SESSION_H
+#define __PERF_SESSION_H
+
+#include <util/hist.h>
+#include <util/event.h>
+#include "symbol.h"
+#include <util/thread.h>
+#include <linux/bitops.h>
+#include <linux/rbtree.h>
+#include "../../../include/linux/perf_event.h"
+
+#define HEADER_FEAT_BITS 256
+
+struct sample_queue;
+struct ip_callchain;
+struct thread;
+
+struct perf_header {
+ int frozen;
+ int attrs, size;
+ bool needs_swap;
+ struct perf_header_attr **attr;
+ s64 attr_offset;
+ u64 data_offset;
+ u64 data_size;
+ u64 event_offset;
+ u64 event_size;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct ordered_samples {
+ u64 last_flush;
+ u64 next_flush;
+ u64 max_timestamp;
+ struct list_head samples_head;
+ struct sample_queue *last_inserted;
+};
+
+struct perf_session {
+ struct perf_header header;
+ unsigned long size;
+ unsigned long mmap_window;
+ struct rb_root threads;
+ struct thread *last_match;
+ struct machine host_machine;
+ struct rb_root machines;
+ struct rb_root hists_tree;
+ /*
+ * FIXME: should point to the first entry in hists_tree and
+ * be a hists instance. Right now its only 'report'
+ * that is using ->hists_tree while all the rest use
+ * ->hists.
+ */
+ struct hists hists;
+ u64 sample_type;
+ int fd;
+ bool fd_pipe;
+ bool repipe;
+ int cwdlen;
+ char *cwd;
+ struct ordered_samples ordered_samples;
+ char filename[0];
+};
+
+struct perf_event_ops;
+
+typedef int (*event_op)(event_t *self, struct perf_session *session);
+typedef int (*event_op2)(event_t *self, struct perf_session *session,
+ struct perf_event_ops *ops);
+
+struct perf_event_ops {
+ event_op sample,
+ mmap,
+ comm,
+ fork,
+ exit,
+ lost,
+ read,
+ throttle,
+ unthrottle,
+ attr,
+ event_type,
+ tracing_data,
+ build_id;
+ event_op2 finished_round;
+ bool ordered_samples;
+};
+
+struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe);
+void perf_session__delete(struct perf_session *self);
+
+void perf_event_header__bswap(struct perf_event_header *self);
+
+int __perf_session__process_events(struct perf_session *self,
+ u64 data_offset, u64 data_size, u64 size,
+ struct perf_event_ops *ops);
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *event_ops);
+
+struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent);
+
+bool perf_session__has_traces(struct perf_session *self, const char *msg);
+
+int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
+ const char *symbol_name,
+ u64 addr);
+
+void mem_bswap_64(void *src, int byte_size);
+
+int perf_session__create_kernel_maps(struct perf_session *self);
+
+int do_read(int fd, void *buf, size_t size);
+void perf_session__update_sample_type(struct perf_session *self);
+
+static inline
+struct machine *perf_session__find_host_machine(struct perf_session *self)
+{
+ return &self->host_machine;
+}
+
+static inline
+struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__find(&self->machines, pid);
+}
+
+static inline
+struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__findnew(&self->machines, pid);
+}
+
+static inline
+void perf_session__process_machines(struct perf_session *self,
+ machine__process_t process)
+{
+ process(&self->host_machine, self);
+ return machines__process(&self->machines, process, self);
+}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
+
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
+ FILE *fp, bool with_hits);
+
+static inline
+size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
+{
+ return hists__fprintf_nr_events(&self->hists, fp);
+}
+#endif /* __PERF_SESSION_H */
diff --git a/tools/lib/perf/symbol.c b/tools/lib/perf/symbol.c
new file mode 100644
index 0000000..04593d2
--- /dev/null
+++ b/tools/lib/perf/symbol.c
@@ -0,0 +1,2346 @@
+#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 <util/build-id.h>
+#include "symbol.h"
+#include <lk/strlist.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <limits.h>
+#include <sys/utsname.h>
+
+#include <lk/debug.h>
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+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,
+};
+
+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, 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->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 %s\n",
+ self->start, self->end, 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->long_name != self->name)
+ 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 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, 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, 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,
+ 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,
+ 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)
+{
+ 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_err("%s: cannot read %s ELF file.\n", __func__, name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
+ if (sec == NULL) {
+ 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;
+
+ 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, 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)
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+ 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;
+ }
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out_elf_end;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ 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_elf_end;
+ }
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out_elf_end;
+
+ 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_elf_end:
+ 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;
+ u8 build_id[BUILD_ID_SIZE];
+ int ret = -1;
+ int fd;
+ struct machine *machine;
+ const char *root_dir;
+
+ 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;
+ }
+
+ self->origin = DSO__ORIG_BUILD_ID_CACHE;
+ if (dso__build_id_filename(self, name, size) != NULL)
+ goto open_file;
+more:
+ do {
+ self->origin++;
+ switch (self->origin) {
+ 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:
+ if (filename__read_build_id(self->long_name, build_id,
+ sizeof(build_id))) {
+ char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+ build_id__sprintf(build_id, sizeof(build_id),
+ build_id_hex);
+ snprintf(name, size,
+ "/usr/lib/debug/.build-id/%.2s/%s.debug",
+ build_id_hex, build_id_hex + 2);
+ if (self->has_build_id)
+ goto compare_build_id;
+ break;
+ }
+ self->origin++;
+ /* Fall thru */
+ 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:
+ goto out;
+ }
+
+ if (self->has_build_id) {
+ if (filename__read_build_id(name, build_id,
+ sizeof(build_id)) < 0)
+ goto more;
+compare_build_id:
+ if (!dso__build_id_equal(self, build_id))
+ goto more;
+ }
+open_file:
+ fd = open(name, O_RDONLY);
+ } while (fd < 0);
+
+ ret = dso__load_sym(self, map, name, fd, filter, 0);
+ close(fd);
+
+ /*
+ * Some people seem to have debuginfo files _WITHOUT_ debug info!?!?
+ */
+ if (!ret)
+ goto more;
+
+ if (ret > 0) {
+ int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
+ if (nr_plt > 0)
+ ret += nr_plt;
+ }
+out:
+ 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);
+
+ 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);
+ if (map_groups__set_modules_path_dir(self, path) < 0)
+ goto failure;
+ } 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)
+ goto failure;
+ dso__set_long_name(map->dso, long_name);
+ dso__kernel_module_get_build_id(map->dso, "");
+ }
+ }
+
+ return 0;
+failure:
+ closedir(dir);
+ return -1;
+}
+
+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;
+
+ if (self->has_build_id) {
+ u8 build_id[BUILD_ID_SIZE];
+
+ if (filename__read_build_id(vmlinux, build_id,
+ sizeof(build_id)) < 0) {
+ pr_debug("No build_id in %s, ignoring it\n", vmlinux);
+ return -1;
+ }
+ if (!dso__build_id_equal(self, build_id)) {
+ char expected_build_id[BUILD_ID_SIZE * 2 + 1],
+ vmlinux_build_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->build_id,
+ sizeof(self->build_id),
+ expected_build_id);
+ build_id__sprintf(build_id, sizeof(build_id),
+ vmlinux_build_id);
+ pr_debug("build_id in %s is %s while expected is %s, "
+ "ignoring it\n", vmlinux, vmlinux_build_id,
+ expected_build_id);
+ return -1;
+ }
+ }
+
+ 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);
+ 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;
+}
+
+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)
+{
+ 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;
+
+ return 0;
+
+out_free_dso_list:
+ strlist__delete(symbol_conf.dso_list);
+out_free_comm_list:
+ strlist__delete(symbol_conf.comm_list);
+ return -1;
+}
+
+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;
+}
+
+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..80e569b
--- /dev/null
+++ b/tools/lib/perf/symbol.h
@@ -0,0 +1,221 @@
+#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 <linux/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;
+ 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,
+ full_paths,
+ show_cpu_utilization;
+ 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;
+};
+
+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];
+ u8 adjust_symbols:1;
+ u8 slen_calculated:1;
+ u8 has_build_id:1;
+ enum dso_kernel_type kernel;
+ u8 hit:1;
+ u8 annotate_warned: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);
+
+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));
+
+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);
+
+int symbol__init(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/Makefile b/tools/perf/Makefile
index 0a5b00f..653802b 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -358,15 +358,12 @@ LIB_H += util/build-id.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/quote.h
LIB_H += util/help.h
-LIB_H += util/session.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
@@ -390,12 +387,9 @@ LIB_OBJS += $(OUTPUT)util/run-command.o
LIB_OBJS += $(OUTPUT)util/quote.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/callchain.o
LIB_OBJS += $(OUTPUT)util/values.o
-LIB_OBJS += $(OUTPUT)util/map.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
@@ -535,41 +529,6 @@ else
endif
endif
-ifdef NO_DEMANGLE
- BASIC_CFLAGS += -DNO_DEMANGLE
-else ifdef HAVE_CPLUS_DEMANGLE
- EXTLIBS += -liberty
- BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
-else
- FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
- has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
- ifeq ($(has_bfd),y)
- EXTLIBS += -lbfd
- else
- FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
- has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
- ifeq ($(has_bfd_iberty),y)
- EXTLIBS += -lbfd -liberty
- else
- FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
- has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
- ifeq ($(has_bfd_iberty_z),y)
- EXTLIBS += -lbfd -liberty -lz
- else
- FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
- has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
- ifeq ($(has_cplus_demangle),y)
- EXTLIBS += -liberty
- BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
- else
- msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)
- BASIC_CFLAGS += -DNO_DEMANGLE
- endif
- endif
- endif
- endif
-endif
-
ifndef CC_LD_DYNPATH
ifdef NO_R_TO_GCC_LINKER
# Some gcc does not accept and pass -R to the linker to specify
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index a4d9620..1c78b75 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 <linux/rbtree.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "perf.h"
#include <lk/debug.h>
@@ -24,7 +24,7 @@
#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
-#include "util/session.h"
+#include <perf/session.h>
static char const *input_name = "perf.data";
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 5fe42b6..440a92b 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -8,12 +8,12 @@
*/
#include "builtin.h"
#include "perf.h"
-#include "util/cache.h"
#include <lk/debug.h>
+#include <lk/strlist.h>
#include <perf/header.h>
+#include <perf/symbol.h>
+#include "util/cache.h"
#include "util/parse-options.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-buildid-list.c b/tools/perf/builtin-buildid-list.c
index b767dde..8cfc4d3 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -12,8 +12,8 @@
#include "util/cache.h"
#include <lk/debug.h>
#include "util/parse-options.h"
-#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/session.h>
+#include <perf/symbol.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 b346e7f..2d406dd 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -9,9 +9,9 @@
#include <lk/debug.h>
#include "util/event.h"
#include "util/hist.h"
-#include "util/session.h"
+#include <perf/session.h>
#include "util/sort.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include <lk/util.h>
#include <stdlib.h>
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index a6c3caa..fd1092e 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -8,7 +8,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/session.h"
+#include <perf/session.h>
#include <lk/debug.h>
#include <perf/header.h>
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 6003678..3129210 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -3,10 +3,10 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include <perf/header.h>
-#include "util/session.h"
+#include <perf/session.h>
#include "util/parse-options.h"
#include "util/trace-event.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 1a110fa..509d11f 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -3,10 +3,10 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include <perf/header.h>
-#include "util/session.h"
+#include <perf/session.h>
#include "util/parse-options.h"
#include "util/trace-event.h"
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index dc229ab..17abce4 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/symbol.h>
#include "util/thread.h"
#include <perf/header.h>
@@ -11,7 +11,7 @@
#include "util/trace-event.h"
#include <lk/debug.h>
-#include "util/session.h"
+#include <perf/session.h>
#include <sys/types.h>
#include <sys/prctl.h>
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index f53edba..113213f 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -36,9 +36,9 @@
#include "builtin.h"
#include <lk/util.h>
#include <lk/strlist.h>
-#include "util/symbol.h"
#include <lk/debug.h>
#include <lk/debugfs.h>
+#include <perf/symbol.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 34bc049..e78efad 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -19,8 +19,8 @@
#include <perf/header.h>
#include "util/event.h"
#include <lk/debug.h>
-#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/session.h>
+#include <perf/symbol.h>
#include <lk/cpumap.h>
#include <unistd.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 266f721..98284d1 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 <linux/rbtree.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/callchain.h"
#include <lk/strlist.h>
#include "util/values.h"
@@ -21,7 +21,7 @@
#include "perf.h"
#include <lk/debug.h>
#include <perf/header.h>
-#include "util/session.h"
+#include <perf/session.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 6af08bf..24dabdc 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -3,10 +3,10 @@
#include <lk/util.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include <perf/header.h>
-#include "util/session.h"
+#include <perf/session.h>
#include "util/parse-options.h"
#include "util/trace-event.h"
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 1be7ad9..f5d6686 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -8,8 +8,8 @@
#include "util/cache.h"
#include <lk/debug.h>
#include "util/parse-options.h"
-#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/session.h>
+#include <perf/symbol.h>
#include "util/thread.h"
static long page_size;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 1699cf6..eddeeae 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 <linux/rbtree.h>
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/callchain.h"
#include <lk/strlist.h>
@@ -29,7 +29,7 @@
#include "util/parse-options.h"
#include <perf/parse-events.h>
#include "util/event.h"
-#include "util/session.h"
+#include <perf/session.h>
#include "util/svghelper.h"
static char const *input_name = "perf.data";
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index e4a5783..8edc974 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -21,8 +21,8 @@
#include "perf.h"
#include <lk/color.h>
-#include "util/session.h"
-#include "util/symbol.h"
+#include <perf/session.h>
+#include <perf/symbol.h>
#include "util/thread.h"
#include <lk/util.h>
#include <linux/rbtree.h>
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 6c3bc42..1edffa4 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -3,12 +3,12 @@
#include <lk/util.h>
#include <lk/debug.h>
#include "util/cache.h"
-#include "util/symbol.h"
+#include <perf/symbol.h>
#include "util/thread.h"
#include <perf/header.h>
#include "util/exec_cmd.h"
#include "util/trace-event.h"
-#include "util/session.h"
+#include <perf/session.h>
static char const *script_name;
static char const *generate_script_lang;
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index a338745..5969758 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -13,7 +13,7 @@
#include <stdio.h>
#include "build-id.h"
#include "event.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#include <linux/kernel.h>
static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index e72ed79..a8a6bd9 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -1,7 +1,7 @@
#ifndef PERF_BUILD_ID_H_
#define PERF_BUILD_ID_H_ 1
-#include "session.h"
+#include <perf/session.h>
#include "config.h"
extern struct perf_event_ops build_id__mark_dso_hit_ops;
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 809850f..ca8a73d 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -5,7 +5,7 @@
#include <linux/list.h>
#include <linux/rbtree.h>
#include "event.h"
-#include "symbol.h"
+#include <perf/symbol.h>
enum chain_mode {
CHAIN_NONE,
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index fda3406..1669fcc 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1,7 +1,7 @@
#include <linux/types.h>
#include "event.h"
#include <lk/debug.h>
-#include "session.h"
+#include <perf/session.h>
#include "sort.h"
#include "string.h"
#include <lk/strlist.h>
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 887ee63..fef5236 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -4,7 +4,7 @@
#include <limits.h>
#include "../perf.h"
-#include "map.h"
+#include <perf/map.h>
/*
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index a409e27..2dfa141 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,7 +1,7 @@
#include <lk/util.h>
#include "build-id.h"
#include "hist.h"
-#include "session.h"
+#include <perf/session.h>
#include "sort.h"
#include <math.h>
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
deleted file mode 100644
index fa82eba..0000000
--- a/tools/perf/util/map.c
+++ /dev/null
@@ -1,630 +0,0 @@
-#include "symbol.h"
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <lk/debug.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;
-}
-
-static int strcommon(const char *pathname, char *cwd, int cwdlen)
-{
- int n = 0;
-
- while (n < cwdlen && pathname[n] == cwd[n])
- ++n;
-
- return n;
-}
-
-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;
-}
-
-struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
- u64 pgoff, u32 pid, char *filename,
- enum map_type type, char *cwd, int cwdlen)
-{
- struct map *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- char newfilename[PATH_MAX];
- struct dso *dso;
- int anon;
-
- if (cwd) {
- int n = strcommon(filename, cwd, cwdlen);
-
- if (n == cwdlen) {
- snprintf(newfilename, sizeof(newfilename),
- ".%s", filename + n);
- filename = newfilename;
- }
- }
-
- 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;
-}
-
-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);
-
- 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);
- /*
- * 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[map->type]);
- /*
- * 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)
- return -ENOMEM;
-
- 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)
- return -ENOMEM;
-
- after->start = map->end + 1;
- map_groups__insert(self, after);
- if (_verbose >= 2)
- map__fprintf(after, fp);
- }
- }
-
- 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);
-}
-
-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;
-}
-
-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 c7ed844..0000000
--- a/tools/perf/util/map.h
+++ /dev/null
@@ -1,217 +0,0 @@
-#ifndef __PERF_MAP_H
-#define __PERF_MAP_H
-
-#include <linux/compiler.h>
-#include <linux/list.h>
-#include <linux/rbtree.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <lk/types.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;
- enum map_type type;
- 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, char *cwd, int cwdlen);
-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);
-struct map *maps__find(struct rb_root *maps, u64 addr);
-void map_groups__init(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);
-
-/*
- * 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 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/newt.c b/tools/perf/util/newt.c
index 3918f22..63f89e0 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -18,9 +18,9 @@
#include "cache.h"
#include "hist.h"
#include "pstack.h"
-#include "session.h"
+#include <perf/session.h>
#include "sort.h"
-#include "symbol.h"
+#include <perf/symbol.h>
#if SLANG_VERSION < 20104
#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 26f29ca..d3e911b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -40,7 +40,7 @@
#include <lk/debug.h>
#include "cache.h"
#include <lk/color.h>
-#include "symbol.h"
+#include <perf/symbol.h>
#include "thread.h"
#include <lk/debugfs.h>
#include "trace-event.h" /* For __unused */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 8ac178d..789c583 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -37,7 +37,7 @@
#include "event.h"
#include <lk/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.c b/tools/perf/util/session.c
deleted file mode 100644
index 638ecd0..0000000
--- a/tools/perf/util/session.c
+++ /dev/null
@@ -1,906 +0,0 @@
-#define _FILE_OFFSET_BITS 64
-
-#include <linux/kernel.h>
-
-#include <byteswap.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include "session.h"
-#include "sort.h"
-#include <lk/util.h>
-
-static int perf_session__open(struct perf_session *self, bool force)
-{
- struct stat input_stat;
-
- if (!strcmp(self->filename, "-")) {
- self->fd_pipe = true;
- self->fd = STDIN_FILENO;
-
- if (perf_header__read(self, self->fd) < 0)
- pr_err("incompatible file format");
-
- return 0;
- }
-
- self->fd = open(self->filename, O_RDONLY);
- if (self->fd < 0) {
- int err = errno;
-
- pr_err("failed to open %s: %s", self->filename, strerror(err));
- if (err == ENOENT && !strcmp(self->filename, "perf.data"))
- pr_err(" (try 'perf record' first)");
- pr_err("\n");
- return -errno;
- }
-
- if (fstat(self->fd, &input_stat) < 0)
- goto out_close;
-
- if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
- pr_err("file %s not owned by current user or root\n",
- self->filename);
- goto out_close;
- }
-
- if (!input_stat.st_size) {
- pr_info("zero-sized file (%s), nothing to do!\n",
- self->filename);
- goto out_close;
- }
-
- if (perf_header__read(self, self->fd) < 0) {
- pr_err("incompatible file format");
- goto out_close;
- }
-
- self->size = input_stat.st_size;
- return 0;
-
-out_close:
- close(self->fd);
- self->fd = -1;
- return -1;
-}
-
-void perf_session__update_sample_type(struct perf_session *self)
-{
- self->sample_type = perf_header__sample_type(&self->header);
-}
-
-int perf_session__create_kernel_maps(struct perf_session *self)
-{
- int ret = machine__create_kernel_maps(&self->host_machine);
-
- if (ret >= 0)
- ret = machines__create_guest_kernel_maps(&self->machines);
- return ret;
-}
-
-struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
-{
- size_t len = filename ? strlen(filename) + 1 : 0;
- struct perf_session *self = zalloc(sizeof(*self) + len);
-
- if (self == NULL)
- goto out;
-
- if (perf_header__init(&self->header) < 0)
- goto out_free;
-
- memcpy(self->filename, filename, len);
- self->threads = RB_ROOT;
- self->hists_tree = RB_ROOT;
- self->last_match = NULL;
- self->mmap_window = 32;
- self->cwd = NULL;
- self->cwdlen = 0;
- self->machines = RB_ROOT;
- self->repipe = repipe;
- INIT_LIST_HEAD(&self->ordered_samples.samples_head);
- machine__init(&self->host_machine, "", HOST_KERNEL_ID);
-
- if (mode == O_RDONLY) {
- if (perf_session__open(self, force) < 0)
- goto out_delete;
- } else if (mode == O_WRONLY) {
- /*
- * In O_RDONLY mode this will be performed when reading the
- * kernel MMAP event, in event__process_mmap().
- */
- if (perf_session__create_kernel_maps(self) < 0)
- goto out_delete;
- }
-
- perf_session__update_sample_type(self);
-out:
- return self;
-out_free:
- free(self);
- return NULL;
-out_delete:
- perf_session__delete(self);
- return NULL;
-}
-
-void perf_session__delete(struct perf_session *self)
-{
- perf_header__exit(&self->header);
- close(self->fd);
- free(self->cwd);
- free(self);
-}
-
-static bool symbol__match_parent_regex(struct symbol *sym)
-{
- if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
- return 1;
-
- return 0;
-}
-
-struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
- struct thread *thread,
- struct ip_callchain *chain,
- struct symbol **parent)
-{
- u8 cpumode = PERF_RECORD_MISC_USER;
- unsigned int i;
- struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
-
- if (!syms)
- return NULL;
-
- for (i = 0; i < chain->nr; i++) {
- u64 ip = chain->ips[i];
- struct addr_location al;
-
- if (ip >= PERF_CONTEXT_MAX) {
- switch (ip) {
- case PERF_CONTEXT_HV:
- cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
- case PERF_CONTEXT_KERNEL:
- cpumode = PERF_RECORD_MISC_KERNEL; break;
- case PERF_CONTEXT_USER:
- cpumode = PERF_RECORD_MISC_USER; break;
- default:
- break;
- }
- continue;
- }
-
- al.filtered = false;
- thread__find_addr_location(thread, self, cpumode,
- MAP__FUNCTION, thread->pid, ip, &al, NULL);
- if (al.sym != NULL) {
- if (sort__has_parent && !*parent &&
- symbol__match_parent_regex(al.sym))
- *parent = al.sym;
- if (!symbol_conf.use_callchain)
- break;
- syms[i].map = al.map;
- syms[i].sym = al.sym;
- }
- }
-
- return syms;
-}
-
-static int process_event_stub(event_t *event __used,
- struct perf_session *session __used)
-{
- dump_printf(": unhandled!\n");
- return 0;
-}
-
-static int process_finished_round_stub(event_t *event __used,
- struct perf_session *session __used,
- struct perf_event_ops *ops __used)
-{
- dump_printf(": unhandled!\n");
- return 0;
-}
-
-static int process_finished_round(event_t *event,
- struct perf_session *session,
- struct perf_event_ops *ops);
-
-static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
-{
- if (handler->sample == NULL)
- handler->sample = process_event_stub;
- if (handler->mmap == NULL)
- handler->mmap = process_event_stub;
- if (handler->comm == NULL)
- handler->comm = process_event_stub;
- if (handler->fork == NULL)
- handler->fork = process_event_stub;
- if (handler->exit == NULL)
- handler->exit = process_event_stub;
- if (handler->lost == NULL)
- handler->lost = process_event_stub;
- if (handler->read == NULL)
- handler->read = process_event_stub;
- if (handler->throttle == NULL)
- handler->throttle = process_event_stub;
- if (handler->unthrottle == NULL)
- handler->unthrottle = process_event_stub;
- if (handler->attr == NULL)
- handler->attr = process_event_stub;
- if (handler->event_type == NULL)
- handler->event_type = process_event_stub;
- if (handler->tracing_data == NULL)
- handler->tracing_data = process_event_stub;
- if (handler->build_id == NULL)
- handler->build_id = process_event_stub;
- if (handler->finished_round == NULL) {
- if (handler->ordered_samples)
- handler->finished_round = process_finished_round;
- else
- handler->finished_round = process_finished_round_stub;
- }
-}
-
-void mem_bswap_64(void *src, int byte_size)
-{
- u64 *m = src;
-
- while (byte_size > 0) {
- *m = bswap_64(*m);
- byte_size -= sizeof(u64);
- ++m;
- }
-}
-
-static void event__all64_swap(event_t *self)
-{
- struct perf_event_header *hdr = &self->header;
- mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
-}
-
-static void event__comm_swap(event_t *self)
-{
- self->comm.pid = bswap_32(self->comm.pid);
- self->comm.tid = bswap_32(self->comm.tid);
-}
-
-static void event__mmap_swap(event_t *self)
-{
- self->mmap.pid = bswap_32(self->mmap.pid);
- self->mmap.tid = bswap_32(self->mmap.tid);
- self->mmap.start = bswap_64(self->mmap.start);
- self->mmap.len = bswap_64(self->mmap.len);
- self->mmap.pgoff = bswap_64(self->mmap.pgoff);
-}
-
-static void event__task_swap(event_t *self)
-{
- self->fork.pid = bswap_32(self->fork.pid);
- self->fork.tid = bswap_32(self->fork.tid);
- self->fork.ppid = bswap_32(self->fork.ppid);
- self->fork.ptid = bswap_32(self->fork.ptid);
- self->fork.time = bswap_64(self->fork.time);
-}
-
-static void event__read_swap(event_t *self)
-{
- self->read.pid = bswap_32(self->read.pid);
- self->read.tid = bswap_32(self->read.tid);
- self->read.value = bswap_64(self->read.value);
- self->read.time_enabled = bswap_64(self->read.time_enabled);
- self->read.time_running = bswap_64(self->read.time_running);
- self->read.id = bswap_64(self->read.id);
-}
-
-static void event__attr_swap(event_t *self)
-{
- size_t size;
-
- self->attr.attr.type = bswap_32(self->attr.attr.type);
- self->attr.attr.size = bswap_32(self->attr.attr.size);
- self->attr.attr.config = bswap_64(self->attr.attr.config);
- self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period);
- self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type);
- self->attr.attr.read_format = bswap_64(self->attr.attr.read_format);
- self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events);
- self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type);
- self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr);
- self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len);
-
- size = self->header.size;
- size -= (void *)&self->attr.id - (void *)self;
- mem_bswap_64(self->attr.id, size);
-}
-
-static void event__event_type_swap(event_t *self)
-{
- self->event_type.event_type.event_id =
- bswap_64(self->event_type.event_type.event_id);
-}
-
-static void event__tracing_data_swap(event_t *self)
-{
- self->tracing_data.size = bswap_32(self->tracing_data.size);
-}
-
-typedef void (*event__swap_op)(event_t *self);
-
-static event__swap_op event__swap_ops[] = {
- [PERF_RECORD_MMAP] = event__mmap_swap,
- [PERF_RECORD_COMM] = event__comm_swap,
- [PERF_RECORD_FORK] = event__task_swap,
- [PERF_RECORD_EXIT] = event__task_swap,
- [PERF_RECORD_LOST] = event__all64_swap,
- [PERF_RECORD_READ] = event__read_swap,
- [PERF_RECORD_SAMPLE] = event__all64_swap,
- [PERF_RECORD_HEADER_ATTR] = event__attr_swap,
- [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
- [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
- [PERF_RECORD_HEADER_BUILD_ID] = NULL,
- [PERF_RECORD_HEADER_MAX] = NULL,
-};
-
-struct sample_queue {
- u64 timestamp;
- struct sample_event *event;
- struct list_head list;
-};
-
-static void flush_sample_queue(struct perf_session *s,
- struct perf_event_ops *ops)
-{
- struct list_head *head = &s->ordered_samples.samples_head;
- u64 limit = s->ordered_samples.next_flush;
- struct sample_queue *tmp, *iter;
-
- if (!ops->ordered_samples || !limit)
- return;
-
- list_for_each_entry_safe(iter, tmp, head, list) {
- if (iter->timestamp > limit)
- return;
-
- if (iter == s->ordered_samples.last_inserted)
- s->ordered_samples.last_inserted = NULL;
-
- ops->sample((event_t *)iter->event, s);
-
- s->ordered_samples.last_flush = iter->timestamp;
- list_del(&iter->list);
- free(iter->event);
- free(iter);
- }
-}
-
-/*
- * When perf record finishes a pass on every buffers, it records this pseudo
- * event.
- * We record the max timestamp t found in the pass n.
- * Assuming these timestamps are monotonic across cpus, we know that if
- * a buffer still has events with timestamps below t, they will be all
- * available and then read in the pass n + 1.
- * Hence when we start to read the pass n + 2, we can safely flush every
- * events with timestamps below t.
- *
- * ============ PASS n =================
- * CPU 0 | CPU 1
- * |
- * cnt1 timestamps | cnt2 timestamps
- * 1 | 2
- * 2 | 3
- * - | 4 <--- max recorded
- *
- * ============ PASS n + 1 ==============
- * CPU 0 | CPU 1
- * |
- * cnt1 timestamps | cnt2 timestamps
- * 3 | 5
- * 4 | 6
- * 5 | 7 <---- max recorded
- *
- * Flush every events below timestamp 4
- *
- * ============ PASS n + 2 ==============
- * CPU 0 | CPU 1
- * |
- * cnt1 timestamps | cnt2 timestamps
- * 6 | 8
- * 7 | 9
- * - | 10
- *
- * Flush every events below timestamp 7
- * etc...
- */
-static int process_finished_round(event_t *event __used,
- struct perf_session *session,
- struct perf_event_ops *ops)
-{
- flush_sample_queue(session, ops);
- session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
-
- return 0;
-}
-
-static void __queue_sample_end(struct sample_queue *new, struct list_head *head)
-{
- struct sample_queue *iter;
-
- list_for_each_entry_reverse(iter, head, list) {
- if (iter->timestamp < new->timestamp) {
- list_add(&new->list, &iter->list);
- return;
- }
- }
-
- list_add(&new->list, head);
-}
-
-static void __queue_sample_before(struct sample_queue *new,
- struct sample_queue *iter,
- struct list_head *head)
-{
- list_for_each_entry_continue_reverse(iter, head, list) {
- if (iter->timestamp < new->timestamp) {
- list_add(&new->list, &iter->list);
- return;
- }
- }
-
- list_add(&new->list, head);
-}
-
-static void __queue_sample_after(struct sample_queue *new,
- struct sample_queue *iter,
- struct list_head *head)
-{
- list_for_each_entry_continue(iter, head, list) {
- if (iter->timestamp > new->timestamp) {
- list_add_tail(&new->list, &iter->list);
- return;
- }
- }
- list_add_tail(&new->list, head);
-}
-
-/* The queue is ordered by time */
-static void __queue_sample_event(struct sample_queue *new,
- struct perf_session *s)
-{
- struct sample_queue *last_inserted = s->ordered_samples.last_inserted;
- struct list_head *head = &s->ordered_samples.samples_head;
-
-
- if (!last_inserted) {
- __queue_sample_end(new, head);
- return;
- }
-
- /*
- * Most of the time the current event has a timestamp
- * very close to the last event inserted, unless we just switched
- * to another event buffer. Having a sorting based on a list and
- * on the last inserted event that is close to the current one is
- * probably more efficient than an rbtree based sorting.
- */
- if (last_inserted->timestamp >= new->timestamp)
- __queue_sample_before(new, last_inserted, head);
- else
- __queue_sample_after(new, last_inserted, head);
-}
-
-static int queue_sample_event(event_t *event, struct sample_data *data,
- struct perf_session *s)
-{
- u64 timestamp = data->time;
- struct sample_queue *new;
-
-
- if (timestamp < s->ordered_samples.last_flush) {
- printf("Warning: Timestamp below last timeslice flush\n");
- return -EINVAL;
- }
-
- new = malloc(sizeof(*new));
- if (!new)
- return -ENOMEM;
-
- new->timestamp = timestamp;
-
- new->event = malloc(event->header.size);
- if (!new->event) {
- free(new);
- return -ENOMEM;
- }
-
- memcpy(new->event, event, event->header.size);
-
- __queue_sample_event(new, s);
- s->ordered_samples.last_inserted = new;
-
- if (new->timestamp > s->ordered_samples.max_timestamp)
- s->ordered_samples.max_timestamp = new->timestamp;
-
- return 0;
-}
-
-static int perf_session__process_sample(event_t *event, struct perf_session *s,
- struct perf_event_ops *ops)
-{
- struct sample_data data;
-
- if (!ops->ordered_samples)
- return ops->sample(event, s);
-
- bzero(&data, sizeof(struct sample_data));
- event__parse_sample(event, s->sample_type, &data);
-
- queue_sample_event(event, &data, s);
-
- return 0;
-}
-
-static int perf_session__process_event(struct perf_session *self,
- event_t *event,
- struct perf_event_ops *ops,
- u64 offset, u64 head)
-{
- trace_event(event);
-
- if (event->header.type < PERF_RECORD_HEADER_MAX) {
- dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
- offset + head, event->header.size,
- event__name[event->header.type]);
- hists__inc_nr_events(&self->hists, event->header.type);
- }
-
- if (self->header.needs_swap && event__swap_ops[event->header.type])
- event__swap_ops[event->header.type](event);
-
- switch (event->header.type) {
- case PERF_RECORD_SAMPLE:
- return perf_session__process_sample(event, self, ops);
- case PERF_RECORD_MMAP:
- return ops->mmap(event, self);
- case PERF_RECORD_COMM:
- return ops->comm(event, self);
- case PERF_RECORD_FORK:
- return ops->fork(event, self);
- case PERF_RECORD_EXIT:
- return ops->exit(event, self);
- case PERF_RECORD_LOST:
- return ops->lost(event, self);
- case PERF_RECORD_READ:
- return ops->read(event, self);
- case PERF_RECORD_THROTTLE:
- return ops->throttle(event, self);
- case PERF_RECORD_UNTHROTTLE:
- return ops->unthrottle(event, self);
- case PERF_RECORD_HEADER_ATTR:
- return ops->attr(event, self);
- case PERF_RECORD_HEADER_EVENT_TYPE:
- return ops->event_type(event, self);
- case PERF_RECORD_HEADER_TRACING_DATA:
- /* setup for reading amidst mmap */
- lseek(self->fd, offset + head, SEEK_SET);
- return ops->tracing_data(event, self);
- case PERF_RECORD_HEADER_BUILD_ID:
- return ops->build_id(event, self);
- case PERF_RECORD_FINISHED_ROUND:
- return ops->finished_round(event, self, ops);
- default:
- ++self->hists.stats.nr_unknown_events;
- return -1;
- }
-}
-
-void perf_event_header__bswap(struct perf_event_header *self)
-{
- self->type = bswap_32(self->type);
- self->misc = bswap_16(self->misc);
- self->size = bswap_16(self->size);
-}
-
-static struct thread *perf_session__register_idle_thread(struct perf_session *self)
-{
- struct thread *thread = perf_session__findnew(self, 0);
-
- if (thread == NULL || thread__set_comm(thread, "swapper")) {
- pr_err("problem inserting idle task.\n");
- thread = NULL;
- }
-
- return thread;
-}
-
-int do_read(int fd, void *buf, size_t size)
-{
- void *buf_start = buf;
-
- while (size) {
- int ret = read(fd, buf, size);
-
- if (ret <= 0)
- return ret;
-
- size -= ret;
- buf += ret;
- }
-
- return buf - buf_start;
-}
-
-#define session_done() (*(volatile int *)(&session_done))
-volatile int session_done;
-
-static int __perf_session__process_pipe_events(struct perf_session *self,
- struct perf_event_ops *ops)
-{
- event_t event;
- uint32_t size;
- int skip = 0;
- u64 head;
- int err;
- void *p;
-
- perf_event_ops__fill_defaults(ops);
-
- head = 0;
-more:
- err = do_read(self->fd, &event, sizeof(struct perf_event_header));
- if (err <= 0) {
- if (err == 0)
- goto done;
-
- pr_err("failed to read event header\n");
- goto out_err;
- }
-
- if (self->header.needs_swap)
- perf_event_header__bswap(&event.header);
-
- size = event.header.size;
- if (size == 0)
- size = 8;
-
- p = &event;
- p += sizeof(struct perf_event_header);
-
- if (size - sizeof(struct perf_event_header)) {
- err = do_read(self->fd, p,
- size - sizeof(struct perf_event_header));
- if (err <= 0) {
- if (err == 0) {
- pr_err("unexpected end of event stream\n");
- goto done;
- }
-
- pr_err("failed to read event data\n");
- goto out_err;
- }
- }
-
- if (size == 0 ||
- (skip = perf_session__process_event(self, &event, ops,
- 0, head)) < 0) {
- dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
- head, event.header.size, event.header.type);
- /*
- * assume we lost track of the stream, check alignment, and
- * increment a single u64 in the hope to catch on again 'soon'.
- */
- if (unlikely(head & 7))
- head &= ~7ULL;
-
- size = 8;
- }
-
- head += size;
-
- dump_printf("\n%#Lx [%#x]: event: %d\n",
- head, event.header.size, event.header.type);
-
- if (skip > 0)
- head += skip;
-
- if (!session_done())
- goto more;
-done:
- err = 0;
-out_err:
- return err;
-}
-
-int __perf_session__process_events(struct perf_session *self,
- u64 data_offset, u64 data_size,
- u64 file_size, struct perf_event_ops *ops)
-{
- int err, mmap_prot, mmap_flags;
- u64 head, shift;
- u64 offset = 0;
- size_t page_size;
- event_t *event;
- uint32_t size;
- char *buf;
- struct ui_progress *progress = ui_progress__new("Processing events...",
- self->size);
- if (progress == NULL)
- return -1;
-
- perf_event_ops__fill_defaults(ops);
-
- page_size = sysconf(_SC_PAGESIZE);
-
- head = data_offset;
- shift = page_size * (head / page_size);
- offset += shift;
- head -= shift;
-
- mmap_prot = PROT_READ;
- mmap_flags = MAP_SHARED;
-
- if (self->header.needs_swap) {
- mmap_prot |= PROT_WRITE;
- mmap_flags = MAP_PRIVATE;
- }
-remap:
- buf = mmap(NULL, page_size * self->mmap_window, mmap_prot,
- mmap_flags, self->fd, offset);
- if (buf == MAP_FAILED) {
- pr_err("failed to mmap file\n");
- err = -errno;
- goto out_err;
- }
-
-more:
- event = (event_t *)(buf + head);
- ui_progress__update(progress, offset);
-
- if (self->header.needs_swap)
- perf_event_header__bswap(&event->header);
- size = event->header.size;
- if (size == 0)
- size = 8;
-
- if (head + event->header.size >= page_size * self->mmap_window) {
- int munmap_ret;
-
- shift = page_size * (head / page_size);
-
- munmap_ret = munmap(buf, page_size * self->mmap_window);
- assert(munmap_ret == 0);
-
- offset += shift;
- head -= shift;
- goto remap;
- }
-
- size = event->header.size;
-
- dump_printf("\n%#Lx [%#x]: event: %d\n",
- offset + head, event->header.size, event->header.type);
-
- if (size == 0 ||
- perf_session__process_event(self, event, ops, offset, head) < 0) {
- dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
- offset + head, event->header.size,
- event->header.type);
- /*
- * assume we lost track of the stream, check alignment, and
- * increment a single u64 in the hope to catch on again 'soon'.
- */
- if (unlikely(head & 7))
- head &= ~7ULL;
-
- size = 8;
- }
-
- head += size;
-
- if (offset + head >= data_offset + data_size)
- goto done;
-
- if (offset + head < file_size)
- goto more;
-done:
- err = 0;
- /* do the final flush for ordered samples */
- self->ordered_samples.next_flush = ULLONG_MAX;
- flush_sample_queue(self, ops);
-out_err:
- ui_progress__delete(progress);
- return err;
-}
-
-int perf_session__process_events(struct perf_session *self,
- struct perf_event_ops *ops)
-{
- int err;
-
- if (perf_session__register_idle_thread(self) == NULL)
- return -ENOMEM;
-
- if (!symbol_conf.full_paths) {
- char bf[PATH_MAX];
-
- if (getcwd(bf, sizeof(bf)) == NULL) {
- err = -errno;
-out_getcwd_err:
- pr_err("failed to get the current directory\n");
- goto out_err;
- }
- self->cwd = strdup(bf);
- if (self->cwd == NULL) {
- err = -ENOMEM;
- goto out_getcwd_err;
- }
- self->cwdlen = strlen(self->cwd);
- }
-
- if (!self->fd_pipe)
- err = __perf_session__process_events(self,
- self->header.data_offset,
- self->header.data_size,
- self->size, ops);
- else
- err = __perf_session__process_pipe_events(self, ops);
-out_err:
- return err;
-}
-
-bool perf_session__has_traces(struct perf_session *self, const char *msg)
-{
- if (!(self->sample_type & PERF_SAMPLE_RAW)) {
- pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
- return false;
- }
-
- return true;
-}
-
-int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
- const char *symbol_name,
- u64 addr)
-{
- char *bracket;
- enum map_type i;
- struct ref_reloc_sym *ref;
-
- ref = zalloc(sizeof(struct ref_reloc_sym));
- if (ref == NULL)
- return -ENOMEM;
-
- ref->name = strdup(symbol_name);
- if (ref->name == NULL) {
- free(ref);
- return -ENOMEM;
- }
-
- bracket = strchr(ref->name, ']');
- if (bracket)
- *bracket = '\0';
-
- ref->addr = addr;
-
- for (i = 0; i < MAP__NR_TYPES; ++i) {
- struct kmap *kmap = map__kmap(maps[i]);
- kmap->ref_reloc_sym = ref;
- }
-
- return 0;
-}
-
-size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
-{
- return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
- __dsos__fprintf(&self->host_machine.user_dsos, fp) +
- machines__fprintf_dsos(&self->machines, fp);
-}
-
-size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
- bool with_hits)
-{
- size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
- return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
-}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
deleted file mode 100644
index bf3b5c4..0000000
--- a/tools/perf/util/session.h
+++ /dev/null
@@ -1,158 +0,0 @@
-#ifndef __PERF_SESSION_H
-#define __PERF_SESSION_H
-
-#include "hist.h"
-#include "event.h"
-#include "symbol.h"
-#include "thread.h"
-#include <linux/bitops.h>
-#include <linux/rbtree.h>
-#include "../../../include/linux/perf_event.h"
-
-#define HEADER_FEAT_BITS 256
-
-struct sample_queue;
-struct ip_callchain;
-struct thread;
-
-struct perf_header {
- int frozen;
- int attrs, size;
- bool needs_swap;
- struct perf_header_attr **attr;
- s64 attr_offset;
- u64 data_offset;
- u64 data_size;
- u64 event_offset;
- u64 event_size;
- DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
-};
-
-struct ordered_samples {
- u64 last_flush;
- u64 next_flush;
- u64 max_timestamp;
- struct list_head samples_head;
- struct sample_queue *last_inserted;
-};
-
-struct perf_session {
- struct perf_header header;
- unsigned long size;
- unsigned long mmap_window;
- struct rb_root threads;
- struct thread *last_match;
- struct machine host_machine;
- struct rb_root machines;
- struct rb_root hists_tree;
- /*
- * FIXME: should point to the first entry in hists_tree and
- * be a hists instance. Right now its only 'report'
- * that is using ->hists_tree while all the rest use
- * ->hists.
- */
- struct hists hists;
- u64 sample_type;
- int fd;
- bool fd_pipe;
- bool repipe;
- int cwdlen;
- char *cwd;
- struct ordered_samples ordered_samples;
- char filename[0];
-};
-
-struct perf_event_ops;
-
-typedef int (*event_op)(event_t *self, struct perf_session *session);
-typedef int (*event_op2)(event_t *self, struct perf_session *session,
- struct perf_event_ops *ops);
-
-struct perf_event_ops {
- event_op sample,
- mmap,
- comm,
- fork,
- exit,
- lost,
- read,
- throttle,
- unthrottle,
- attr,
- event_type,
- tracing_data,
- build_id;
- event_op2 finished_round;
- bool ordered_samples;
-};
-
-struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe);
-void perf_session__delete(struct perf_session *self);
-
-void perf_event_header__bswap(struct perf_event_header *self);
-
-int __perf_session__process_events(struct perf_session *self,
- u64 data_offset, u64 data_size, u64 size,
- struct perf_event_ops *ops);
-int perf_session__process_events(struct perf_session *self,
- struct perf_event_ops *event_ops);
-
-struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
- struct thread *thread,
- struct ip_callchain *chain,
- struct symbol **parent);
-
-bool perf_session__has_traces(struct perf_session *self, const char *msg);
-
-int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
- const char *symbol_name,
- u64 addr);
-
-void mem_bswap_64(void *src, int byte_size);
-
-int perf_session__create_kernel_maps(struct perf_session *self);
-
-int do_read(int fd, void *buf, size_t size);
-void perf_session__update_sample_type(struct perf_session *self);
-
-static inline
-struct machine *perf_session__find_host_machine(struct perf_session *self)
-{
- return &self->host_machine;
-}
-
-static inline
-struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
-{
- if (pid == HOST_KERNEL_ID)
- return &self->host_machine;
- return machines__find(&self->machines, pid);
-}
-
-static inline
-struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
-{
- if (pid == HOST_KERNEL_ID)
- return &self->host_machine;
- return machines__findnew(&self->machines, pid);
-}
-
-static inline
-void perf_session__process_machines(struct perf_session *self,
- machine__process_t process)
-{
- process(&self->host_machine, self);
- return machines__process(&self->machines, process, self);
-}
-
-size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
-
-size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
- FILE *fp, bool with_hits);
-
-static inline
-size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
-{
- return hists__fprintf_nr_events(&self->hists, fp);
-}
-#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 0d6ed4c..ccc3045 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 <linux/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 edd8202..0000000
--- a/tools/perf/util/symbol.c
+++ /dev/null
@@ -1,2347 +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 "symbol.h"
-#include <lk/strlist.h>
-
-#include <libelf.h>
-#include <gelf.h>
-#include <elf.h>
-#include <limits.h>
-#include <sys/utsname.h>
-
-#include <lk/debug.h>
-
-#ifndef NT_GNU_BUILD_ID
-#define NT_GNU_BUILD_ID 3
-#endif
-
-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,
-};
-
-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, 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->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 %s\n",
- self->start, self->end, 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->long_name != self->name)
- 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 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, 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, 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,
- 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,
- 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)
-{
- 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_err("%s: cannot read %s ELF file.\n", __func__, name);
- goto out_close;
- }
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
- if (sec == NULL) {
- 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;
-
- 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, 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)
-
-int filename__read_build_id(const char *filename, void *bf, size_t size)
-{
- int fd, err = -1;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- Elf_Data *data;
- Elf_Scn *sec;
- Elf_Kind ek;
- void *ptr;
- 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;
- }
-
- ek = elf_kind(elf);
- if (ek != ELF_K_ELF)
- goto out_elf_end;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
- }
-
- 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_elf_end;
- }
-
- data = elf_getdata(sec, NULL);
- if (data == NULL)
- goto out_elf_end;
-
- 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_elf_end:
- 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;
- u8 build_id[BUILD_ID_SIZE];
- int ret = -1;
- int fd;
- struct machine *machine;
- const char *root_dir;
-
- 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;
- }
-
- self->origin = DSO__ORIG_BUILD_ID_CACHE;
- if (dso__build_id_filename(self, name, size) != NULL)
- goto open_file;
-more:
- do {
- self->origin++;
- switch (self->origin) {
- 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:
- if (filename__read_build_id(self->long_name, build_id,
- sizeof(build_id))) {
- char build_id_hex[BUILD_ID_SIZE * 2 + 1];
- build_id__sprintf(build_id, sizeof(build_id),
- build_id_hex);
- snprintf(name, size,
- "/usr/lib/debug/.build-id/%.2s/%s.debug",
- build_id_hex, build_id_hex + 2);
- if (self->has_build_id)
- goto compare_build_id;
- break;
- }
- self->origin++;
- /* Fall thru */
- 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:
- goto out;
- }
-
- if (self->has_build_id) {
- if (filename__read_build_id(name, build_id,
- sizeof(build_id)) < 0)
- goto more;
-compare_build_id:
- if (!dso__build_id_equal(self, build_id))
- goto more;
- }
-open_file:
- fd = open(name, O_RDONLY);
- } while (fd < 0);
-
- ret = dso__load_sym(self, map, name, fd, filter, 0);
- close(fd);
-
- /*
- * Some people seem to have debuginfo files _WITHOUT_ debug info!?!?
- */
- if (!ret)
- goto more;
-
- if (ret > 0) {
- int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
- if (nr_plt > 0)
- ret += nr_plt;
- }
-out:
- 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);
-
- 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);
- if (map_groups__set_modules_path_dir(self, path) < 0)
- goto failure;
- } 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)
- goto failure;
- dso__set_long_name(map->dso, long_name);
- dso__kernel_module_get_build_id(map->dso, "");
- }
- }
-
- return 0;
-failure:
- closedir(dir);
- return -1;
-}
-
-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;
-
- if (self->has_build_id) {
- u8 build_id[BUILD_ID_SIZE];
-
- if (filename__read_build_id(vmlinux, build_id,
- sizeof(build_id)) < 0) {
- pr_debug("No build_id in %s, ignoring it\n", vmlinux);
- return -1;
- }
- if (!dso__build_id_equal(self, build_id)) {
- char expected_build_id[BUILD_ID_SIZE * 2 + 1],
- vmlinux_build_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(self->build_id,
- sizeof(self->build_id),
- expected_build_id);
- build_id__sprintf(build_id, sizeof(build_id),
- vmlinux_build_id);
- pr_debug("build_id in %s is %s while expected is %s, "
- "ignoring it\n", vmlinux, vmlinux_build_id,
- expected_build_id);
- return -1;
- }
- }
-
- 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);
- 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;
-}
-
-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)
-{
- 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;
-
- return 0;
-
-out_free_dso_list:
- strlist__delete(symbol_conf.dso_list);
-out_free_comm_list:
- strlist__delete(symbol_conf.comm_list);
- return -1;
-}
-
-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;
-}
-
-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 80e569b..0000000
--- a/tools/perf/util/symbol.h
+++ /dev/null
@@ -1,221 +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 <linux/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;
- 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,
- full_paths,
- show_cpu_utilization;
- 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;
-};
-
-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];
- u8 adjust_symbols:1;
- u8 slen_calculated:1;
- u8 has_build_id:1;
- enum dso_kernel_type kernel;
- u8 hit:1;
- u8 annotate_warned: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);
-
-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));
-
-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);
-
-int symbol__init(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 c2652fd..c5e5207 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -2,7 +2,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include "session.h"
+#include <perf/session.h>
#include "thread.h"
#include <lk/util.h>
#include <lk/debug.h>
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 1dfd9ff..93baaea 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -3,7 +3,7 @@
#include <linux/rbtree.h>
#include <unistd.h>
-#include "symbol.h"
+#include <perf/symbol.h>
struct thread {
struct rb_node rb_node;
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 14/21] perf: Carve out perf bits for general usage, p3
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (12 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 13/21] perf: Carve out perf bits for general usage, p2 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 15/21] perf: Export trace event utils Borislav Petkov
` (6 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Those are pulled in when linking parse-events.c with other tools like
the RAS daemon so export them into tools/lib/perf/ too.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 10 +
tools/lib/lk/debug.h | 2 +-
tools/lib/perf/build-id.c | 111 ++++
tools/lib/perf/build-id.h | 12 +
tools/lib/perf/event.c | 827 ++++++++++++++++++++++++++++
tools/lib/perf/event.h | 167 ++++++
| 2 +-
tools/lib/perf/hist.c | 1082 +++++++++++++++++++++++++++++++++++++
tools/lib/perf/hist.h | 129 +++++
tools/lib/perf/session.c | 3 +-
tools/lib/perf/session.h | 6 +-
tools/lib/perf/sort.c | 346 ++++++++++++
tools/lib/perf/sort.h | 115 ++++
tools/lib/perf/symbol.c | 2 +-
tools/lib/perf/thread.c | 164 ++++++
tools/lib/perf/thread.h | 44 ++
tools/perf/Makefile | 10 -
tools/perf/builtin-annotate.c | 8 +-
tools/perf/builtin-buildid-list.c | 2 +-
tools/perf/builtin-diff.c | 6 +-
tools/perf/builtin-kmem.c | 2 +-
tools/perf/builtin-kvm.c | 2 +-
tools/perf/builtin-lock.c | 2 +-
tools/perf/builtin-record.c | 4 +-
tools/perf/builtin-report.c | 6 +-
tools/perf/builtin-sched.c | 2 +-
tools/perf/builtin-stat.c | 4 +-
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/perf.c | 2 +-
tools/perf/util/build-id.c | 111 ----
tools/perf/util/build-id.h | 12 -
tools/perf/util/callchain.c | 7 -
tools/perf/util/callchain.h | 3 +-
tools/perf/util/event.c | 820 ----------------------------
tools/perf/util/event.h | 166 ------
tools/perf/util/hist.c | 1082 -------------------------------------
tools/perf/util/hist.h | 129 -----
tools/perf/util/newt.c | 4 +-
tools/perf/util/probe-event.c | 4 +-
tools/perf/util/probe-finder.c | 2 +-
tools/perf/util/sort.c | 346 ------------
tools/perf/util/sort.h | 116 ----
tools/perf/util/thread.c | 164 ------
tools/perf/util/thread.h | 44 --
47 files changed, 3044 insertions(+), 3046 deletions(-)
create mode 100644 tools/lib/perf/build-id.c
create mode 100644 tools/lib/perf/build-id.h
create mode 100644 tools/lib/perf/event.c
create mode 100644 tools/lib/perf/event.h
create mode 100644 tools/lib/perf/hist.c
create mode 100644 tools/lib/perf/hist.h
create mode 100644 tools/lib/perf/sort.c
create mode 100644 tools/lib/perf/sort.h
create mode 100644 tools/lib/perf/thread.c
create mode 100644 tools/lib/perf/thread.h
delete mode 100644 tools/perf/util/build-id.c
delete mode 100644 tools/perf/util/build-id.h
delete mode 100644 tools/perf/util/event.c
delete mode 100644 tools/perf/util/event.h
delete mode 100644 tools/perf/util/hist.c
delete mode 100644 tools/perf/util/hist.h
delete mode 100644 tools/perf/util/sort.c
delete mode 100644 tools/perf/util/sort.h
delete mode 100644 tools/perf/util/thread.c
delete mode 100644 tools/perf/util/thread.h
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index dd87c43..cabef46 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -18,6 +18,11 @@ LIB_H += perf/header.h
LIB_H += perf/symbol.h
LIB_H += perf/map.h
LIB_H += perf/session.h
+LIB_H += perf/build-id.h
+LIB_H += perf/hist.h
+LIB_H += perf/thread.h
+LIB_H += perf/sort.h
+LIB_H += perf/event.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -39,6 +44,11 @@ LIB_OBJS += $(OUTPUT)perf/header.o
LIB_OBJS += $(OUTPUT)perf/symbol.o
LIB_OBJS += $(OUTPUT)perf/map.o
LIB_OBJS += $(OUTPUT)perf/session.o
+LIB_OBJS += $(OUTPUT)perf/build-id.o
+LIB_OBJS += $(OUTPUT)perf/hist.o
+LIB_OBJS += $(OUTPUT)perf/thread.o
+LIB_OBJS += $(OUTPUT)perf/sort.o
+LIB_OBJS += $(OUTPUT)perf/event.o
LIBFILE = lklib.a
diff --git a/tools/lib/lk/debug.h b/tools/lib/lk/debug.h
index 6df0457..44fdfab 100644
--- a/tools/lib/lk/debug.h
+++ b/tools/lib/lk/debug.h
@@ -3,7 +3,7 @@
#define __LK_DEBUG_H
#include <stdbool.h>
-#include <util/event.h>
+#include <perf/event.h>
extern int verbose;
extern bool dump_trace;
diff --git a/tools/lib/perf/build-id.c b/tools/lib/perf/build-id.c
new file mode 100644
index 0000000..5969758
--- /dev/null
+++ b/tools/lib/perf/build-id.c
@@ -0,0 +1,111 @@
+/*
+ * build-id.c
+ *
+ * build-id support
+ *
+ * Copyright (C) 2009, 2010 Red Hat Inc.
+ * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include <lk/util.h>
+#include <lk/debug.h>
+#include <lk/config.h>
+#include <lk/strbuf.h>
+#include <stdio.h>
+#include "build-id.h"
+#include "event.h"
+#include <perf/symbol.h>
+#include <linux/kernel.h>
+
+static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
+{
+ struct addr_location al;
+ u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct thread *thread = perf_session__findnew(session, event->ip.pid);
+
+ if (thread == NULL) {
+ pr_err("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
+ event->ip.pid, event->ip.ip, &al);
+
+ if (al.map != NULL)
+ al.map->dso->hit = 1;
+
+ return 0;
+}
+
+struct perf_event_ops build_id__mark_dso_hit_ops = {
+ .sample = build_id__mark_dso_hit,
+ .mmap = event__process_mmap,
+ .fork = event__process_task,
+};
+
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
+{
+ char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+
+ if (!self->has_build_id)
+ return NULL;
+
+ build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
+ if (bf == NULL) {
+ if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
+ build_id_hex, build_id_hex + 2) < 0)
+ return NULL;
+ } else
+ snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
+ build_id_hex, build_id_hex + 2);
+ return bf;
+}
+
+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 = lk_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/lib/perf/build-id.h b/tools/lib/perf/build-id.h
new file mode 100644
index 0000000..1b73726
--- /dev/null
+++ b/tools/lib/perf/build-id.h
@@ -0,0 +1,12 @@
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+
+#include "session.h"
+#include <util/config.h>
+
+extern struct perf_event_ops build_id__mark_dso_hit_ops;
+
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+extern void set_buildid_dir(void);
+
+#endif
diff --git a/tools/lib/perf/event.c b/tools/lib/perf/event.c
new file mode 100644
index 0000000..0f16325
--- /dev/null
+++ b/tools/lib/perf/event.c
@@ -0,0 +1,827 @@
+#include <linux/types.h>
+#include "event.h"
+#include <lk/debug.h>
+#include <perf/session.h>
+#include "sort.h"
+#include "string.h"
+#include <lk/strlist.h>
+#include "thread.h"
+
+const char *event__name[] = {
+ [0] = "TOTAL",
+ [PERF_RECORD_MMAP] = "MMAP",
+ [PERF_RECORD_LOST] = "LOST",
+ [PERF_RECORD_COMM] = "COMM",
+ [PERF_RECORD_EXIT] = "EXIT",
+ [PERF_RECORD_THROTTLE] = "THROTTLE",
+ [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
+ [PERF_RECORD_FORK] = "FORK",
+ [PERF_RECORD_READ] = "READ",
+ [PERF_RECORD_SAMPLE] = "SAMPLE",
+ [PERF_RECORD_HEADER_ATTR] = "ATTR",
+ [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
+ [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
+ [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
+};
+
+bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
+{
+ unsigned int chain_size = event->header.size;
+ chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
+ return chain->nr * sizeof(u64) <= chain_size;
+}
+
+static pid_t event__synthesize_comm(pid_t pid, int full,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t ev;
+ char filename[PATH_MAX];
+ char bf[BUFSIZ];
+ FILE *fp;
+ size_t size = 0;
+ DIR *tasks;
+ struct dirent dirent, *next;
+ pid_t tgid = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+out_race:
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return 0;
+ }
+
+ memset(&ev.comm, 0, sizeof(ev.comm));
+ while (!ev.comm.comm[0] || !ev.comm.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ goto out_failure;
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(ev.comm.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = ev.comm.pid = atoi(tgids);
+ }
+ }
+
+ ev.comm.header.type = PERF_RECORD_COMM;
+ size = ALIGN(size, sizeof(u64));
+ ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
+
+ if (!full) {
+ ev.comm.tid = pid;
+
+ process(&ev, session);
+ goto out_fclose;
+ }
+
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
+
+ tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
+ while (!readdir_r(tasks, &dirent, &next) && next) {
+ char *end;
+ pid = strtol(dirent.d_name, &end, 10);
+ if (*end)
+ continue;
+
+ ev.comm.tid = pid;
+
+ process(&ev, session);
+ }
+ closedir(tasks);
+
+out_fclose:
+ fclose(fp);
+ return tgid;
+
+out_failure:
+ pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
+ return -1;
+}
+
+static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ char filename[PATH_MAX];
+ FILE *fp;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return -1;
+ }
+
+ while (1) {
+ char bf[BUFSIZ], *pbf = bf;
+ event_t ev = {
+ .header = {
+ .type = PERF_RECORD_MMAP,
+ /*
+ * Just like the kernel, see __perf_event_mmap
+ * in kernel/perf_event.c
+ */
+ .misc = PERF_RECORD_MISC_USER,
+ },
+ };
+ int n;
+ size_t size;
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ break;
+
+ /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
+ n = hex2u64(pbf, &ev.mmap.start);
+ if (n < 0)
+ continue;
+ pbf += n + 1;
+ n = hex2u64(pbf, &ev.mmap.len);
+ if (n < 0)
+ continue;
+ pbf += n + 3;
+ if (*pbf == 'x') { /* vm_exec */
+ u64 vm_pgoff;
+ char *execname = strchr(bf, '/');
+
+ /* Catch VDSO */
+ if (execname == NULL)
+ execname = strstr(bf, "[vdso]");
+
+ if (execname == NULL)
+ continue;
+
+ pbf += 3;
+ n = hex2u64(pbf, &vm_pgoff);
+ /* pgoff is in bytes, not pages */
+ if (n >= 0)
+ ev.mmap.pgoff = vm_pgoff << getpagesize();
+ else
+ ev.mmap.pgoff = 0;
+
+ size = strlen(execname);
+ execname[size - 1] = '\0'; /* Remove \n */
+ memcpy(ev.mmap.filename, execname, size);
+ size = ALIGN(size, sizeof(u64));
+ ev.mmap.len -= ev.mmap.start;
+ ev.mmap.header.size = (sizeof(ev.mmap) -
+ (sizeof(ev.mmap.filename) - size));
+ ev.mmap.pid = tgid;
+ ev.mmap.tid = pid;
+
+ process(&ev, session);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+int event__synthesize_modules(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine)
+{
+ struct rb_node *nd;
+ struct map_groups *kmaps = &machine->kmaps;
+ u16 misc;
+
+ /*
+ * kernel uses 0 for user space maps, see kernel/perf_event.c
+ * __perf_event_mmap
+ */
+ if (machine__is_host(machine))
+ misc = PERF_RECORD_MISC_KERNEL;
+ else
+ misc = PERF_RECORD_MISC_GUEST_KERNEL;
+
+ for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
+ nd; nd = rb_next(nd)) {
+ event_t ev;
+ size_t size;
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+
+ if (pos->dso->kernel)
+ continue;
+
+ size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
+ memset(&ev, 0, sizeof(ev));
+ ev.mmap.header.misc = misc;
+ ev.mmap.header.type = PERF_RECORD_MMAP;
+ ev.mmap.header.size = (sizeof(ev.mmap) -
+ (sizeof(ev.mmap.filename) - size));
+ ev.mmap.start = pos->start;
+ ev.mmap.len = pos->end - pos->start;
+ ev.mmap.pid = machine->pid;
+
+ memcpy(ev.mmap.filename, pos->dso->long_name,
+ pos->dso->long_name_len + 1);
+ process(&ev, session);
+ }
+
+ return 0;
+}
+
+int event__synthesize_thread(pid_t pid, event__handler_t process,
+ struct perf_session *session)
+{
+ pid_t tgid = event__synthesize_comm(pid, 1, process, session);
+ if (tgid == -1)
+ return -1;
+ return event__synthesize_mmap_events(pid, tgid, process, session);
+}
+
+void event__synthesize_threads(event__handler_t process,
+ struct perf_session *session)
+{
+ DIR *proc;
+ struct dirent dirent, *next;
+
+ proc = opendir("/proc");
+
+ while (!readdir_r(proc, &dirent, &next) && next) {
+ char *end;
+ pid_t pid = strtol(dirent.d_name, &end, 10);
+
+ if (*end) /* only interested in proper numerical dirents */
+ continue;
+
+ event__synthesize_thread(pid, process, session);
+ }
+
+ closedir(proc);
+}
+
+struct process_symbol_args {
+ const char *name;
+ u64 start;
+};
+
+static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
+{
+ struct process_symbol_args *args = arg;
+
+ /*
+ * Must be a function or at least an alias, as in PARISC64, where "_text" is
+ * an 'A' to the same address as "_stext".
+ */
+ if (!(symbol_type__is_a(type, MAP__FUNCTION) ||
+ type == 'A') || strcmp(name, args->name))
+ return 0;
+
+ args->start = start;
+ return 1;
+}
+
+int event__synthesize_kernel_mmap(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine,
+ const char *symbol_name)
+{
+ size_t size;
+ const char *filename, *mmap_name;
+ char path[PATH_MAX];
+ char name_buff[PATH_MAX];
+ struct map *map;
+
+ event_t ev = {
+ .header = {
+ .type = PERF_RECORD_MMAP,
+ },
+ };
+ /*
+ * We should get this from /sys/kernel/sections/.text, but till that is
+ * available use this, and after it is use this as a fallback for older
+ * kernels.
+ */
+ struct process_symbol_args args = { .name = symbol_name, };
+
+ mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));
+ if (machine__is_host(machine)) {
+ /*
+ * kernel uses PERF_RECORD_MISC_USER for user space maps,
+ * see kernel/perf_event.c __perf_event_mmap
+ */
+ ev.header.misc = PERF_RECORD_MISC_KERNEL;
+ filename = "/proc/kallsyms";
+ } else {
+ ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
+ if (machine__is_default_guest(machine))
+ filename = (char *) symbol_conf.default_guest_kallsyms;
+ else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ filename = path;
+ }
+ }
+
+ if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
+ return -ENOENT;
+
+ map = machine->vmlinux_maps[MAP__FUNCTION];
+ size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
+ "%s%s", mmap_name, symbol_name) + 1;
+ size = ALIGN(size, sizeof(u64));
+ ev.mmap.header.size = (sizeof(ev.mmap) -
+ (sizeof(ev.mmap.filename) - size));
+ ev.mmap.pgoff = args.start;
+ ev.mmap.start = map->start;
+ ev.mmap.len = map->end - ev.mmap.start;
+ ev.mmap.pid = machine->pid;
+
+ return process(&ev, session);
+}
+
+static void thread__comm_adjust(struct thread *self)
+{
+ char *comm = self->comm;
+
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.comm_list ||
+ strlist__has_entry(symbol_conf.comm_list, comm))) {
+ unsigned int slen = strlen(comm);
+
+ if (slen > comms__col_width) {
+ comms__col_width = slen;
+ threads__col_width = slen + 6;
+ }
+ }
+}
+
+static int thread__set_comm_adjust(struct thread *self, const char *comm)
+{
+ int ret = thread__set_comm(self, comm);
+
+ if (ret)
+ return ret;
+
+ thread__comm_adjust(self);
+
+ return 0;
+}
+
+int event__process_comm(event_t *self, struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, self->comm.tid);
+
+ dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
+
+ if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
+ dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int event__process_lost(event_t *self, struct perf_session *session)
+{
+ dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
+ session->hists.stats.total_lost += self->lost.lost;
+ return 0;
+}
+
+static void event_set_kernel_mmap_len(struct map **maps, event_t *self)
+{
+ maps[MAP__FUNCTION]->start = self->mmap.start;
+ maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
+ /*
+ * Be a bit paranoid here, some perf.data file came with
+ * a zero sized synthesized MMAP event for the kernel.
+ */
+ if (maps[MAP__FUNCTION]->end == 0)
+ maps[MAP__FUNCTION]->end = ~0UL;
+}
+
+static int event__process_kernel_mmap(event_t *self,
+ struct perf_session *session)
+{
+ struct map *map;
+ char kmmap_prefix[PATH_MAX];
+ struct machine *machine;
+ enum dso_kernel_type kernel_type;
+ bool is_kernel_mmap;
+
+ machine = perf_session__findnew_machine(session, self->mmap.pid);
+ if (!machine) {
+ pr_err("Can't find id %d's machine\n", self->mmap.pid);
+ goto out_problem;
+ }
+
+ machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
+ if (machine__is_host(machine))
+ kernel_type = DSO_TYPE_KERNEL;
+ else
+ kernel_type = DSO_TYPE_GUEST_KERNEL;
+
+ is_kernel_mmap = memcmp(self->mmap.filename,
+ kmmap_prefix,
+ strlen(kmmap_prefix)) == 0;
+ if (self->mmap.filename[0] == '/' ||
+ (!is_kernel_mmap && self->mmap.filename[0] == '[')) {
+
+ char short_module_name[1024];
+ char *name, *dot;
+
+ if (self->mmap.filename[0] == '/') {
+ name = strrchr(self->mmap.filename, '/');
+ if (name == NULL)
+ goto out_problem;
+
+ ++name; /* skip / */
+ dot = strrchr(name, '.');
+ if (dot == NULL)
+ goto out_problem;
+ snprintf(short_module_name, sizeof(short_module_name),
+ "[%.*s]", (int)(dot - name), name);
+ strxfrchar(short_module_name, '-', '_');
+ } else
+ strcpy(short_module_name, self->mmap.filename);
+
+ map = machine__new_module(machine, self->mmap.start,
+ self->mmap.filename);
+ if (map == NULL)
+ goto out_problem;
+
+ name = strdup(short_module_name);
+ if (name == NULL)
+ goto out_problem;
+
+ map->dso->short_name = name;
+ map->end = map->start + self->mmap.len;
+ } else if (is_kernel_mmap) {
+ const char *symbol_name = (self->mmap.filename +
+ strlen(kmmap_prefix));
+ /*
+ * Should be there already, from the build-id table in
+ * the header.
+ */
+ struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
+ kmmap_prefix);
+ if (kernel == NULL)
+ goto out_problem;
+
+ kernel->kernel = kernel_type;
+ if (__machine__create_kernel_maps(machine, kernel) < 0)
+ goto out_problem;
+
+ event_set_kernel_mmap_len(machine->vmlinux_maps, self);
+ perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
+ symbol_name,
+ self->mmap.pgoff);
+ if (machine__is_default_guest(machine)) {
+ /*
+ * preload dso of guest kernel and modules
+ */
+ dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
+ NULL);
+ }
+ }
+ return 0;
+out_problem:
+ return -1;
+}
+
+int event__process_mmap(event_t *self, struct perf_session *session)
+{
+ struct machine *machine;
+ struct thread *thread;
+ struct map *map;
+ u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ int ret = 0;
+
+ dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
+ self->mmap.pid, self->mmap.tid, self->mmap.start,
+ self->mmap.len, self->mmap.pgoff, self->mmap.filename);
+
+ if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
+ cpumode == PERF_RECORD_MISC_KERNEL) {
+ ret = event__process_kernel_mmap(self, session);
+ if (ret < 0)
+ goto out_problem;
+ return 0;
+ }
+
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL)
+ goto out_problem;
+ thread = perf_session__findnew(session, self->mmap.pid);
+ map = map__new(&machine->user_dsos, self->mmap.start,
+ self->mmap.len, self->mmap.pgoff,
+ self->mmap.pid, self->mmap.filename,
+ MAP__FUNCTION, session->cwd, session->cwdlen);
+
+ if (thread == NULL || map == NULL)
+ goto out_problem;
+
+ thread__insert_map(thread, map);
+ return 0;
+
+out_problem:
+ dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
+ return 0;
+}
+
+int event__process_task(event_t *self, struct perf_session *session)
+{
+ struct thread *thread = perf_session__findnew(session, self->fork.tid);
+ struct thread *parent = perf_session__findnew(session, self->fork.ptid);
+
+ dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
+ self->fork.ppid, self->fork.ptid);
+
+ if (self->header.type == PERF_RECORD_EXIT)
+ return 0;
+
+ if (thread == NULL || parent == NULL ||
+ thread__fork(thread, parent) < 0) {
+ dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void thread__find_addr_map(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al)
+{
+ struct map_groups *mg = &self->mg;
+ struct machine *machine = NULL;
+
+ al->thread = self;
+ al->addr = addr;
+ al->cpumode = cpumode;
+ al->filtered = false;
+
+ if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
+ al->level = 'k';
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL) {
+ al->map = NULL;
+ return;
+ }
+ mg = &machine->kmaps;
+ } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
+ al->level = '.';
+ machine = perf_session__find_host_machine(session);
+ } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
+ al->level = 'g';
+ machine = perf_session__find_machine(session, pid);
+ if (machine == NULL) {
+ al->map = NULL;
+ return;
+ }
+ mg = &machine->kmaps;
+ } else {
+ /*
+ * 'u' means guest os user space.
+ * TODO: We don't support guest user space. Might support late.
+ */
+ if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest)
+ al->level = 'u';
+ else
+ al->level = 'H';
+ al->map = NULL;
+
+ if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
+ cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
+ !perf_guest)
+ al->filtered = true;
+ if ((cpumode == PERF_RECORD_MISC_USER ||
+ cpumode == PERF_RECORD_MISC_KERNEL) &&
+ !perf_host)
+ al->filtered = true;
+
+ return;
+ }
+try_again:
+ al->map = map_groups__find(mg, type, al->addr);
+ if (al->map == NULL) {
+ /*
+ * If this is outside of all known maps, and is a negative
+ * address, try to look it up in the kernel dso, as it might be
+ * a vsyscall or vdso (which executes in user-mode).
+ *
+ * XXX This is nasty, we should have a symbol list in the
+ * "[vdso]" dso, but for now lets use the old trick of looking
+ * in the whole kernel symbol list.
+ */
+ if ((long long)al->addr < 0 &&
+ cpumode == PERF_RECORD_MISC_KERNEL &&
+ machine && mg != &machine->kmaps) {
+ mg = &machine->kmaps;
+ goto try_again;
+ }
+ } else
+ al->addr = al->map->map_ip(al->map, al->addr);
+}
+
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al,
+ symbol_filter_t filter)
+{
+ thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
+ if (al->map != NULL)
+ al->sym = map__find_symbol(al->map, al->addr, filter);
+ else
+ al->sym = NULL;
+}
+
+static void dso__calc_col_width(struct dso *self)
+{
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.dso_list ||
+ strlist__has_entry(symbol_conf.dso_list, self->name))) {
+ u16 slen = self->short_name_len;
+ if (verbose)
+ slen = self->long_name_len;
+ if (dsos__col_width < slen)
+ dsos__col_width = slen;
+ }
+
+ self->slen_calculated = 1;
+}
+
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, struct sample_data *data,
+ symbol_filter_t filter)
+{
+ u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct thread *thread;
+
+ event__parse_sample(self, session->sample_type, data);
+
+ dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n",
+ self->header.misc, data->pid, data->tid, data->ip,
+ data->period, data->cpu);
+
+ if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
+ unsigned int i;
+
+ dump_printf("... chain: nr:%Lu\n", data->callchain->nr);
+
+ if (!ip_callchain__valid(data->callchain, self)) {
+ pr_debug("call-chain problem with event, "
+ "skipping it.\n");
+ goto out_filtered;
+ }
+
+ if (dump_trace) {
+ for (i = 0; i < data->callchain->nr; i++)
+ dump_printf("..... %2d: %016Lx\n",
+ i, data->callchain->ips[i]);
+ }
+ }
+ thread = perf_session__findnew(session, self->ip.pid);
+ if (thread == NULL)
+ return -1;
+
+ if (symbol_conf.comm_list &&
+ !strlist__has_entry(symbol_conf.comm_list, thread->comm))
+ goto out_filtered;
+
+ dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+ /*
+ * Have we already created the kernel maps for the host machine?
+ *
+ * This should have happened earlier, when we processed the kernel MMAP
+ * events, but for older perf.data files there was no such thing, so do
+ * it now.
+ */
+ if (cpumode == PERF_RECORD_MISC_KERNEL &&
+ session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
+ machine__create_kernel_maps(&session->host_machine);
+
+ thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
+ self->ip.pid, self->ip.ip, al);
+ dump_printf(" ...... dso: %s\n",
+ al->map ? al->map->dso->long_name :
+ al->level == 'H' ? "[hypervisor]" : "<not found>");
+ al->sym = NULL;
+ al->cpu = data->cpu;
+
+ if (al->map) {
+ if (symbol_conf.dso_list &&
+ (!al->map || !al->map->dso ||
+ !(strlist__has_entry(symbol_conf.dso_list,
+ al->map->dso->short_name) ||
+ (al->map->dso->short_name != al->map->dso->long_name &&
+ strlist__has_entry(symbol_conf.dso_list,
+ al->map->dso->long_name)))))
+ goto out_filtered;
+ /*
+ * We have to do this here as we may have a dso with no symbol
+ * hit that has a name longer than the ones with symbols
+ * sampled.
+ */
+ if (!sort_dso.elide && !al->map->dso->slen_calculated)
+ dso__calc_col_width(al->map->dso);
+
+ al->sym = map__find_symbol(al->map, al->addr, filter);
+ } else {
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+ if (dsos__col_width < unresolved_col_width &&
+ !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ !symbol_conf.dso_list)
+ dsos__col_width = unresolved_col_width;
+ }
+
+ if (symbol_conf.sym_list && al->sym &&
+ !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
+ goto out_filtered;
+
+ return 0;
+
+out_filtered:
+ al->filtered = true;
+ return 0;
+}
+
+int event__parse_sample(const event_t *event, u64 type, struct sample_data *data)
+{
+ const u64 *array = event->sample.array;
+
+ if (type & PERF_SAMPLE_IP) {
+ data->ip = event->ip.ip;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TID) {
+ u32 *p = (u32 *)array;
+ data->pid = p[0];
+ data->tid = p[1];
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TIME) {
+ data->time = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_ADDR) {
+ data->addr = *array;
+ array++;
+ }
+
+ data->id = -1ULL;
+ if (type & PERF_SAMPLE_ID) {
+ data->id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_STREAM_ID) {
+ data->stream_id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_CPU) {
+ u32 *p = (u32 *)array;
+ data->cpu = *p;
+ array++;
+ } else
+ data->cpu = -1;
+
+ if (type & PERF_SAMPLE_PERIOD) {
+ data->period = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_READ) {
+ pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
+ return -1;
+ }
+
+ if (type & PERF_SAMPLE_CALLCHAIN) {
+ data->callchain = (struct ip_callchain *)array;
+ array += 1 + data->callchain->nr;
+ }
+
+ if (type & PERF_SAMPLE_RAW) {
+ u32 *p = (u32 *)array;
+ data->raw_size = *p;
+ p++;
+ data->raw_data = p;
+ }
+
+ return 0;
+}
diff --git a/tools/lib/perf/event.h b/tools/lib/perf/event.h
new file mode 100644
index 0000000..a3260f8
--- /dev/null
+++ b/tools/lib/perf/event.h
@@ -0,0 +1,167 @@
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+
+#include <limits.h>
+
+#include <perf.h>
+#include <perf/map.h>
+
+/*
+ * PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
+ */
+struct ip_event {
+ struct perf_event_header header;
+ u64 ip;
+ u32 pid, tid;
+ unsigned char __more_data[];
+};
+
+struct mmap_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ char filename[PATH_MAX];
+};
+
+struct comm_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ char comm[16];
+};
+
+struct fork_event {
+ struct perf_event_header header;
+ u32 pid, ppid;
+ u32 tid, ptid;
+ u64 time;
+};
+
+struct lost_event {
+ struct perf_event_header header;
+ u64 id;
+ u64 lost;
+};
+
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct read_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 value;
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
+};
+
+struct sample_event {
+ struct perf_event_header header;
+ u64 array[];
+};
+
+struct sample_data {
+ u64 ip;
+ u32 pid, tid;
+ u64 time;
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ u64 period;
+ u32 cpu;
+ u32 raw_size;
+ void *raw_data;
+ struct ip_callchain *callchain;
+};
+
+#define BUILD_ID_SIZE 20
+
+struct build_id_event {
+ struct perf_event_header header;
+ pid_t pid;
+ u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ char filename[];
+};
+
+enum perf_user_event_type { /* above any possible kernel type */
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65,
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_HEADER_MAX
+};
+
+struct attr_event {
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ u64 id[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+ u64 event_id;
+ char name[MAX_EVENT_NAME];
+};
+
+struct event_type_event {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+};
+
+struct tracing_data_event {
+ struct perf_event_header header;
+ u32 size;
+};
+
+typedef union event_union {
+ struct perf_event_header header;
+ struct ip_event ip;
+ struct mmap_event mmap;
+ struct comm_event comm;
+ struct fork_event fork;
+ struct lost_event lost;
+ struct read_event read;
+ struct sample_event sample;
+ struct attr_event attr;
+ struct event_type_event event_type;
+ struct tracing_data_event tracing_data;
+ struct build_id_event build_id;
+} event_t;
+
+void event__print_totals(void);
+
+struct perf_session;
+
+typedef int (*event__handler_t)(event_t *event, struct perf_session *session);
+
+int event__synthesize_thread(pid_t pid, event__handler_t process,
+ struct perf_session *session);
+void event__synthesize_threads(event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_kernel_mmap(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine,
+ const char *symbol_name);
+
+int event__synthesize_modules(event__handler_t process,
+ struct perf_session *session,
+ struct machine *machine);
+
+int event__process_comm(event_t *self, struct perf_session *session);
+int event__process_lost(event_t *self, struct perf_session *session);
+int event__process_mmap(event_t *self, struct perf_session *session);
+int event__process_task(event_t *self, struct perf_session *session);
+
+struct addr_location;
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, struct sample_data *data,
+ symbol_filter_t filter);
+int event__parse_sample(const event_t *event, u64 type, struct sample_data *data);
+
+extern const char *event__name[];
+
+bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
+#endif /* __PERF_RECORD_H */
--git a/tools/lib/perf/header.h b/tools/lib/perf/header.h
index e930814..3845118 100644
--- a/tools/lib/perf/header.h
+++ b/tools/lib/perf/header.h
@@ -5,7 +5,7 @@
#include <sys/types.h>
#include <stdbool.h>
#include <lk/types.h>
-#include <util/event.h>
+#include "event.h"
#include "session.h"
#include <linux/bitmap.h>
diff --git a/tools/lib/perf/hist.c b/tools/lib/perf/hist.c
new file mode 100644
index 0000000..2dfa141
--- /dev/null
+++ b/tools/lib/perf/hist.c
@@ -0,0 +1,1082 @@
+#include <lk/util.h>
+#include "build-id.h"
+#include "hist.h"
+#include <perf/session.h>
+#include "sort.h"
+#include <math.h>
+
+struct callchain_param callchain_param = {
+ .mode = CHAIN_GRAPH_REL,
+ .min_percent = 0.5
+};
+
+static void hist_entry__add_cpumode_period(struct hist_entry *self,
+ unsigned int cpumode, u64 period)
+{
+ switch (cpumode) {
+ case PERF_RECORD_MISC_KERNEL:
+ self->period_sys += period;
+ break;
+ case PERF_RECORD_MISC_USER:
+ self->period_us += period;
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ self->period_guest_sys += period;
+ break;
+ case PERF_RECORD_MISC_GUEST_USER:
+ self->period_guest_us += period;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * histogram, sorted on item, collects periods
+ */
+
+static struct hist_entry *hist_entry__new(struct hist_entry *template)
+{
+ size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0;
+ struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
+
+ if (self != NULL) {
+ *self = *template;
+ self->nr_events = 1;
+ if (symbol_conf.use_callchain)
+ callchain_init(self->callchain);
+ }
+
+ return self;
+}
+
+static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry)
+{
+ if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen)
+ self->max_sym_namelen = entry->ms.sym->namelen;
+ ++self->nr_entries;
+}
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent, u64 period)
+{
+ struct rb_node **p = &self->entries.rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *he;
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = al->map,
+ .sym = al->sym,
+ },
+ .cpu = al->cpu,
+ .ip = al->addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ };
+ int cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ he = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__cmp(&entry, he);
+
+ if (!cmp) {
+ he->period += period;
+ ++he->nr_events;
+ goto out;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ he = hist_entry__new(&entry);
+ if (!he)
+ return NULL;
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, &self->entries);
+ hists__inc_nr_entries(self, he);
+out:
+ hist_entry__add_cpumode_period(he, al->cpumode, period);
+ return he;
+}
+
+int64_t
+hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ cmp = se->se_cmp(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+int64_t
+hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ int64_t (*f)(struct hist_entry *, struct hist_entry *);
+
+ f = se->se_collapse ?: se->se_cmp;
+
+ cmp = f(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+void hist_entry__free(struct hist_entry *he)
+{
+ free(he);
+}
+
+/*
+ * collapse the histogram
+ */
+
+static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+ int64_t cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__collapse(iter, he);
+
+ if (!cmp) {
+ iter->period += he->period;
+ hist_entry__free(he);
+ return false;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, root);
+ return true;
+}
+
+void hists__collapse_resort(struct hists *self)
+{
+ struct rb_root tmp;
+ struct rb_node *next;
+ struct hist_entry *n;
+
+ if (!sort__need_collapse)
+ return;
+
+ tmp = RB_ROOT;
+ next = rb_first(&self->entries);
+ self->nr_entries = 0;
+ self->max_sym_namelen = 0;
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &self->entries);
+ if (collapse__insert_entry(&tmp, n))
+ hists__inc_nr_entries(self, n);
+ }
+
+ self->entries = tmp;
+}
+
+/*
+ * reverse the map, sort on period.
+ */
+
+static void __hists__insert_output_entry(struct rb_root *entries,
+ struct hist_entry *he,
+ u64 min_callchain_hits)
+{
+ struct rb_node **p = &entries->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+
+ if (symbol_conf.use_callchain)
+ callchain_param.sort(&he->sorted_chain, he->callchain,
+ min_callchain_hits, &callchain_param);
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ if (he->period > iter->period)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, entries);
+}
+
+void hists__output_resort(struct hists *self)
+{
+ struct rb_root tmp;
+ struct rb_node *next;
+ struct hist_entry *n;
+ u64 min_callchain_hits;
+
+ min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100);
+
+ tmp = RB_ROOT;
+ next = rb_first(&self->entries);
+
+ self->nr_entries = 0;
+ self->max_sym_namelen = 0;
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &self->entries);
+ __hists__insert_output_entry(&tmp, n, min_callchain_hits);
+ hists__inc_nr_entries(self, n);
+ }
+
+ self->entries = tmp;
+}
+
+static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
+{
+ int i;
+ int ret = fprintf(fp, " ");
+
+ for (i = 0; i < left_margin; i++)
+ ret += fprintf(fp, " ");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
+ int left_margin)
+{
+ int i;
+ size_t ret = callchain__fprintf_left_margin(fp, left_margin);
+
+ for (i = 0; i < depth; i++)
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "| ");
+ else
+ ret += fprintf(fp, " ");
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
+ int depth, int depth_mask, int period,
+ u64 total_samples, int hits,
+ int left_margin)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ for (i = 0; i < depth; i++) {
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "|");
+ else
+ ret += fprintf(fp, " ");
+ if (!period && i == depth - 1) {
+ double percent;
+
+ percent = hits * 100.0 / total_samples;
+ ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ } else
+ ret += fprintf(fp, "%s", " ");
+ }
+ if (chain->ms.sym)
+ ret += fprintf(fp, "%s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
+
+ return ret;
+}
+
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.ms.sym = rem_sq_bracket;
+}
+
+static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int depth,
+ int depth_mask, int left_margin)
+{
+ struct rb_node *node, *next;
+ struct callchain_node *child;
+ struct callchain_list *chain;
+ int new_depth_mask = depth_mask;
+ u64 new_total;
+ u64 remaining;
+ size_t ret = 0;
+ int i;
+ uint entries_printed = 0;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = self->children_hit;
+ else
+ new_total = total_samples;
+
+ remaining = new_total;
+
+ node = rb_first(&self->rb_root);
+ while (node) {
+ u64 cumul;
+
+ child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = cumul_hits(child);
+ remaining -= cumul;
+
+ /*
+ * The depth mask manages the output of pipes that show
+ * the depth. We don't want to keep the pipes of the current
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
+ */
+ next = rb_next(node);
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ /*
+ * But we keep the older depth mask for the line separator
+ * to keep the level link until we reach the last child
+ */
+ ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
+ left_margin);
+ i = 0;
+ list_for_each_entry(chain, &child->val, list) {
+ ret += ipchain__fprintf_graph(fp, chain, depth,
+ new_depth_mask, i++,
+ new_total,
+ cumul,
+ left_margin);
+ }
+ ret += __callchain__fprintf_graph(fp, child, new_total,
+ depth + 1,
+ new_depth_mask | (1 << depth),
+ left_margin);
+ node = next;
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != new_total) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, new_total,
+ remaining, left_margin);
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int left_margin)
+{
+ struct callchain_list *chain;
+ bool printed = false;
+ int i = 0;
+ int ret = 0;
+ u32 entries_printed = 0;
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+
+ if (!printed) {
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "|\n");
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "---");
+
+ left_margin += 3;
+ printed = true;
+ } else
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+
+ ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
+
+ return ret;
+}
+
+static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
+ u64 total_samples)
+{
+ struct callchain_list *chain;
+ size_t ret = 0;
+
+ if (!self)
+ return 0;
+
+ ret += callchain__fprintf_flat(fp, self->parent, total_samples);
+
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n",
+ (void *)(long)chain->ip);
+ }
+
+ return ret;
+}
+
+static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
+ u64 total_samples, int left_margin)
+{
+ struct rb_node *rb_node;
+ struct callchain_node *chain;
+ size_t ret = 0;
+ u32 entries_printed = 0;
+
+ rb_node = rb_first(&self->sorted_chain);
+ while (rb_node) {
+ double percent;
+
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+ percent = chain->hit * 100.0 / total_samples;
+ switch (callchain_param.mode) {
+ case CHAIN_FLAT:
+ ret += percent_color_fprintf(fp, " %6.2f%%\n",
+ percent);
+ ret += callchain__fprintf_flat(fp, chain, total_samples);
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ ret += callchain__fprintf_graph(fp, chain, total_samples,
+ left_margin);
+ case CHAIN_NONE:
+ default:
+ break;
+ }
+ ret += fprintf(fp, "\n");
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ rb_node = rb_next(rb_node);
+ }
+
+ return ret;
+}
+
+int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, bool color, u64 session_total)
+{
+ struct sort_entry *se;
+ u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
+ const char *sep = symbol_conf.field_sep;
+ int ret;
+
+ if (symbol_conf.exclude_other && !self->parent)
+ return 0;
+
+ if (pair_hists) {
+ period = self->pair ? self->pair->period : 0;
+ total = pair_hists->stats.total_period;
+ period_sys = self->pair ? self->pair->period_sys : 0;
+ period_us = self->pair ? self->pair->period_us : 0;
+ period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
+ period_guest_us = self->pair ? self->pair->period_guest_us : 0;
+ } else {
+ period = self->period;
+ total = session_total;
+ period_sys = self->period_sys;
+ period_us = self->period_us;
+ period_guest_sys = self->period_guest_sys;
+ period_guest_us = self->period_guest_us;
+ }
+
+ if (total) {
+ if (color)
+ ret = percent_color_snprintf(s, size,
+ sep ? "%.2f" : " %6.2f%%",
+ (period * 100.0) / total);
+ else
+ ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
+ (period * 100.0) / total);
+ if (symbol_conf.show_cpu_utilization) {
+ ret += percent_color_snprintf(s + ret, size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_sys * 100.0) / total);
+ ret += percent_color_snprintf(s + ret, size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_us * 100.0) / total);
+ if (perf_guest) {
+ ret += percent_color_snprintf(s + ret,
+ size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_guest_sys * 100.0) /
+ total);
+ ret += percent_color_snprintf(s + ret,
+ size - ret,
+ sep ? "%.2f" : " %6.2f%%",
+ (period_guest_us * 100.0) /
+ total);
+ }
+ }
+ } else
+ ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period);
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period);
+ else
+ ret += snprintf(s + ret, size - ret, "%11lld", period);
+ }
+
+ if (pair_hists) {
+ char bf[32];
+ double old_percent = 0, new_percent = 0, diff;
+
+ if (total > 0)
+ old_percent = (period * 100.0) / total;
+ if (session_total > 0)
+ new_percent = (self->period * 100.0) / session_total;
+
+ diff = new_percent - old_percent;
+
+ if (fabs(diff) >= 0.01)
+ snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ else
+ ret += snprintf(s + ret, size - ret, "%11.11s", bf);
+
+ if (show_displacement) {
+ if (displacement)
+ snprintf(bf, sizeof(bf), "%+4ld", displacement);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ else
+ ret += snprintf(s + ret, size - ret, "%6.6s", bf);
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
+ ret += se->se_snprintf(self, s + ret, size - ret,
+ se->se_width ? *se->se_width : 0);
+ }
+
+ return ret;
+}
+
+int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
+ bool show_displacement, long displacement, FILE *fp,
+ u64 session_total)
+{
+ char bf[512];
+ hist_entry__snprintf(self, bf, sizeof(bf), pair_hists,
+ show_displacement, displacement,
+ true, session_total);
+ return fprintf(fp, "%s\n", bf);
+}
+
+static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
+ u64 session_total)
+{
+ int left_margin = 0;
+
+ if (sort__first_dimension == SORT_COMM) {
+ struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
+ typeof(*se), list);
+ left_margin = se->se_width ? *se->se_width : 0;
+ left_margin -= thread__comm_len(self->thread);
+ }
+
+ return hist_entry_callchain__fprintf(fp, self, session_total,
+ left_margin);
+}
+
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp)
+{
+ struct sort_entry *se;
+ struct rb_node *nd;
+ size_t ret = 0;
+ unsigned long position = 1;
+ long displacement = 0;
+ unsigned int width;
+ const char *sep = symbol_conf.field_sep;
+ const char *col_width = symbol_conf.col_width_list_str;
+
+ init_rem_hits();
+
+ fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ fprintf(fp, "%cSamples", *sep);
+ else
+ fputs(" Samples ", fp);
+ }
+
+ if (symbol_conf.show_cpu_utilization) {
+ if (sep) {
+ ret += fprintf(fp, "%csys", *sep);
+ ret += fprintf(fp, "%cus", *sep);
+ if (perf_guest) {
+ ret += fprintf(fp, "%cguest sys", *sep);
+ ret += fprintf(fp, "%cguest us", *sep);
+ }
+ } else {
+ ret += fprintf(fp, " sys ");
+ ret += fprintf(fp, " us ");
+ if (perf_guest) {
+ ret += fprintf(fp, " guest sys ");
+ ret += fprintf(fp, " guest us ");
+ }
+ }
+ }
+
+ if (pair) {
+ if (sep)
+ ret += fprintf(fp, "%cDelta", *sep);
+ else
+ ret += fprintf(fp, " Delta ");
+
+ if (show_displacement) {
+ if (sep)
+ ret += fprintf(fp, "%cDisplacement", *sep);
+ else
+ ret += fprintf(fp, " Displ");
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+ if (sep) {
+ fprintf(fp, "%c%s", *sep, se->se_header);
+ continue;
+ }
+ width = strlen(se->se_header);
+ if (se->se_width) {
+ if (symbol_conf.col_width_list_str) {
+ if (col_width) {
+ *se->se_width = atoi(col_width);
+ col_width = strchr(col_width, ',');
+ if (col_width)
+ ++col_width;
+ }
+ }
+ width = *se->se_width = max(*se->se_width, width);
+ }
+ fprintf(fp, " %*s", width, se->se_header);
+ }
+ fprintf(fp, "\n");
+
+ if (sep)
+ goto print_entries;
+
+ fprintf(fp, "# ........");
+ if (symbol_conf.show_nr_samples)
+ fprintf(fp, " ..........");
+ if (pair) {
+ fprintf(fp, " ..........");
+ if (show_displacement)
+ fprintf(fp, " .....");
+ }
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ unsigned int i;
+
+ if (se->elide)
+ continue;
+
+ fprintf(fp, " ");
+ if (se->se_width)
+ width = *se->se_width;
+ else
+ width = strlen(se->se_header);
+ for (i = 0; i < width; i++)
+ fprintf(fp, ".");
+ }
+
+ fprintf(fp, "\n#\n");
+
+print_entries:
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (show_displacement) {
+ if (h->pair != NULL)
+ displacement = ((long)h->pair->position -
+ (long)position);
+ else
+ displacement = 0;
+ ++position;
+ }
+ ret += hist_entry__fprintf(h, pair, show_displacement,
+ displacement, fp, self->stats.total_period);
+
+ if (symbol_conf.use_callchain)
+ ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period);
+
+ if (h->ms.map == NULL && verbose > 1) {
+ __map_groups__fprintf_maps(&h->thread->mg,
+ MAP__FUNCTION, verbose, fp);
+ fprintf(fp, "%.10s end\n", graph_dotted_line);
+ }
+ }
+
+ free(rem_sq_bracket);
+
+ return ret;
+}
+
+enum hist_filter {
+ HIST_FILTER__DSO,
+ HIST_FILTER__THREAD,
+};
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ self->max_sym_namelen = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (symbol_conf.exclude_other && !h->parent)
+ continue;
+
+ if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
+ h->filtered |= (1 << HIST_FILTER__DSO);
+ continue;
+ }
+
+ h->filtered &= ~(1 << HIST_FILTER__DSO);
+ if (!h->filtered) {
+ ++self->nr_entries;
+ self->stats.total_period += h->period;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
+ if (h->ms.sym &&
+ self->max_sym_namelen < h->ms.sym->namelen)
+ self->max_sym_namelen = h->ms.sym->namelen;
+ }
+ }
+}
+
+void hists__filter_by_thread(struct hists *self, const struct thread *thread)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ self->max_sym_namelen = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (thread != NULL && h->thread != thread) {
+ h->filtered |= (1 << HIST_FILTER__THREAD);
+ continue;
+ }
+ h->filtered &= ~(1 << HIST_FILTER__THREAD);
+ if (!h->filtered) {
+ ++self->nr_entries;
+ self->stats.total_period += h->period;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
+ if (h->ms.sym &&
+ self->max_sym_namelen < h->ms.sym->namelen)
+ self->max_sym_namelen = h->ms.sym->namelen;
+ }
+ }
+}
+
+static int symbol__alloc_hist(struct symbol *self)
+{
+ struct sym_priv *priv = symbol__priv(self);
+ const int size = (sizeof(*priv->hist) +
+ (self->end - self->start) * sizeof(u64));
+
+ priv->hist = zalloc(size);
+ return priv->hist == NULL ? -1 : 0;
+}
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
+{
+ unsigned int sym_size, offset;
+ struct symbol *sym = self->ms.sym;
+ struct sym_priv *priv;
+ struct sym_hist *h;
+
+ if (!sym || !self->ms.map)
+ return 0;
+
+ priv = symbol__priv(sym);
+ if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
+ return -ENOMEM;
+
+ sym_size = sym->end - sym->start;
+ offset = ip - sym->start;
+
+ pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
+
+ if (offset >= sym_size)
+ return 0;
+
+ h = priv->hist;
+ h->sum++;
+ h->ip[offset]++;
+
+ pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start,
+ self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]);
+ return 0;
+}
+
+static struct objdump_line *objdump_line__new(s64 offset, char *line)
+{
+ struct objdump_line *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->offset = offset;
+ self->line = line;
+ }
+
+ return self;
+}
+
+void objdump_line__free(struct objdump_line *self)
+{
+ free(self->line);
+ free(self);
+}
+
+static void objdump__add_line(struct list_head *head, struct objdump_line *line)
+{
+ list_add_tail(&line->node, head);
+}
+
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos)
+{
+ list_for_each_entry_continue(pos, head, node)
+ if (pos->offset >= 0)
+ return pos;
+
+ return NULL;
+}
+
+static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
+ struct list_head *head)
+{
+ struct symbol *sym = self->ms.sym;
+ struct objdump_line *objdump_line;
+ char *line = NULL, *tmp, *tmp2, *c;
+ size_t line_len;
+ s64 line_ip, offset = -1;
+
+ if (getline(&line, &line_len, file) < 0)
+ return -1;
+
+ if (!line)
+ return -1;
+
+ while (line_len != 0 && isspace(line[line_len - 1]))
+ line[--line_len] = '\0';
+
+ c = strchr(line, '\n');
+ if (c)
+ *c = 0;
+
+ line_ip = -1;
+
+ /*
+ * Strip leading spaces:
+ */
+ tmp = line;
+ while (*tmp) {
+ if (*tmp != ' ')
+ break;
+ tmp++;
+ }
+
+ if (*tmp) {
+ /*
+ * Parse hexa addresses followed by ':'
+ */
+ line_ip = strtoull(tmp, &tmp2, 16);
+ if (*tmp2 != ':' || tmp == tmp2)
+ line_ip = -1;
+ }
+
+ if (line_ip != -1) {
+ u64 start = map__rip_2objdump(self->ms.map, sym->start);
+ offset = line_ip - start;
+ }
+
+ objdump_line = objdump_line__new(offset, line);
+ if (objdump_line == NULL) {
+ free(line);
+ return -1;
+ }
+ objdump__add_line(head, objdump_line);
+
+ return 0;
+}
+
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
+{
+ struct symbol *sym = self->ms.sym;
+ struct map *map = self->ms.map;
+ struct dso *dso = map->dso;
+ char *filename = dso__build_id_filename(dso, NULL, 0);
+ bool free_filename = true;
+ char command[PATH_MAX * 2];
+ FILE *file;
+ int err = 0;
+ u64 len;
+
+ if (filename == NULL) {
+ if (dso->has_build_id) {
+ pr_err("Can't annotate %s: not enough memory\n",
+ sym->name);
+ return -ENOMEM;
+ }
+ goto fallback;
+ } else if (readlink(filename, command, sizeof(command)) < 0 ||
+ strstr(command, "[kernel.kallsyms]") ||
+ access(filename, R_OK)) {
+ free(filename);
+fallback:
+ /*
+ * If we don't have build-ids or the build-id file isn't in the
+ * cache, or is just a kallsyms file, well, lets hope that this
+ * DSO is the same as when 'perf record' ran.
+ */
+ filename = dso->long_name;
+ free_filename = false;
+ }
+
+ if (dso->origin == DSO__ORIG_KERNEL) {
+ if (dso->annotate_warned)
+ goto out_free_filename;
+ err = -ENOENT;
+ dso->annotate_warned = 1;
+ pr_err("Can't annotate %s: No vmlinux file was found in the "
+ "path\n", sym->name);
+ goto out_free_filename;
+ }
+
+ pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
+ filename, sym->name, map->unmap_ip(map, sym->start),
+ map->unmap_ip(map, sym->end));
+
+ len = sym->end - sym->start;
+
+ pr_debug("annotating [%p] %30s : [%p] %30s\n",
+ dso, dso->long_name, sym, sym->name);
+
+ snprintf(command, sizeof(command),
+ "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
+ map__rip_2objdump(map, sym->start),
+ map__rip_2objdump(map, sym->end),
+ filename, filename);
+
+ pr_debug("Executing: %s\n", command);
+
+ file = popen(command, "r");
+ if (!file)
+ goto out_free_filename;
+
+ while (!feof(file))
+ if (hist_entry__parse_objdump_line(self, file, head) < 0)
+ break;
+
+ pclose(file);
+out_free_filename:
+ if (free_filename)
+ free(filename);
+ return err;
+}
+
+void hists__inc_nr_events(struct hists *self, u32 type)
+{
+ ++self->stats.nr_events[0];
+ ++self->stats.nr_events[type];
+}
+
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
+{
+ int i;
+ size_t ret = 0;
+
+ for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
+ if (!event__name[i])
+ continue;
+ ret += fprintf(fp, "%10s events: %10d\n",
+ event__name[i], self->stats.nr_events[i]);
+ }
+
+ return ret;
+}
diff --git a/tools/lib/perf/hist.h b/tools/lib/perf/hist.h
new file mode 100644
index 0000000..5d5cf49
--- /dev/null
+++ b/tools/lib/perf/hist.h
@@ -0,0 +1,129 @@
+#ifndef __PERF_HIST_H
+#define __PERF_HIST_H
+
+#include <linux/types.h>
+#include <util/callchain.h>
+
+extern struct callchain_param callchain_param;
+
+struct hist_entry;
+struct addr_location;
+struct symbol;
+struct rb_root;
+
+struct objdump_line {
+ struct list_head node;
+ s64 offset;
+ char *line;
+};
+
+void objdump_line__free(struct objdump_line *self);
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos);
+
+struct sym_hist {
+ u64 sum;
+ u64 ip[0];
+};
+
+struct sym_ext {
+ struct rb_node node;
+ double percent;
+ char *path;
+};
+
+struct sym_priv {
+ struct sym_hist *hist;
+ struct sym_ext *ext;
+};
+
+/*
+ * The kernel collects the number of events it couldn't send in a stretch and
+ * when possible sends this number in a PERF_RECORD_LOST event. The number of
+ * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
+ * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
+ * the sum of all struct lost_event.lost fields reported.
+ *
+ * The total_period is needed because by default auto-freq is used, so
+ * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
+ * the total number of low level events, it is necessary to to sum all struct
+ * sample_event.period and stash the result in total_period.
+ */
+struct events_stats {
+ u64 total_period;
+ u64 total_lost;
+ u32 nr_events[PERF_RECORD_HEADER_MAX];
+ u32 nr_unknown_events;
+};
+
+struct hists {
+ struct rb_node rb_node;
+ struct rb_root entries;
+ u64 nr_entries;
+ struct events_stats stats;
+ u64 config;
+ u64 event_stream;
+ u32 type;
+ u32 max_sym_namelen;
+};
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *parent, u64 period);
+extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
+int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
+ bool show_displacement, long displacement, FILE *fp,
+ u64 total);
+int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, bool color, u64 total);
+void hist_entry__free(struct hist_entry *);
+
+void hists__output_resort(struct hists *self);
+void hists__collapse_resort(struct hists *self);
+
+void hists__inc_nr_events(struct hists *self, u32 type);
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
+
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp);
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head);
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso);
+void hists__filter_by_thread(struct hists *self, const struct thread *thread);
+
+#ifdef NO_NEWT_SUPPORT
+static inline int hists__browse(struct hists *self __used,
+ const char *helpline __used,
+ const char *ev_name __used)
+{
+ return 0;
+}
+
+static inline int hists__tui_browse_tree(struct rb_root *self __used,
+ const char *help __used)
+{
+ return 0;
+}
+
+static inline int hist_entry__tui_annotate(struct hist_entry *self __used)
+{
+ return 0;
+}
+#define KEY_LEFT -1
+#define KEY_RIGHT -2
+#else
+#include <newt.h>
+int hists__browse(struct hists *self, const char *helpline,
+ const char *ev_name);
+int hist_entry__tui_annotate(struct hist_entry *self);
+
+#define KEY_LEFT NEWT_KEY_LEFT
+#define KEY_RIGHT NEWT_KEY_RIGHT
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help);
+#endif
+#endif /* __PERF_HIST_H */
diff --git a/tools/lib/perf/session.c b/tools/lib/perf/session.c
index 5e9998c..c1589e9 100644
--- a/tools/lib/perf/session.c
+++ b/tools/lib/perf/session.c
@@ -6,9 +6,8 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
-
#include "session.h"
-#include <util/sort.h>
+#include "sort.h"
#include <lk/util.h>
static int perf_session__open(struct perf_session *self, bool force)
diff --git a/tools/lib/perf/session.h b/tools/lib/perf/session.h
index 1bbece2..bf3b5c4 100644
--- a/tools/lib/perf/session.h
+++ b/tools/lib/perf/session.h
@@ -1,10 +1,10 @@
#ifndef __PERF_SESSION_H
#define __PERF_SESSION_H
-#include <util/hist.h>
-#include <util/event.h>
+#include "hist.h"
+#include "event.h"
#include "symbol.h"
-#include <util/thread.h>
+#include "thread.h"
#include <linux/bitops.h>
#include <linux/rbtree.h>
#include "../../../include/linux/perf_event.h"
diff --git a/tools/lib/perf/sort.c b/tools/lib/perf/sort.c
new file mode 100644
index 0000000..c27b4b0
--- /dev/null
+++ b/tools/lib/perf/sort.c
@@ -0,0 +1,346 @@
+#include "sort.h"
+
+regex_t parent_regex;
+const char default_parent_pattern[] = "^sys_|^do_page_fault";
+const char *parent_pattern = default_parent_pattern;
+const char default_sort_order[] = "comm,dso,symbol";
+const char *sort_order = default_sort_order;
+int sort__need_collapse = 0;
+int sort__has_parent = 0;
+
+enum sort_type sort__first_dimension;
+
+unsigned int dsos__col_width;
+unsigned int comms__col_width;
+unsigned int threads__col_width;
+unsigned int cpus__col_width;
+static unsigned int parent_symbol__col_width;
+char * field_sep;
+
+LIST_HEAD(hist_entry__sort_list);
+
+static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width);
+
+struct sort_entry sort_thread = {
+ .se_header = "Command: Pid",
+ .se_cmp = sort__thread_cmp,
+ .se_snprintf = hist_entry__thread_snprintf,
+ .se_width = &threads__col_width,
+};
+
+struct sort_entry sort_comm = {
+ .se_header = "Command",
+ .se_cmp = sort__comm_cmp,
+ .se_collapse = sort__comm_collapse,
+ .se_snprintf = hist_entry__comm_snprintf,
+ .se_width = &comms__col_width,
+};
+
+struct sort_entry sort_dso = {
+ .se_header = "Shared Object",
+ .se_cmp = sort__dso_cmp,
+ .se_snprintf = hist_entry__dso_snprintf,
+ .se_width = &dsos__col_width,
+};
+
+struct sort_entry sort_sym = {
+ .se_header = "Symbol",
+ .se_cmp = sort__sym_cmp,
+ .se_snprintf = hist_entry__sym_snprintf,
+};
+
+struct sort_entry sort_parent = {
+ .se_header = "Parent symbol",
+ .se_cmp = sort__parent_cmp,
+ .se_snprintf = hist_entry__parent_snprintf,
+ .se_width = &parent_symbol__col_width,
+};
+
+struct sort_entry sort_cpu = {
+ .se_header = "CPU",
+ .se_cmp = sort__cpu_cmp,
+ .se_snprintf = hist_entry__cpu_snprintf,
+ .se_width = &cpus__col_width,
+};
+
+struct sort_dimension {
+ const char *name;
+ struct sort_entry *entry;
+ int taken;
+};
+
+static struct sort_dimension sort_dimensions[] = {
+ { .name = "pid", .entry = &sort_thread, },
+ { .name = "comm", .entry = &sort_comm, },
+ { .name = "dso", .entry = &sort_dso, },
+ { .name = "symbol", .entry = &sort_sym, },
+ { .name = "parent", .entry = &sort_parent, },
+ { .name = "cpu", .entry = &sort_cpu, },
+};
+
+int64_t cmp_null(void *l, void *r)
+{
+ if (!l && !r)
+ return 0;
+ else if (!l)
+ return -1;
+ else
+ return 1;
+}
+
+/* --sort pid */
+
+int64_t
+sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vsnprintf(bf, size, fmt, ap);
+ if (field_sep && n > 0) {
+ char *sep = bf;
+
+ while (1) {
+ sep = strchr(sep, *field_sep);
+ if (sep == NULL)
+ break;
+ *sep = '.';
+ }
+ }
+ va_end(ap);
+ return n;
+}
+
+static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%*s:%5d", width,
+ self->thread->comm ?: "", self->thread->pid);
+}
+
+static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
+}
+
+/* --sort dso */
+
+int64_t
+sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
+ struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
+ const char *dso_name_l, *dso_name_r;
+
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
+
+ if (verbose) {
+ dso_name_l = dso_l->long_name;
+ dso_name_r = dso_r->long_name;
+ } else {
+ dso_name_l = dso_l->short_name;
+ dso_name_r = dso_r->short_name;
+ }
+
+ return strcmp(dso_name_l, dso_name_r);
+}
+
+static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ if (self->ms.map && self->ms.map->dso) {
+ const char *dso_name = !verbose ? self->ms.map->dso->short_name :
+ self->ms.map->dso->long_name;
+ return repsep_snprintf(bf, size, "%-*s", width, dso_name);
+ }
+
+ return repsep_snprintf(bf, size, "%*Lx", width, self->ip);
+}
+
+/* --sort symbol */
+
+int64_t
+sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ u64 ip_l, ip_r;
+
+ if (left->ms.sym == right->ms.sym)
+ return 0;
+
+ ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
+ ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
+
+ return (int64_t)(ip_r - ip_l);
+}
+
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ size_t ret = 0;
+
+ if (verbose) {
+ char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
+ ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o);
+ }
+
+ ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
+ if (self->ms.sym)
+ ret += repsep_snprintf(bf + ret, size - ret, "%s",
+ self->ms.sym->name);
+ else
+ ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip);
+
+ return ret;
+}
+
+/* --sort comm */
+
+int64_t
+sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+int64_t
+sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
+{
+ char *comm_l = left->thread->comm;
+ char *comm_r = right->thread->comm;
+
+ if (!comm_l || !comm_r)
+ return cmp_null(comm_l, comm_r);
+
+ return strcmp(comm_l, comm_r);
+}
+
+/* --sort parent */
+
+int64_t
+sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct symbol *sym_l = left->parent;
+ struct symbol *sym_r = right->parent;
+
+ if (!sym_l || !sym_r)
+ return cmp_null(sym_l, sym_r);
+
+ return strcmp(sym_l->name, sym_r->name);
+}
+
+static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*s", width,
+ self->parent ? self->parent->name : "[other]");
+}
+
+/* --sort cpu */
+
+int64_t
+sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->cpu - left->cpu;
+}
+
+static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
+}
+
+int sort_dimension__add(const char *tok)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
+ struct sort_dimension *sd = &sort_dimensions[i];
+
+ if (sd->taken)
+ continue;
+
+ if (strncasecmp(tok, sd->name, strlen(tok)))
+ continue;
+
+ if (sd->entry->se_collapse)
+ sort__need_collapse = 1;
+
+ if (sd->entry == &sort_parent) {
+ int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
+ if (ret) {
+ char err[BUFSIZ];
+
+ regerror(ret, &parent_regex, err, sizeof(err));
+ pr_err("Invalid regex: %s\n%s", parent_pattern, err);
+ return -EINVAL;
+ }
+ sort__has_parent = 1;
+ }
+
+ if (list_empty(&hist_entry__sort_list)) {
+ if (!strcmp(sd->name, "pid"))
+ sort__first_dimension = SORT_PID;
+ else if (!strcmp(sd->name, "comm"))
+ sort__first_dimension = SORT_COMM;
+ else if (!strcmp(sd->name, "dso"))
+ sort__first_dimension = SORT_DSO;
+ else if (!strcmp(sd->name, "symbol"))
+ sort__first_dimension = SORT_SYM;
+ else if (!strcmp(sd->name, "parent"))
+ sort__first_dimension = SORT_PARENT;
+ else if (!strcmp(sd->name, "cpu"))
+ sort__first_dimension = SORT_CPU;
+ }
+
+ list_add_tail(&sd->entry->list, &hist_entry__sort_list);
+ sd->taken = 1;
+
+ return 0;
+ }
+
+ return -ESRCH;
+}
+
+void setup_sorting(const char * const usagestr[], const struct option *opts)
+{
+ char *tmp, *tok, *str = strdup(sort_order);
+
+ for (tok = strtok_r(str, ", ", &tmp);
+ tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ if (sort_dimension__add(tok) < 0) {
+ error("Unknown --sort key: `%s'", tok);
+ usage_with_options(usagestr, opts);
+ }
+ }
+
+ free(str);
+}
+
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp)
+{
+ if (list && strlist__nr_entries(list) == 1) {
+ if (fp != NULL)
+ fprintf(fp, "# %s: %s\n", list_name,
+ strlist__entry(list, 0)->s);
+ self->elide = true;
+ }
+}
diff --git a/tools/lib/perf/sort.h b/tools/lib/perf/sort.h
new file mode 100644
index 0000000..6c3a5af
--- /dev/null
+++ b/tools/lib/perf/sort.h
@@ -0,0 +1,115 @@
+#ifndef __PERF_SORT_H
+#define __PERF_SORT_H
+#include <builtin.h>
+#include <perf.h>
+
+#include <lk/util.h>
+#include <lk/color.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <perf/symbol.h>
+#include "string.h"
+#include <util/callchain.h>
+#include <lk/strlist.h>
+#include "values.h"
+
+#include <lk/debug.h>
+#include <perf/header.h>
+#include <perf/parse-events.h>
+
+#include <util/parse-options.h>
+#include <util/cache.h>
+
+#include "thread.h"
+#include "sort.h"
+
+extern regex_t parent_regex;
+extern const char *sort_order;
+extern const char default_parent_pattern[];
+extern const char *parent_pattern;
+extern const char default_sort_order[];
+extern int sort__need_collapse;
+extern int sort__has_parent;
+extern char *field_sep;
+extern struct sort_entry sort_comm;
+extern struct sort_entry sort_dso;
+extern struct sort_entry sort_sym;
+extern struct sort_entry sort_parent;
+extern unsigned int dsos__col_width;
+extern unsigned int comms__col_width;
+extern unsigned int threads__col_width;
+extern unsigned int cpus__col_width;
+extern enum sort_type sort__first_dimension;
+
+struct hist_entry {
+ struct rb_node rb_node;
+ u64 period;
+ u64 period_sys;
+ u64 period_us;
+ u64 period_guest_sys;
+ u64 period_guest_us;
+ struct map_symbol ms;
+ struct thread *thread;
+ u64 ip;
+ s32 cpu;
+ u32 nr_events;
+ char level;
+ u8 filtered;
+ struct symbol *parent;
+ union {
+ unsigned long position;
+ struct hist_entry *pair;
+ struct rb_root sorted_chain;
+ };
+ struct callchain_node callchain[0];
+};
+
+enum sort_type {
+ SORT_PID,
+ SORT_COMM,
+ SORT_DSO,
+ SORT_SYM,
+ SORT_PARENT,
+ SORT_CPU,
+};
+
+/*
+ * configurable sorting bits
+ */
+
+struct sort_entry {
+ struct list_head list;
+
+ const char *se_header;
+
+ int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
+ int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
+ int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
+ unsigned int width);
+ unsigned int *se_width;
+ bool elide;
+};
+
+extern struct sort_entry sort_thread;
+extern struct list_head hist_entry__sort_list;
+
+void setup_sorting(const char * const usagestr[], const struct option *opts);
+
+extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
+extern int64_t cmp_null(void *, void *);
+extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
+int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
+extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
+extern int sort_dimension__add(const char *);
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp);
+
+#endif /* __PERF_SORT_H */
diff --git a/tools/lib/perf/symbol.c b/tools/lib/perf/symbol.c
index 04593d2..dddbeab 100644
--- a/tools/lib/perf/symbol.c
+++ b/tools/lib/perf/symbol.c
@@ -11,7 +11,7 @@
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
-#include <util/build-id.h>
+#include "build-id.h"
#include "symbol.h"
#include <lk/strlist.h>
#include <libelf.h>
diff --git a/tools/lib/perf/thread.c b/tools/lib/perf/thread.c
new file mode 100644
index 0000000..1f9793d
--- /dev/null
+++ b/tools/lib/perf/thread.c
@@ -0,0 +1,164 @@
+#include <perf.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <perf/session.h>
+#include "thread.h"
+#include <lk/util.h>
+#include <lk/debug.h>
+
+int find_all_tid(int pid, pid_t ** all_tid)
+{
+ char name[256];
+ int items;
+ struct dirent **namelist = NULL;
+ int ret = 0;
+ int i;
+
+ sprintf(name, "/proc/%d/task", pid);
+ items = scandir(name, &namelist, NULL, NULL);
+ if (items <= 0)
+ return -ENOENT;
+ *all_tid = malloc(sizeof(pid_t) * items);
+ if (!*all_tid) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ for (i = 0; i < items; i++)
+ (*all_tid)[i] = atoi(namelist[i]->d_name);
+
+ ret = items;
+
+failure:
+ for (i=0; i<items; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ return ret;
+}
+
+static struct thread *thread__new(pid_t pid)
+{
+ struct thread *self = zalloc(sizeof(*self));
+
+ if (self != NULL) {
+ map_groups__init(&self->mg);
+ self->pid = pid;
+ self->comm = malloc(32);
+ if (self->comm)
+ snprintf(self->comm, 32, ":%d", self->pid);
+ }
+
+ return self;
+}
+
+int thread__set_comm(struct thread *self, const char *comm)
+{
+ int err;
+
+ if (self->comm)
+ free(self->comm);
+ self->comm = strdup(comm);
+ err = self->comm == NULL ? -ENOMEM : 0;
+ if (!err) {
+ self->comm_set = true;
+ map_groups__flush(&self->mg);
+ }
+ return err;
+}
+
+int thread__comm_len(struct thread *self)
+{
+ if (!self->comm_len) {
+ if (!self->comm)
+ return 0;
+ self->comm_len = strlen(self->comm);
+ }
+
+ return self->comm_len;
+}
+
+static size_t thread__fprintf(struct thread *self, FILE *fp)
+{
+ return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
+ map_groups__fprintf(&self->mg, verbose, fp);
+}
+
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
+{
+ struct rb_node **p = &self->threads.rb_node;
+ struct rb_node *parent = NULL;
+ struct thread *th;
+
+ /*
+ * Font-end cache - PID lookups come in blocks,
+ * so most of the time we dont have to look up
+ * the full rbtree:
+ */
+ if (self->last_match && self->last_match->pid == pid)
+ return self->last_match;
+
+ while (*p != NULL) {
+ parent = *p;
+ th = rb_entry(parent, struct thread, rb_node);
+
+ if (th->pid == pid) {
+ self->last_match = th;
+ return th;
+ }
+
+ if (pid < th->pid)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ th = thread__new(pid);
+ if (th != NULL) {
+ rb_link_node(&th->rb_node, parent, p);
+ rb_insert_color(&th->rb_node, &self->threads);
+ self->last_match = th;
+ }
+
+ return th;
+}
+
+void thread__insert_map(struct thread *self, struct map *map)
+{
+ map_groups__fixup_overlappings(&self->mg, map, verbose, stderr);
+ map_groups__insert(&self->mg, map);
+}
+
+int thread__fork(struct thread *self, struct thread *parent)
+{
+ int i;
+
+ if (parent->comm_set) {
+ if (self->comm)
+ free(self->comm);
+ self->comm = strdup(parent->comm);
+ if (!self->comm)
+ return -ENOMEM;
+ self->comm_set = true;
+ }
+
+ for (i = 0; i < MAP__NR_TYPES; ++i)
+ if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
+ return -ENOMEM;
+ return 0;
+}
+
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
+{
+ size_t ret = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
+ struct thread *pos = rb_entry(nd, struct thread, rb_node);
+
+ ret += thread__fprintf(pos, fp);
+ }
+
+ return ret;
+}
diff --git a/tools/lib/perf/thread.h b/tools/lib/perf/thread.h
new file mode 100644
index 0000000..93baaea
--- /dev/null
+++ b/tools/lib/perf/thread.h
@@ -0,0 +1,44 @@
+#ifndef __PERF_THREAD_H
+#define __PERF_THREAD_H
+
+#include <linux/rbtree.h>
+#include <unistd.h>
+#include <perf/symbol.h>
+
+struct thread {
+ struct rb_node rb_node;
+ struct map_groups mg;
+ pid_t pid;
+ char shortname[3];
+ bool comm_set;
+ char *comm;
+ int comm_len;
+};
+
+struct perf_session;
+
+int find_all_tid(int pid, pid_t ** all_tid);
+int thread__set_comm(struct thread *self, const char *comm);
+int thread__comm_len(struct thread *self);
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
+void thread__insert_map(struct thread *self, struct map *map);
+int thread__fork(struct thread *self, struct thread *parent);
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
+
+static inline struct map *thread__find_map(struct thread *self,
+ enum map_type type, u64 addr)
+{
+ return self ? map_groups__find(&self->mg, type, addr) : NULL;
+}
+
+void thread__find_addr_map(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al);
+
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
+ enum map_type type, pid_t pid, u64 addr,
+ struct addr_location *al,
+ symbol_filter_t filter);
+#endif /* __PERF_THREAD_H */
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 653802b..001831e 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -354,8 +354,6 @@ LIB_H += util/include/dwarf-regs.h
LIB_H += perf.h
LIB_H += util/cache.h
LIB_H += util/callchain.h
-LIB_H += util/build-id.h
-LIB_H += util/event.h
LIB_H += util/exec_cmd.h
LIB_H += util/levenshtein.h
LIB_H += util/parse-options.h
@@ -365,9 +363,6 @@ LIB_H += util/svghelper.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
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
@@ -375,9 +370,7 @@ LIB_H += util/config.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
-LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/environment.o
-LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
LIB_OBJS += $(OUTPUT)util/help.o
LIB_OBJS += $(OUTPUT)util/levenshtein.o
@@ -390,14 +383,11 @@ LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/pager.o
LIB_OBJS += $(OUTPUT)util/callchain.o
LIB_OBJS += $(OUTPUT)util/values.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
-LIB_OBJS += $(OUTPUT)util/hist.o
LIB_OBJS += $(OUTPUT)util/probe-event.o
LIB_OBJS += $(OUTPUT)util/config.o
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 1c78b75..29753d1 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -18,12 +18,12 @@
#include "perf.h"
#include <lk/debug.h>
-#include "util/event.h"
+#include <perf/event.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
-#include "util/thread.h"
-#include "util/sort.h"
-#include "util/hist.h"
+#include <perf/thread.h>
+#include <perf/sort.h>
+#include <perf/hist.h>
#include <perf/session.h>
static char const *input_name = "perf.data";
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 8cfc4d3..5e3253a 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -8,7 +8,7 @@
*/
#include "builtin.h"
#include "perf.h"
-#include "util/build-id.h"
+#include <perf/build-id.h>
#include "util/cache.h"
#include <lk/debug.h>
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 2d406dd..9615c36 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -7,10 +7,10 @@
#include "builtin.h"
#include <lk/debug.h>
-#include "util/event.h"
-#include "util/hist.h"
+#include <perf/event.h>
+#include <perf/hist.h>
#include <perf/session.h>
-#include "util/sort.h"
+#include <perf/sort.h>
#include <perf/symbol.h>
#include <lk/util.h>
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 3129210..32476b9 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -4,7 +4,7 @@
#include <lk/util.h>
#include "util/cache.h"
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <perf/header.h>
#include <perf/session.h>
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 509d11f..10a8c4d 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -4,7 +4,7 @@
#include <lk/util.h>
#include "util/cache.h"
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <perf/header.h>
#include <perf/session.h>
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 17abce4..8ded0a7 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -4,7 +4,7 @@
#include <lk/util.h>
#include "util/cache.h"
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <perf/header.h>
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index e78efad..6072b83 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -11,13 +11,13 @@
#include "perf.h"
-#include "util/build-id.h"
+#include <perf/build-id.h>
#include <lk/util.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
#include <perf/header.h>
-#include "util/event.h"
+#include <perf/event.h>
#include <lk/debug.h>
#include <perf/session.h>
#include <perf/symbol.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 98284d1..0803898 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -26,9 +26,9 @@
#include "util/parse-options.h"
#include <perf/parse-events.h>
-#include "util/thread.h"
-#include "util/sort.h"
-#include "util/hist.h"
+#include <perf/thread.h>
+#include <perf/sort.h>
+#include <perf/hist.h>
static char const *input_name = "perf.data";
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 24dabdc..6df3cb3 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -4,7 +4,7 @@
#include <lk/util.h>
#include "util/cache.h"
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <perf/header.h>
#include <perf/session.h>
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 56c47bc..e1186c0 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -42,11 +42,11 @@
#include <lk/util.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
-#include "util/event.h"
+#include <perf/event.h>
#include <lk/debug.h>
#include <perf/header.h>
#include <lk/cpumap.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <sys/prctl.h>
#include <math.h>
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index f5d6686..0028015 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -10,7 +10,7 @@
#include "util/parse-options.h"
#include <perf/session.h>
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
static long page_size;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index eddeeae..71dd659 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -28,7 +28,7 @@
#include <perf/header.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
-#include "util/event.h"
+#include <perf/event.h>
#include <perf/session.h>
#include "util/svghelper.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 8edc974..866ba3d 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -23,7 +23,7 @@
#include <lk/color.h>
#include <perf/session.h>
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <lk/util.h>
#include <linux/rbtree.h>
#include "util/parse-options.h"
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 1edffa4..585af2e 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -4,7 +4,7 @@
#include <lk/debug.h>
#include "util/cache.h"
#include <perf/symbol.h>
-#include "util/thread.h"
+#include <perf/thread.h>
#include <perf/header.h>
#include "util/exec_cmd.h"
#include "util/trace-event.h"
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 4d57b28..4268ed4 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -12,7 +12,7 @@
#include "util/cache.h"
#include "util/config.h"
#include "util/quote.h"
-#include "util/build-id.h"
+#include <perf/build-id.h>
#include "util/run-command.h"
#include <perf/parse-events.h>
#include <lk/debugfs.h>
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
deleted file mode 100644
index 5969758..0000000
--- a/tools/perf/util/build-id.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * build-id.c
- *
- * build-id support
- *
- * Copyright (C) 2009, 2010 Red Hat Inc.
- * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
- */
-#include <lk/util.h>
-#include <lk/debug.h>
-#include <lk/config.h>
-#include <lk/strbuf.h>
-#include <stdio.h>
-#include "build-id.h"
-#include "event.h"
-#include <perf/symbol.h>
-#include <linux/kernel.h>
-
-static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
-{
- struct addr_location al;
- u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- struct thread *thread = perf_session__findnew(session, event->ip.pid);
-
- if (thread == NULL) {
- pr_err("problem processing %d event, skipping it.\n",
- event->header.type);
- return -1;
- }
-
- thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
- event->ip.pid, event->ip.ip, &al);
-
- if (al.map != NULL)
- al.map->dso->hit = 1;
-
- return 0;
-}
-
-struct perf_event_ops build_id__mark_dso_hit_ops = {
- .sample = build_id__mark_dso_hit,
- .mmap = event__process_mmap,
- .fork = event__process_task,
-};
-
-char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
-{
- char build_id_hex[BUILD_ID_SIZE * 2 + 1];
-
- if (!self->has_build_id)
- return NULL;
-
- build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
- if (bf == NULL) {
- if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
- build_id_hex, build_id_hex + 2) < 0)
- return NULL;
- } else
- snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
- build_id_hex, build_id_hex + 2);
- return bf;
-}
-
-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 = lk_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/build-id.h b/tools/perf/util/build-id.h
deleted file mode 100644
index a8a6bd9..0000000
--- a/tools/perf/util/build-id.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef PERF_BUILD_ID_H_
-#define PERF_BUILD_ID_H_ 1
-
-#include <perf/session.h>
-#include "config.h"
-
-extern struct perf_event_ops build_id__mark_dso_hit_ops;
-
-char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
-extern void set_buildid_dir(void);
-
-#endif
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index dcc0254..6de3448 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -19,13 +19,6 @@
#include <lk/debug.h>
#include "callchain.h"
-bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
-{
- unsigned int chain_size = event->header.size;
- chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
- return chain->nr * sizeof(u64) <= chain_size;
-}
-
#define chain_for_each_child(child, parent) \
list_for_each_entry(child, &parent->children, brothers)
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index ca8a73d..13176e6 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -4,7 +4,7 @@
#include "../perf.h"
#include <linux/list.h>
#include <linux/rbtree.h>
-#include "event.h"
+#include <perf/event.h>
#include <perf/symbol.h>
enum chain_mode {
@@ -60,5 +60,4 @@ int register_callchain_param(struct callchain_param *param);
int append_chain(struct callchain_node *root, struct ip_callchain *chain,
struct map_symbol *syms);
-bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
deleted file mode 100644
index 1669fcc..0000000
--- a/tools/perf/util/event.c
+++ /dev/null
@@ -1,820 +0,0 @@
-#include <linux/types.h>
-#include "event.h"
-#include <lk/debug.h>
-#include <perf/session.h>
-#include "sort.h"
-#include "string.h"
-#include <lk/strlist.h>
-#include "thread.h"
-
-const char *event__name[] = {
- [0] = "TOTAL",
- [PERF_RECORD_MMAP] = "MMAP",
- [PERF_RECORD_LOST] = "LOST",
- [PERF_RECORD_COMM] = "COMM",
- [PERF_RECORD_EXIT] = "EXIT",
- [PERF_RECORD_THROTTLE] = "THROTTLE",
- [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
- [PERF_RECORD_FORK] = "FORK",
- [PERF_RECORD_READ] = "READ",
- [PERF_RECORD_SAMPLE] = "SAMPLE",
- [PERF_RECORD_HEADER_ATTR] = "ATTR",
- [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
- [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
- [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
-};
-
-static pid_t event__synthesize_comm(pid_t pid, int full,
- event__handler_t process,
- struct perf_session *session)
-{
- event_t ev;
- char filename[PATH_MAX];
- char bf[BUFSIZ];
- FILE *fp;
- size_t size = 0;
- DIR *tasks;
- struct dirent dirent, *next;
- pid_t tgid = 0;
-
- snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
-out_race:
- /*
- * We raced with a task exiting - just return:
- */
- pr_debug("couldn't open %s\n", filename);
- return 0;
- }
-
- memset(&ev.comm, 0, sizeof(ev.comm));
- while (!ev.comm.comm[0] || !ev.comm.pid) {
- if (fgets(bf, sizeof(bf), fp) == NULL)
- goto out_failure;
-
- if (memcmp(bf, "Name:", 5) == 0) {
- char *name = bf + 5;
- while (*name && isspace(*name))
- ++name;
- size = strlen(name) - 1;
- memcpy(ev.comm.comm, name, size++);
- } else if (memcmp(bf, "Tgid:", 5) == 0) {
- char *tgids = bf + 5;
- while (*tgids && isspace(*tgids))
- ++tgids;
- tgid = ev.comm.pid = atoi(tgids);
- }
- }
-
- ev.comm.header.type = PERF_RECORD_COMM;
- size = ALIGN(size, sizeof(u64));
- ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
-
- if (!full) {
- ev.comm.tid = pid;
-
- process(&ev, session);
- goto out_fclose;
- }
-
- snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
-
- tasks = opendir(filename);
- if (tasks == NULL)
- goto out_race;
-
- while (!readdir_r(tasks, &dirent, &next) && next) {
- char *end;
- pid = strtol(dirent.d_name, &end, 10);
- if (*end)
- continue;
-
- ev.comm.tid = pid;
-
- process(&ev, session);
- }
- closedir(tasks);
-
-out_fclose:
- fclose(fp);
- return tgid;
-
-out_failure:
- pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
- return -1;
-}
-
-static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
- event__handler_t process,
- struct perf_session *session)
-{
- char filename[PATH_MAX];
- FILE *fp;
-
- snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
- /*
- * We raced with a task exiting - just return:
- */
- pr_debug("couldn't open %s\n", filename);
- return -1;
- }
-
- while (1) {
- char bf[BUFSIZ], *pbf = bf;
- event_t ev = {
- .header = {
- .type = PERF_RECORD_MMAP,
- /*
- * Just like the kernel, see __perf_event_mmap
- * in kernel/perf_event.c
- */
- .misc = PERF_RECORD_MISC_USER,
- },
- };
- int n;
- size_t size;
- if (fgets(bf, sizeof(bf), fp) == NULL)
- break;
-
- /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- n = hex2u64(pbf, &ev.mmap.start);
- if (n < 0)
- continue;
- pbf += n + 1;
- n = hex2u64(pbf, &ev.mmap.len);
- if (n < 0)
- continue;
- pbf += n + 3;
- if (*pbf == 'x') { /* vm_exec */
- u64 vm_pgoff;
- char *execname = strchr(bf, '/');
-
- /* Catch VDSO */
- if (execname == NULL)
- execname = strstr(bf, "[vdso]");
-
- if (execname == NULL)
- continue;
-
- pbf += 3;
- n = hex2u64(pbf, &vm_pgoff);
- /* pgoff is in bytes, not pages */
- if (n >= 0)
- ev.mmap.pgoff = vm_pgoff << getpagesize();
- else
- ev.mmap.pgoff = 0;
-
- size = strlen(execname);
- execname[size - 1] = '\0'; /* Remove \n */
- memcpy(ev.mmap.filename, execname, size);
- size = ALIGN(size, sizeof(u64));
- ev.mmap.len -= ev.mmap.start;
- ev.mmap.header.size = (sizeof(ev.mmap) -
- (sizeof(ev.mmap.filename) - size));
- ev.mmap.pid = tgid;
- ev.mmap.tid = pid;
-
- process(&ev, session);
- }
- }
-
- fclose(fp);
- return 0;
-}
-
-int event__synthesize_modules(event__handler_t process,
- struct perf_session *session,
- struct machine *machine)
-{
- struct rb_node *nd;
- struct map_groups *kmaps = &machine->kmaps;
- u16 misc;
-
- /*
- * kernel uses 0 for user space maps, see kernel/perf_event.c
- * __perf_event_mmap
- */
- if (machine__is_host(machine))
- misc = PERF_RECORD_MISC_KERNEL;
- else
- misc = PERF_RECORD_MISC_GUEST_KERNEL;
-
- for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
- nd; nd = rb_next(nd)) {
- event_t ev;
- size_t size;
- struct map *pos = rb_entry(nd, struct map, rb_node);
-
- if (pos->dso->kernel)
- continue;
-
- size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
- memset(&ev, 0, sizeof(ev));
- ev.mmap.header.misc = misc;
- ev.mmap.header.type = PERF_RECORD_MMAP;
- ev.mmap.header.size = (sizeof(ev.mmap) -
- (sizeof(ev.mmap.filename) - size));
- ev.mmap.start = pos->start;
- ev.mmap.len = pos->end - pos->start;
- ev.mmap.pid = machine->pid;
-
- memcpy(ev.mmap.filename, pos->dso->long_name,
- pos->dso->long_name_len + 1);
- process(&ev, session);
- }
-
- return 0;
-}
-
-int event__synthesize_thread(pid_t pid, event__handler_t process,
- struct perf_session *session)
-{
- pid_t tgid = event__synthesize_comm(pid, 1, process, session);
- if (tgid == -1)
- return -1;
- return event__synthesize_mmap_events(pid, tgid, process, session);
-}
-
-void event__synthesize_threads(event__handler_t process,
- struct perf_session *session)
-{
- DIR *proc;
- struct dirent dirent, *next;
-
- proc = opendir("/proc");
-
- while (!readdir_r(proc, &dirent, &next) && next) {
- char *end;
- pid_t pid = strtol(dirent.d_name, &end, 10);
-
- if (*end) /* only interested in proper numerical dirents */
- continue;
-
- event__synthesize_thread(pid, process, session);
- }
-
- closedir(proc);
-}
-
-struct process_symbol_args {
- const char *name;
- u64 start;
-};
-
-static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
-{
- struct process_symbol_args *args = arg;
-
- /*
- * Must be a function or at least an alias, as in PARISC64, where "_text" is
- * an 'A' to the same address as "_stext".
- */
- if (!(symbol_type__is_a(type, MAP__FUNCTION) ||
- type == 'A') || strcmp(name, args->name))
- return 0;
-
- args->start = start;
- return 1;
-}
-
-int event__synthesize_kernel_mmap(event__handler_t process,
- struct perf_session *session,
- struct machine *machine,
- const char *symbol_name)
-{
- size_t size;
- const char *filename, *mmap_name;
- char path[PATH_MAX];
- char name_buff[PATH_MAX];
- struct map *map;
-
- event_t ev = {
- .header = {
- .type = PERF_RECORD_MMAP,
- },
- };
- /*
- * We should get this from /sys/kernel/sections/.text, but till that is
- * available use this, and after it is use this as a fallback for older
- * kernels.
- */
- struct process_symbol_args args = { .name = symbol_name, };
-
- mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));
- if (machine__is_host(machine)) {
- /*
- * kernel uses PERF_RECORD_MISC_USER for user space maps,
- * see kernel/perf_event.c __perf_event_mmap
- */
- ev.header.misc = PERF_RECORD_MISC_KERNEL;
- filename = "/proc/kallsyms";
- } else {
- ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
- if (machine__is_default_guest(machine))
- filename = (char *) symbol_conf.default_guest_kallsyms;
- else {
- sprintf(path, "%s/proc/kallsyms", machine->root_dir);
- filename = path;
- }
- }
-
- if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
- return -ENOENT;
-
- map = machine->vmlinux_maps[MAP__FUNCTION];
- size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
- "%s%s", mmap_name, symbol_name) + 1;
- size = ALIGN(size, sizeof(u64));
- ev.mmap.header.size = (sizeof(ev.mmap) -
- (sizeof(ev.mmap.filename) - size));
- ev.mmap.pgoff = args.start;
- ev.mmap.start = map->start;
- ev.mmap.len = map->end - ev.mmap.start;
- ev.mmap.pid = machine->pid;
-
- return process(&ev, session);
-}
-
-static void thread__comm_adjust(struct thread *self)
-{
- char *comm = self->comm;
-
- if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
- (!symbol_conf.comm_list ||
- strlist__has_entry(symbol_conf.comm_list, comm))) {
- unsigned int slen = strlen(comm);
-
- if (slen > comms__col_width) {
- comms__col_width = slen;
- threads__col_width = slen + 6;
- }
- }
-}
-
-static int thread__set_comm_adjust(struct thread *self, const char *comm)
-{
- int ret = thread__set_comm(self, comm);
-
- if (ret)
- return ret;
-
- thread__comm_adjust(self);
-
- return 0;
-}
-
-int event__process_comm(event_t *self, struct perf_session *session)
-{
- struct thread *thread = perf_session__findnew(session, self->comm.tid);
-
- dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
-
- if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
- dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
- return -1;
- }
-
- return 0;
-}
-
-int event__process_lost(event_t *self, struct perf_session *session)
-{
- dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
- session->hists.stats.total_lost += self->lost.lost;
- return 0;
-}
-
-static void event_set_kernel_mmap_len(struct map **maps, event_t *self)
-{
- maps[MAP__FUNCTION]->start = self->mmap.start;
- maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
- /*
- * Be a bit paranoid here, some perf.data file came with
- * a zero sized synthesized MMAP event for the kernel.
- */
- if (maps[MAP__FUNCTION]->end == 0)
- maps[MAP__FUNCTION]->end = ~0UL;
-}
-
-static int event__process_kernel_mmap(event_t *self,
- struct perf_session *session)
-{
- struct map *map;
- char kmmap_prefix[PATH_MAX];
- struct machine *machine;
- enum dso_kernel_type kernel_type;
- bool is_kernel_mmap;
-
- machine = perf_session__findnew_machine(session, self->mmap.pid);
- if (!machine) {
- pr_err("Can't find id %d's machine\n", self->mmap.pid);
- goto out_problem;
- }
-
- machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
- if (machine__is_host(machine))
- kernel_type = DSO_TYPE_KERNEL;
- else
- kernel_type = DSO_TYPE_GUEST_KERNEL;
-
- is_kernel_mmap = memcmp(self->mmap.filename,
- kmmap_prefix,
- strlen(kmmap_prefix)) == 0;
- if (self->mmap.filename[0] == '/' ||
- (!is_kernel_mmap && self->mmap.filename[0] == '[')) {
-
- char short_module_name[1024];
- char *name, *dot;
-
- if (self->mmap.filename[0] == '/') {
- name = strrchr(self->mmap.filename, '/');
- if (name == NULL)
- goto out_problem;
-
- ++name; /* skip / */
- dot = strrchr(name, '.');
- if (dot == NULL)
- goto out_problem;
- snprintf(short_module_name, sizeof(short_module_name),
- "[%.*s]", (int)(dot - name), name);
- strxfrchar(short_module_name, '-', '_');
- } else
- strcpy(short_module_name, self->mmap.filename);
-
- map = machine__new_module(machine, self->mmap.start,
- self->mmap.filename);
- if (map == NULL)
- goto out_problem;
-
- name = strdup(short_module_name);
- if (name == NULL)
- goto out_problem;
-
- map->dso->short_name = name;
- map->end = map->start + self->mmap.len;
- } else if (is_kernel_mmap) {
- const char *symbol_name = (self->mmap.filename +
- strlen(kmmap_prefix));
- /*
- * Should be there already, from the build-id table in
- * the header.
- */
- struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
- kmmap_prefix);
- if (kernel == NULL)
- goto out_problem;
-
- kernel->kernel = kernel_type;
- if (__machine__create_kernel_maps(machine, kernel) < 0)
- goto out_problem;
-
- event_set_kernel_mmap_len(machine->vmlinux_maps, self);
- perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
- symbol_name,
- self->mmap.pgoff);
- if (machine__is_default_guest(machine)) {
- /*
- * preload dso of guest kernel and modules
- */
- dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
- NULL);
- }
- }
- return 0;
-out_problem:
- return -1;
-}
-
-int event__process_mmap(event_t *self, struct perf_session *session)
-{
- struct machine *machine;
- struct thread *thread;
- struct map *map;
- u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- int ret = 0;
-
- dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
- self->mmap.pid, self->mmap.tid, self->mmap.start,
- self->mmap.len, self->mmap.pgoff, self->mmap.filename);
-
- if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
- cpumode == PERF_RECORD_MISC_KERNEL) {
- ret = event__process_kernel_mmap(self, session);
- if (ret < 0)
- goto out_problem;
- return 0;
- }
-
- machine = perf_session__find_host_machine(session);
- if (machine == NULL)
- goto out_problem;
- thread = perf_session__findnew(session, self->mmap.pid);
- map = map__new(&machine->user_dsos, self->mmap.start,
- self->mmap.len, self->mmap.pgoff,
- self->mmap.pid, self->mmap.filename,
- MAP__FUNCTION, session->cwd, session->cwdlen);
-
- if (thread == NULL || map == NULL)
- goto out_problem;
-
- thread__insert_map(thread, map);
- return 0;
-
-out_problem:
- dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
- return 0;
-}
-
-int event__process_task(event_t *self, struct perf_session *session)
-{
- struct thread *thread = perf_session__findnew(session, self->fork.tid);
- struct thread *parent = perf_session__findnew(session, self->fork.ptid);
-
- dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
- self->fork.ppid, self->fork.ptid);
-
- if (self->header.type == PERF_RECORD_EXIT)
- return 0;
-
- if (thread == NULL || parent == NULL ||
- thread__fork(thread, parent) < 0) {
- dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
- return -1;
- }
-
- return 0;
-}
-
-void thread__find_addr_map(struct thread *self,
- struct perf_session *session, u8 cpumode,
- enum map_type type, pid_t pid, u64 addr,
- struct addr_location *al)
-{
- struct map_groups *mg = &self->mg;
- struct machine *machine = NULL;
-
- al->thread = self;
- al->addr = addr;
- al->cpumode = cpumode;
- al->filtered = false;
-
- if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
- al->level = 'k';
- machine = perf_session__find_host_machine(session);
- if (machine == NULL) {
- al->map = NULL;
- return;
- }
- mg = &machine->kmaps;
- } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
- al->level = '.';
- machine = perf_session__find_host_machine(session);
- } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
- al->level = 'g';
- machine = perf_session__find_machine(session, pid);
- if (machine == NULL) {
- al->map = NULL;
- return;
- }
- mg = &machine->kmaps;
- } else {
- /*
- * 'u' means guest os user space.
- * TODO: We don't support guest user space. Might support late.
- */
- if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest)
- al->level = 'u';
- else
- al->level = 'H';
- al->map = NULL;
-
- if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
- cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
- !perf_guest)
- al->filtered = true;
- if ((cpumode == PERF_RECORD_MISC_USER ||
- cpumode == PERF_RECORD_MISC_KERNEL) &&
- !perf_host)
- al->filtered = true;
-
- return;
- }
-try_again:
- al->map = map_groups__find(mg, type, al->addr);
- if (al->map == NULL) {
- /*
- * If this is outside of all known maps, and is a negative
- * address, try to look it up in the kernel dso, as it might be
- * a vsyscall or vdso (which executes in user-mode).
- *
- * XXX This is nasty, we should have a symbol list in the
- * "[vdso]" dso, but for now lets use the old trick of looking
- * in the whole kernel symbol list.
- */
- if ((long long)al->addr < 0 &&
- cpumode == PERF_RECORD_MISC_KERNEL &&
- machine && mg != &machine->kmaps) {
- mg = &machine->kmaps;
- goto try_again;
- }
- } else
- al->addr = al->map->map_ip(al->map, al->addr);
-}
-
-void thread__find_addr_location(struct thread *self,
- struct perf_session *session, u8 cpumode,
- enum map_type type, pid_t pid, u64 addr,
- struct addr_location *al,
- symbol_filter_t filter)
-{
- thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
- if (al->map != NULL)
- al->sym = map__find_symbol(al->map, al->addr, filter);
- else
- al->sym = NULL;
-}
-
-static void dso__calc_col_width(struct dso *self)
-{
- if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
- (!symbol_conf.dso_list ||
- strlist__has_entry(symbol_conf.dso_list, self->name))) {
- u16 slen = self->short_name_len;
- if (verbose)
- slen = self->long_name_len;
- if (dsos__col_width < slen)
- dsos__col_width = slen;
- }
-
- self->slen_calculated = 1;
-}
-
-int event__preprocess_sample(const event_t *self, struct perf_session *session,
- struct addr_location *al, struct sample_data *data,
- symbol_filter_t filter)
-{
- u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- struct thread *thread;
-
- event__parse_sample(self, session->sample_type, data);
-
- dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n",
- self->header.misc, data->pid, data->tid, data->ip,
- data->period, data->cpu);
-
- if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
- unsigned int i;
-
- dump_printf("... chain: nr:%Lu\n", data->callchain->nr);
-
- if (!ip_callchain__valid(data->callchain, self)) {
- pr_debug("call-chain problem with event, "
- "skipping it.\n");
- goto out_filtered;
- }
-
- if (dump_trace) {
- for (i = 0; i < data->callchain->nr; i++)
- dump_printf("..... %2d: %016Lx\n",
- i, data->callchain->ips[i]);
- }
- }
- thread = perf_session__findnew(session, self->ip.pid);
- if (thread == NULL)
- return -1;
-
- if (symbol_conf.comm_list &&
- !strlist__has_entry(symbol_conf.comm_list, thread->comm))
- goto out_filtered;
-
- dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
- /*
- * Have we already created the kernel maps for the host machine?
- *
- * This should have happened earlier, when we processed the kernel MMAP
- * events, but for older perf.data files there was no such thing, so do
- * it now.
- */
- if (cpumode == PERF_RECORD_MISC_KERNEL &&
- session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
- machine__create_kernel_maps(&session->host_machine);
-
- thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
- self->ip.pid, self->ip.ip, al);
- dump_printf(" ...... dso: %s\n",
- al->map ? al->map->dso->long_name :
- al->level == 'H' ? "[hypervisor]" : "<not found>");
- al->sym = NULL;
- al->cpu = data->cpu;
-
- if (al->map) {
- if (symbol_conf.dso_list &&
- (!al->map || !al->map->dso ||
- !(strlist__has_entry(symbol_conf.dso_list,
- al->map->dso->short_name) ||
- (al->map->dso->short_name != al->map->dso->long_name &&
- strlist__has_entry(symbol_conf.dso_list,
- al->map->dso->long_name)))))
- goto out_filtered;
- /*
- * We have to do this here as we may have a dso with no symbol
- * hit that has a name longer than the ones with symbols
- * sampled.
- */
- if (!sort_dso.elide && !al->map->dso->slen_calculated)
- dso__calc_col_width(al->map->dso);
-
- al->sym = map__find_symbol(al->map, al->addr, filter);
- } else {
- const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
-
- if (dsos__col_width < unresolved_col_width &&
- !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
- !symbol_conf.dso_list)
- dsos__col_width = unresolved_col_width;
- }
-
- if (symbol_conf.sym_list && al->sym &&
- !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
- goto out_filtered;
-
- return 0;
-
-out_filtered:
- al->filtered = true;
- return 0;
-}
-
-int event__parse_sample(const event_t *event, u64 type, struct sample_data *data)
-{
- const u64 *array = event->sample.array;
-
- if (type & PERF_SAMPLE_IP) {
- data->ip = event->ip.ip;
- array++;
- }
-
- if (type & PERF_SAMPLE_TID) {
- u32 *p = (u32 *)array;
- data->pid = p[0];
- data->tid = p[1];
- array++;
- }
-
- if (type & PERF_SAMPLE_TIME) {
- data->time = *array;
- array++;
- }
-
- if (type & PERF_SAMPLE_ADDR) {
- data->addr = *array;
- array++;
- }
-
- data->id = -1ULL;
- if (type & PERF_SAMPLE_ID) {
- data->id = *array;
- array++;
- }
-
- if (type & PERF_SAMPLE_STREAM_ID) {
- data->stream_id = *array;
- array++;
- }
-
- if (type & PERF_SAMPLE_CPU) {
- u32 *p = (u32 *)array;
- data->cpu = *p;
- array++;
- } else
- data->cpu = -1;
-
- if (type & PERF_SAMPLE_PERIOD) {
- data->period = *array;
- array++;
- }
-
- if (type & PERF_SAMPLE_READ) {
- pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
- return -1;
- }
-
- if (type & PERF_SAMPLE_CALLCHAIN) {
- data->callchain = (struct ip_callchain *)array;
- array += 1 + data->callchain->nr;
- }
-
- if (type & PERF_SAMPLE_RAW) {
- u32 *p = (u32 *)array;
- data->raw_size = *p;
- p++;
- data->raw_data = p;
- }
-
- return 0;
-}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
deleted file mode 100644
index fef5236..0000000
--- a/tools/perf/util/event.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef __PERF_RECORD_H
-#define __PERF_RECORD_H
-
-#include <limits.h>
-
-#include "../perf.h"
-#include <perf/map.h>
-
-/*
- * PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
- */
-struct ip_event {
- struct perf_event_header header;
- u64 ip;
- u32 pid, tid;
- unsigned char __more_data[];
-};
-
-struct mmap_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 start;
- u64 len;
- u64 pgoff;
- char filename[PATH_MAX];
-};
-
-struct comm_event {
- struct perf_event_header header;
- u32 pid, tid;
- char comm[16];
-};
-
-struct fork_event {
- struct perf_event_header header;
- u32 pid, ppid;
- u32 tid, ptid;
- u64 time;
-};
-
-struct lost_event {
- struct perf_event_header header;
- u64 id;
- u64 lost;
-};
-
-/*
- * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
- */
-struct read_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 value;
- u64 time_enabled;
- u64 time_running;
- u64 id;
-};
-
-struct sample_event {
- struct perf_event_header header;
- u64 array[];
-};
-
-struct sample_data {
- u64 ip;
- u32 pid, tid;
- u64 time;
- u64 addr;
- u64 id;
- u64 stream_id;
- u64 period;
- u32 cpu;
- u32 raw_size;
- void *raw_data;
- struct ip_callchain *callchain;
-};
-
-#define BUILD_ID_SIZE 20
-
-struct build_id_event {
- struct perf_event_header header;
- pid_t pid;
- u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
- char filename[];
-};
-
-enum perf_user_event_type { /* above any possible kernel type */
- PERF_RECORD_HEADER_ATTR = 64,
- PERF_RECORD_HEADER_EVENT_TYPE = 65,
- PERF_RECORD_HEADER_TRACING_DATA = 66,
- PERF_RECORD_HEADER_BUILD_ID = 67,
- PERF_RECORD_FINISHED_ROUND = 68,
- PERF_RECORD_HEADER_MAX
-};
-
-struct attr_event {
- struct perf_event_header header;
- struct perf_event_attr attr;
- u64 id[];
-};
-
-#define MAX_EVENT_NAME 64
-
-struct perf_trace_event_type {
- u64 event_id;
- char name[MAX_EVENT_NAME];
-};
-
-struct event_type_event {
- struct perf_event_header header;
- struct perf_trace_event_type event_type;
-};
-
-struct tracing_data_event {
- struct perf_event_header header;
- u32 size;
-};
-
-typedef union event_union {
- struct perf_event_header header;
- struct ip_event ip;
- struct mmap_event mmap;
- struct comm_event comm;
- struct fork_event fork;
- struct lost_event lost;
- struct read_event read;
- struct sample_event sample;
- struct attr_event attr;
- struct event_type_event event_type;
- struct tracing_data_event tracing_data;
- struct build_id_event build_id;
-} event_t;
-
-void event__print_totals(void);
-
-struct perf_session;
-
-typedef int (*event__handler_t)(event_t *event, struct perf_session *session);
-
-int event__synthesize_thread(pid_t pid, event__handler_t process,
- struct perf_session *session);
-void event__synthesize_threads(event__handler_t process,
- struct perf_session *session);
-int event__synthesize_kernel_mmap(event__handler_t process,
- struct perf_session *session,
- struct machine *machine,
- const char *symbol_name);
-
-int event__synthesize_modules(event__handler_t process,
- struct perf_session *session,
- struct machine *machine);
-
-int event__process_comm(event_t *self, struct perf_session *session);
-int event__process_lost(event_t *self, struct perf_session *session);
-int event__process_mmap(event_t *self, struct perf_session *session);
-int event__process_task(event_t *self, struct perf_session *session);
-
-struct addr_location;
-int event__preprocess_sample(const event_t *self, struct perf_session *session,
- struct addr_location *al, struct sample_data *data,
- symbol_filter_t filter);
-int event__parse_sample(const event_t *event, u64 type, struct sample_data *data);
-
-extern const char *event__name[];
-
-#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
deleted file mode 100644
index 2dfa141..0000000
--- a/tools/perf/util/hist.c
+++ /dev/null
@@ -1,1082 +0,0 @@
-#include <lk/util.h>
-#include "build-id.h"
-#include "hist.h"
-#include <perf/session.h>
-#include "sort.h"
-#include <math.h>
-
-struct callchain_param callchain_param = {
- .mode = CHAIN_GRAPH_REL,
- .min_percent = 0.5
-};
-
-static void hist_entry__add_cpumode_period(struct hist_entry *self,
- unsigned int cpumode, u64 period)
-{
- switch (cpumode) {
- case PERF_RECORD_MISC_KERNEL:
- self->period_sys += period;
- break;
- case PERF_RECORD_MISC_USER:
- self->period_us += period;
- break;
- case PERF_RECORD_MISC_GUEST_KERNEL:
- self->period_guest_sys += period;
- break;
- case PERF_RECORD_MISC_GUEST_USER:
- self->period_guest_us += period;
- break;
- default:
- break;
- }
-}
-
-/*
- * histogram, sorted on item, collects periods
- */
-
-static struct hist_entry *hist_entry__new(struct hist_entry *template)
-{
- size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0;
- struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
-
- if (self != NULL) {
- *self = *template;
- self->nr_events = 1;
- if (symbol_conf.use_callchain)
- callchain_init(self->callchain);
- }
-
- return self;
-}
-
-static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry)
-{
- if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen)
- self->max_sym_namelen = entry->ms.sym->namelen;
- ++self->nr_entries;
-}
-
-struct hist_entry *__hists__add_entry(struct hists *self,
- struct addr_location *al,
- struct symbol *sym_parent, u64 period)
-{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
- struct hist_entry *he;
- struct hist_entry entry = {
- .thread = al->thread,
- .ms = {
- .map = al->map,
- .sym = al->sym,
- },
- .cpu = al->cpu,
- .ip = al->addr,
- .level = al->level,
- .period = period,
- .parent = sym_parent,
- };
- int cmp;
-
- while (*p != NULL) {
- parent = *p;
- he = rb_entry(parent, struct hist_entry, rb_node);
-
- cmp = hist_entry__cmp(&entry, he);
-
- if (!cmp) {
- he->period += period;
- ++he->nr_events;
- goto out;
- }
-
- if (cmp < 0)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- he = hist_entry__new(&entry);
- if (!he)
- return NULL;
- rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, &self->entries);
- hists__inc_nr_entries(self, he);
-out:
- hist_entry__add_cpumode_period(he, al->cpumode, period);
- return he;
-}
-
-int64_t
-hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
-{
- struct sort_entry *se;
- int64_t cmp = 0;
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- cmp = se->se_cmp(left, right);
- if (cmp)
- break;
- }
-
- return cmp;
-}
-
-int64_t
-hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
-{
- struct sort_entry *se;
- int64_t cmp = 0;
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- int64_t (*f)(struct hist_entry *, struct hist_entry *);
-
- f = se->se_collapse ?: se->se_cmp;
-
- cmp = f(left, right);
- if (cmp)
- break;
- }
-
- return cmp;
-}
-
-void hist_entry__free(struct hist_entry *he)
-{
- free(he);
-}
-
-/*
- * collapse the histogram
- */
-
-static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
-{
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
- struct hist_entry *iter;
- int64_t cmp;
-
- while (*p != NULL) {
- parent = *p;
- iter = rb_entry(parent, struct hist_entry, rb_node);
-
- cmp = hist_entry__collapse(iter, he);
-
- if (!cmp) {
- iter->period += he->period;
- hist_entry__free(he);
- return false;
- }
-
- if (cmp < 0)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, root);
- return true;
-}
-
-void hists__collapse_resort(struct hists *self)
-{
- struct rb_root tmp;
- struct rb_node *next;
- struct hist_entry *n;
-
- if (!sort__need_collapse)
- return;
-
- tmp = RB_ROOT;
- next = rb_first(&self->entries);
- self->nr_entries = 0;
- self->max_sym_namelen = 0;
-
- while (next) {
- n = rb_entry(next, struct hist_entry, rb_node);
- next = rb_next(&n->rb_node);
-
- rb_erase(&n->rb_node, &self->entries);
- if (collapse__insert_entry(&tmp, n))
- hists__inc_nr_entries(self, n);
- }
-
- self->entries = tmp;
-}
-
-/*
- * reverse the map, sort on period.
- */
-
-static void __hists__insert_output_entry(struct rb_root *entries,
- struct hist_entry *he,
- u64 min_callchain_hits)
-{
- struct rb_node **p = &entries->rb_node;
- struct rb_node *parent = NULL;
- struct hist_entry *iter;
-
- if (symbol_conf.use_callchain)
- callchain_param.sort(&he->sorted_chain, he->callchain,
- min_callchain_hits, &callchain_param);
-
- while (*p != NULL) {
- parent = *p;
- iter = rb_entry(parent, struct hist_entry, rb_node);
-
- if (he->period > iter->period)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, entries);
-}
-
-void hists__output_resort(struct hists *self)
-{
- struct rb_root tmp;
- struct rb_node *next;
- struct hist_entry *n;
- u64 min_callchain_hits;
-
- min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100);
-
- tmp = RB_ROOT;
- next = rb_first(&self->entries);
-
- self->nr_entries = 0;
- self->max_sym_namelen = 0;
-
- while (next) {
- n = rb_entry(next, struct hist_entry, rb_node);
- next = rb_next(&n->rb_node);
-
- rb_erase(&n->rb_node, &self->entries);
- __hists__insert_output_entry(&tmp, n, min_callchain_hits);
- hists__inc_nr_entries(self, n);
- }
-
- self->entries = tmp;
-}
-
-static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
-{
- int i;
- int ret = fprintf(fp, " ");
-
- for (i = 0; i < left_margin; i++)
- ret += fprintf(fp, " ");
-
- return ret;
-}
-
-static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
- int left_margin)
-{
- int i;
- size_t ret = callchain__fprintf_left_margin(fp, left_margin);
-
- for (i = 0; i < depth; i++)
- if (depth_mask & (1 << i))
- ret += fprintf(fp, "| ");
- else
- ret += fprintf(fp, " ");
-
- ret += fprintf(fp, "\n");
-
- return ret;
-}
-
-static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
- int depth, int depth_mask, int period,
- u64 total_samples, int hits,
- int left_margin)
-{
- int i;
- size_t ret = 0;
-
- ret += callchain__fprintf_left_margin(fp, left_margin);
- for (i = 0; i < depth; i++) {
- if (depth_mask & (1 << i))
- ret += fprintf(fp, "|");
- else
- ret += fprintf(fp, " ");
- if (!period && i == depth - 1) {
- double percent;
-
- percent = hits * 100.0 / total_samples;
- ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
- } else
- ret += fprintf(fp, "%s", " ");
- }
- if (chain->ms.sym)
- ret += fprintf(fp, "%s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
-
- return ret;
-}
-
-static struct symbol *rem_sq_bracket;
-static struct callchain_list rem_hits;
-
-static void init_rem_hits(void)
-{
- rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
- if (!rem_sq_bracket) {
- fprintf(stderr, "Not enough memory to display remaining hits\n");
- return;
- }
-
- strcpy(rem_sq_bracket->name, "[...]");
- rem_hits.ms.sym = rem_sq_bracket;
-}
-
-static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
- u64 total_samples, int depth,
- int depth_mask, int left_margin)
-{
- struct rb_node *node, *next;
- struct callchain_node *child;
- struct callchain_list *chain;
- int new_depth_mask = depth_mask;
- u64 new_total;
- u64 remaining;
- size_t ret = 0;
- int i;
- uint entries_printed = 0;
-
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = self->children_hit;
- else
- new_total = total_samples;
-
- remaining = new_total;
-
- node = rb_first(&self->rb_root);
- while (node) {
- u64 cumul;
-
- child = rb_entry(node, struct callchain_node, rb_node);
- cumul = cumul_hits(child);
- remaining -= cumul;
-
- /*
- * The depth mask manages the output of pipes that show
- * the depth. We don't want to keep the pipes of the current
- * level for the last child of this depth.
- * Except if we have remaining filtered hits. They will
- * supersede the last child
- */
- next = rb_next(node);
- if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
- new_depth_mask &= ~(1 << (depth - 1));
-
- /*
- * But we keep the older depth mask for the line separator
- * to keep the level link until we reach the last child
- */
- ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
- left_margin);
- i = 0;
- list_for_each_entry(chain, &child->val, list) {
- ret += ipchain__fprintf_graph(fp, chain, depth,
- new_depth_mask, i++,
- new_total,
- cumul,
- left_margin);
- }
- ret += __callchain__fprintf_graph(fp, child, new_total,
- depth + 1,
- new_depth_mask | (1 << depth),
- left_margin);
- node = next;
- if (++entries_printed == callchain_param.print_limit)
- break;
- }
-
- if (callchain_param.mode == CHAIN_GRAPH_REL &&
- remaining && remaining != new_total) {
-
- if (!rem_sq_bracket)
- return ret;
-
- new_depth_mask &= ~(1 << (depth - 1));
-
- ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
- new_depth_mask, 0, new_total,
- remaining, left_margin);
- }
-
- return ret;
-}
-
-static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
- u64 total_samples, int left_margin)
-{
- struct callchain_list *chain;
- bool printed = false;
- int i = 0;
- int ret = 0;
- u32 entries_printed = 0;
-
- list_for_each_entry(chain, &self->val, list) {
- if (!i++ && sort__first_dimension == SORT_SYM)
- continue;
-
- if (!printed) {
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "|\n");
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "---");
-
- left_margin += 3;
- printed = true;
- } else
- ret += callchain__fprintf_left_margin(fp, left_margin);
-
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
-
- if (++entries_printed == callchain_param.print_limit)
- break;
- }
-
- ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
-
- return ret;
-}
-
-static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
- u64 total_samples)
-{
- struct callchain_list *chain;
- size_t ret = 0;
-
- if (!self)
- return 0;
-
- ret += callchain__fprintf_flat(fp, self->parent, total_samples);
-
-
- list_for_each_entry(chain, &self->val, list) {
- if (chain->ip >= PERF_CONTEXT_MAX)
- continue;
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n",
- (void *)(long)chain->ip);
- }
-
- return ret;
-}
-
-static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
- u64 total_samples, int left_margin)
-{
- struct rb_node *rb_node;
- struct callchain_node *chain;
- size_t ret = 0;
- u32 entries_printed = 0;
-
- rb_node = rb_first(&self->sorted_chain);
- while (rb_node) {
- double percent;
-
- chain = rb_entry(rb_node, struct callchain_node, rb_node);
- percent = chain->hit * 100.0 / total_samples;
- switch (callchain_param.mode) {
- case CHAIN_FLAT:
- ret += percent_color_fprintf(fp, " %6.2f%%\n",
- percent);
- ret += callchain__fprintf_flat(fp, chain, total_samples);
- break;
- case CHAIN_GRAPH_ABS: /* Falldown */
- case CHAIN_GRAPH_REL:
- ret += callchain__fprintf_graph(fp, chain, total_samples,
- left_margin);
- case CHAIN_NONE:
- default:
- break;
- }
- ret += fprintf(fp, "\n");
- if (++entries_printed == callchain_param.print_limit)
- break;
- rb_node = rb_next(rb_node);
- }
-
- return ret;
-}
-
-int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
- struct hists *pair_hists, bool show_displacement,
- long displacement, bool color, u64 session_total)
-{
- struct sort_entry *se;
- u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
- const char *sep = symbol_conf.field_sep;
- int ret;
-
- if (symbol_conf.exclude_other && !self->parent)
- return 0;
-
- if (pair_hists) {
- period = self->pair ? self->pair->period : 0;
- total = pair_hists->stats.total_period;
- period_sys = self->pair ? self->pair->period_sys : 0;
- period_us = self->pair ? self->pair->period_us : 0;
- period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
- period_guest_us = self->pair ? self->pair->period_guest_us : 0;
- } else {
- period = self->period;
- total = session_total;
- period_sys = self->period_sys;
- period_us = self->period_us;
- period_guest_sys = self->period_guest_sys;
- period_guest_us = self->period_guest_us;
- }
-
- if (total) {
- if (color)
- ret = percent_color_snprintf(s, size,
- sep ? "%.2f" : " %6.2f%%",
- (period * 100.0) / total);
- else
- ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
- (period * 100.0) / total);
- if (symbol_conf.show_cpu_utilization) {
- ret += percent_color_snprintf(s + ret, size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_sys * 100.0) / total);
- ret += percent_color_snprintf(s + ret, size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_us * 100.0) / total);
- if (perf_guest) {
- ret += percent_color_snprintf(s + ret,
- size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_guest_sys * 100.0) /
- total);
- ret += percent_color_snprintf(s + ret,
- size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_guest_us * 100.0) /
- total);
- }
- }
- } else
- ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period);
-
- if (symbol_conf.show_nr_samples) {
- if (sep)
- ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period);
- else
- ret += snprintf(s + ret, size - ret, "%11lld", period);
- }
-
- if (pair_hists) {
- char bf[32];
- double old_percent = 0, new_percent = 0, diff;
-
- if (total > 0)
- old_percent = (period * 100.0) / total;
- if (session_total > 0)
- new_percent = (self->period * 100.0) / session_total;
-
- diff = new_percent - old_percent;
-
- if (fabs(diff) >= 0.01)
- snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
- else
- snprintf(bf, sizeof(bf), " ");
-
- if (sep)
- ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
- else
- ret += snprintf(s + ret, size - ret, "%11.11s", bf);
-
- if (show_displacement) {
- if (displacement)
- snprintf(bf, sizeof(bf), "%+4ld", displacement);
- else
- snprintf(bf, sizeof(bf), " ");
-
- if (sep)
- ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
- else
- ret += snprintf(s + ret, size - ret, "%6.6s", bf);
- }
- }
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
-
- ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
- ret += se->se_snprintf(self, s + ret, size - ret,
- se->se_width ? *se->se_width : 0);
- }
-
- return ret;
-}
-
-int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
- bool show_displacement, long displacement, FILE *fp,
- u64 session_total)
-{
- char bf[512];
- hist_entry__snprintf(self, bf, sizeof(bf), pair_hists,
- show_displacement, displacement,
- true, session_total);
- return fprintf(fp, "%s\n", bf);
-}
-
-static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
- u64 session_total)
-{
- int left_margin = 0;
-
- if (sort__first_dimension == SORT_COMM) {
- struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
- typeof(*se), list);
- left_margin = se->se_width ? *se->se_width : 0;
- left_margin -= thread__comm_len(self->thread);
- }
-
- return hist_entry_callchain__fprintf(fp, self, session_total,
- left_margin);
-}
-
-size_t hists__fprintf(struct hists *self, struct hists *pair,
- bool show_displacement, FILE *fp)
-{
- struct sort_entry *se;
- struct rb_node *nd;
- size_t ret = 0;
- unsigned long position = 1;
- long displacement = 0;
- unsigned int width;
- const char *sep = symbol_conf.field_sep;
- const char *col_width = symbol_conf.col_width_list_str;
-
- init_rem_hits();
-
- fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
-
- if (symbol_conf.show_nr_samples) {
- if (sep)
- fprintf(fp, "%cSamples", *sep);
- else
- fputs(" Samples ", fp);
- }
-
- if (symbol_conf.show_cpu_utilization) {
- if (sep) {
- ret += fprintf(fp, "%csys", *sep);
- ret += fprintf(fp, "%cus", *sep);
- if (perf_guest) {
- ret += fprintf(fp, "%cguest sys", *sep);
- ret += fprintf(fp, "%cguest us", *sep);
- }
- } else {
- ret += fprintf(fp, " sys ");
- ret += fprintf(fp, " us ");
- if (perf_guest) {
- ret += fprintf(fp, " guest sys ");
- ret += fprintf(fp, " guest us ");
- }
- }
- }
-
- if (pair) {
- if (sep)
- ret += fprintf(fp, "%cDelta", *sep);
- else
- ret += fprintf(fp, " Delta ");
-
- if (show_displacement) {
- if (sep)
- ret += fprintf(fp, "%cDisplacement", *sep);
- else
- ret += fprintf(fp, " Displ");
- }
- }
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
- if (sep) {
- fprintf(fp, "%c%s", *sep, se->se_header);
- continue;
- }
- width = strlen(se->se_header);
- if (se->se_width) {
- if (symbol_conf.col_width_list_str) {
- if (col_width) {
- *se->se_width = atoi(col_width);
- col_width = strchr(col_width, ',');
- if (col_width)
- ++col_width;
- }
- }
- width = *se->se_width = max(*se->se_width, width);
- }
- fprintf(fp, " %*s", width, se->se_header);
- }
- fprintf(fp, "\n");
-
- if (sep)
- goto print_entries;
-
- fprintf(fp, "# ........");
- if (symbol_conf.show_nr_samples)
- fprintf(fp, " ..........");
- if (pair) {
- fprintf(fp, " ..........");
- if (show_displacement)
- fprintf(fp, " .....");
- }
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- unsigned int i;
-
- if (se->elide)
- continue;
-
- fprintf(fp, " ");
- if (se->se_width)
- width = *se->se_width;
- else
- width = strlen(se->se_header);
- for (i = 0; i < width; i++)
- fprintf(fp, ".");
- }
-
- fprintf(fp, "\n#\n");
-
-print_entries:
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (show_displacement) {
- if (h->pair != NULL)
- displacement = ((long)h->pair->position -
- (long)position);
- else
- displacement = 0;
- ++position;
- }
- ret += hist_entry__fprintf(h, pair, show_displacement,
- displacement, fp, self->stats.total_period);
-
- if (symbol_conf.use_callchain)
- ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period);
-
- if (h->ms.map == NULL && verbose > 1) {
- __map_groups__fprintf_maps(&h->thread->mg,
- MAP__FUNCTION, verbose, fp);
- fprintf(fp, "%.10s end\n", graph_dotted_line);
- }
- }
-
- free(rem_sq_bracket);
-
- return ret;
-}
-
-enum hist_filter {
- HIST_FILTER__DSO,
- HIST_FILTER__THREAD,
-};
-
-void hists__filter_by_dso(struct hists *self, const struct dso *dso)
-{
- struct rb_node *nd;
-
- self->nr_entries = self->stats.total_period = 0;
- self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
- self->max_sym_namelen = 0;
-
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (symbol_conf.exclude_other && !h->parent)
- continue;
-
- if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
- h->filtered |= (1 << HIST_FILTER__DSO);
- continue;
- }
-
- h->filtered &= ~(1 << HIST_FILTER__DSO);
- if (!h->filtered) {
- ++self->nr_entries;
- self->stats.total_period += h->period;
- self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
- if (h->ms.sym &&
- self->max_sym_namelen < h->ms.sym->namelen)
- self->max_sym_namelen = h->ms.sym->namelen;
- }
- }
-}
-
-void hists__filter_by_thread(struct hists *self, const struct thread *thread)
-{
- struct rb_node *nd;
-
- self->nr_entries = self->stats.total_period = 0;
- self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
- self->max_sym_namelen = 0;
-
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (thread != NULL && h->thread != thread) {
- h->filtered |= (1 << HIST_FILTER__THREAD);
- continue;
- }
- h->filtered &= ~(1 << HIST_FILTER__THREAD);
- if (!h->filtered) {
- ++self->nr_entries;
- self->stats.total_period += h->period;
- self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
- if (h->ms.sym &&
- self->max_sym_namelen < h->ms.sym->namelen)
- self->max_sym_namelen = h->ms.sym->namelen;
- }
- }
-}
-
-static int symbol__alloc_hist(struct symbol *self)
-{
- struct sym_priv *priv = symbol__priv(self);
- const int size = (sizeof(*priv->hist) +
- (self->end - self->start) * sizeof(u64));
-
- priv->hist = zalloc(size);
- return priv->hist == NULL ? -1 : 0;
-}
-
-int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
-{
- unsigned int sym_size, offset;
- struct symbol *sym = self->ms.sym;
- struct sym_priv *priv;
- struct sym_hist *h;
-
- if (!sym || !self->ms.map)
- return 0;
-
- priv = symbol__priv(sym);
- if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
- return -ENOMEM;
-
- sym_size = sym->end - sym->start;
- offset = ip - sym->start;
-
- pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
-
- if (offset >= sym_size)
- return 0;
-
- h = priv->hist;
- h->sum++;
- h->ip[offset]++;
-
- pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start,
- self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]);
- return 0;
-}
-
-static struct objdump_line *objdump_line__new(s64 offset, char *line)
-{
- struct objdump_line *self = malloc(sizeof(*self));
-
- if (self != NULL) {
- self->offset = offset;
- self->line = line;
- }
-
- return self;
-}
-
-void objdump_line__free(struct objdump_line *self)
-{
- free(self->line);
- free(self);
-}
-
-static void objdump__add_line(struct list_head *head, struct objdump_line *line)
-{
- list_add_tail(&line->node, head);
-}
-
-struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
- struct objdump_line *pos)
-{
- list_for_each_entry_continue(pos, head, node)
- if (pos->offset >= 0)
- return pos;
-
- return NULL;
-}
-
-static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
- struct list_head *head)
-{
- struct symbol *sym = self->ms.sym;
- struct objdump_line *objdump_line;
- char *line = NULL, *tmp, *tmp2, *c;
- size_t line_len;
- s64 line_ip, offset = -1;
-
- if (getline(&line, &line_len, file) < 0)
- return -1;
-
- if (!line)
- return -1;
-
- while (line_len != 0 && isspace(line[line_len - 1]))
- line[--line_len] = '\0';
-
- c = strchr(line, '\n');
- if (c)
- *c = 0;
-
- line_ip = -1;
-
- /*
- * Strip leading spaces:
- */
- tmp = line;
- while (*tmp) {
- if (*tmp != ' ')
- break;
- tmp++;
- }
-
- if (*tmp) {
- /*
- * Parse hexa addresses followed by ':'
- */
- line_ip = strtoull(tmp, &tmp2, 16);
- if (*tmp2 != ':' || tmp == tmp2)
- line_ip = -1;
- }
-
- if (line_ip != -1) {
- u64 start = map__rip_2objdump(self->ms.map, sym->start);
- offset = line_ip - start;
- }
-
- objdump_line = objdump_line__new(offset, line);
- if (objdump_line == NULL) {
- free(line);
- return -1;
- }
- objdump__add_line(head, objdump_line);
-
- return 0;
-}
-
-int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
-{
- struct symbol *sym = self->ms.sym;
- struct map *map = self->ms.map;
- struct dso *dso = map->dso;
- char *filename = dso__build_id_filename(dso, NULL, 0);
- bool free_filename = true;
- char command[PATH_MAX * 2];
- FILE *file;
- int err = 0;
- u64 len;
-
- if (filename == NULL) {
- if (dso->has_build_id) {
- pr_err("Can't annotate %s: not enough memory\n",
- sym->name);
- return -ENOMEM;
- }
- goto fallback;
- } else if (readlink(filename, command, sizeof(command)) < 0 ||
- strstr(command, "[kernel.kallsyms]") ||
- access(filename, R_OK)) {
- free(filename);
-fallback:
- /*
- * If we don't have build-ids or the build-id file isn't in the
- * cache, or is just a kallsyms file, well, lets hope that this
- * DSO is the same as when 'perf record' ran.
- */
- filename = dso->long_name;
- free_filename = false;
- }
-
- if (dso->origin == DSO__ORIG_KERNEL) {
- if (dso->annotate_warned)
- goto out_free_filename;
- err = -ENOENT;
- dso->annotate_warned = 1;
- pr_err("Can't annotate %s: No vmlinux file was found in the "
- "path\n", sym->name);
- goto out_free_filename;
- }
-
- pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
- filename, sym->name, map->unmap_ip(map, sym->start),
- map->unmap_ip(map, sym->end));
-
- len = sym->end - sym->start;
-
- pr_debug("annotating [%p] %30s : [%p] %30s\n",
- dso, dso->long_name, sym, sym->name);
-
- snprintf(command, sizeof(command),
- "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
- map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end),
- filename, filename);
-
- pr_debug("Executing: %s\n", command);
-
- file = popen(command, "r");
- if (!file)
- goto out_free_filename;
-
- while (!feof(file))
- if (hist_entry__parse_objdump_line(self, file, head) < 0)
- break;
-
- pclose(file);
-out_free_filename:
- if (free_filename)
- free(filename);
- return err;
-}
-
-void hists__inc_nr_events(struct hists *self, u32 type)
-{
- ++self->stats.nr_events[0];
- ++self->stats.nr_events[type];
-}
-
-size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
-{
- int i;
- size_t ret = 0;
-
- for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
- if (!event__name[i])
- continue;
- ret += fprintf(fp, "%10s events: %10d\n",
- event__name[i], self->stats.nr_events[i]);
- }
-
- return ret;
-}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
deleted file mode 100644
index 83fa33a..0000000
--- a/tools/perf/util/hist.h
+++ /dev/null
@@ -1,129 +0,0 @@
-#ifndef __PERF_HIST_H
-#define __PERF_HIST_H
-
-#include <linux/types.h>
-#include "callchain.h"
-
-extern struct callchain_param callchain_param;
-
-struct hist_entry;
-struct addr_location;
-struct symbol;
-struct rb_root;
-
-struct objdump_line {
- struct list_head node;
- s64 offset;
- char *line;
-};
-
-void objdump_line__free(struct objdump_line *self);
-struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
- struct objdump_line *pos);
-
-struct sym_hist {
- u64 sum;
- u64 ip[0];
-};
-
-struct sym_ext {
- struct rb_node node;
- double percent;
- char *path;
-};
-
-struct sym_priv {
- struct sym_hist *hist;
- struct sym_ext *ext;
-};
-
-/*
- * The kernel collects the number of events it couldn't send in a stretch and
- * when possible sends this number in a PERF_RECORD_LOST event. The number of
- * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
- * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
- * the sum of all struct lost_event.lost fields reported.
- *
- * The total_period is needed because by default auto-freq is used, so
- * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
- * the total number of low level events, it is necessary to to sum all struct
- * sample_event.period and stash the result in total_period.
- */
-struct events_stats {
- u64 total_period;
- u64 total_lost;
- u32 nr_events[PERF_RECORD_HEADER_MAX];
- u32 nr_unknown_events;
-};
-
-struct hists {
- struct rb_node rb_node;
- struct rb_root entries;
- u64 nr_entries;
- struct events_stats stats;
- u64 config;
- u64 event_stream;
- u32 type;
- u32 max_sym_namelen;
-};
-
-struct hist_entry *__hists__add_entry(struct hists *self,
- struct addr_location *al,
- struct symbol *parent, u64 period);
-extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
-int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
- bool show_displacement, long displacement, FILE *fp,
- u64 total);
-int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
- struct hists *pair_hists, bool show_displacement,
- long displacement, bool color, u64 total);
-void hist_entry__free(struct hist_entry *);
-
-void hists__output_resort(struct hists *self);
-void hists__collapse_resort(struct hists *self);
-
-void hists__inc_nr_events(struct hists *self, u32 type);
-size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
-
-size_t hists__fprintf(struct hists *self, struct hists *pair,
- bool show_displacement, FILE *fp);
-
-int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
-int hist_entry__annotate(struct hist_entry *self, struct list_head *head);
-
-void hists__filter_by_dso(struct hists *self, const struct dso *dso);
-void hists__filter_by_thread(struct hists *self, const struct thread *thread);
-
-#ifdef NO_NEWT_SUPPORT
-static inline int hists__browse(struct hists *self __used,
- const char *helpline __used,
- const char *ev_name __used)
-{
- return 0;
-}
-
-static inline int hists__tui_browse_tree(struct rb_root *self __used,
- const char *help __used)
-{
- return 0;
-}
-
-static inline int hist_entry__tui_annotate(struct hist_entry *self __used)
-{
- return 0;
-}
-#define KEY_LEFT -1
-#define KEY_RIGHT -2
-#else
-#include <newt.h>
-int hists__browse(struct hists *self, const char *helpline,
- const char *ev_name);
-int hist_entry__tui_annotate(struct hist_entry *self);
-
-#define KEY_LEFT NEWT_KEY_LEFT
-#define KEY_RIGHT NEWT_KEY_RIGHT
-
-int hists__tui_browse_tree(struct rb_root *self, const char *help);
-#endif
-#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index 63f89e0..dffe075 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -16,10 +16,10 @@
#include <sys/ttydefaults.h>
#include "cache.h"
-#include "hist.h"
+#include <perf/hist.h>
#include "pstack.h"
#include <perf/session.h>
-#include "sort.h"
+#include <perf/sort.h>
#include <perf/symbol.h>
#if SLANG_VERSION < 20104
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index d3e911b..08b591f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -34,14 +34,14 @@
#undef _GNU_SOURCE
#include <lk/util.h>
-#include "event.h"
+#include <perf/event.h>
#include "string.h"
#include <lk/strlist.h>
#include <lk/debug.h>
#include "cache.h"
#include <lk/color.h>
#include <perf/symbol.h>
-#include "thread.h"
+#include <perf/thread.h>
#include <lk/debugfs.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 789c583..de2c598 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -34,7 +34,7 @@
#include <dwarf-regs.h>
#include "string.h"
-#include "event.h"
+#include <perf/event.h>
#include <lk/debug.h>
#include <lk/util.h>
#include <perf/symbol.h>
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
deleted file mode 100644
index c27b4b0..0000000
--- a/tools/perf/util/sort.c
+++ /dev/null
@@ -1,346 +0,0 @@
-#include "sort.h"
-
-regex_t parent_regex;
-const char default_parent_pattern[] = "^sys_|^do_page_fault";
-const char *parent_pattern = default_parent_pattern;
-const char default_sort_order[] = "comm,dso,symbol";
-const char *sort_order = default_sort_order;
-int sort__need_collapse = 0;
-int sort__has_parent = 0;
-
-enum sort_type sort__first_dimension;
-
-unsigned int dsos__col_width;
-unsigned int comms__col_width;
-unsigned int threads__col_width;
-unsigned int cpus__col_width;
-static unsigned int parent_symbol__col_width;
-char * field_sep;
-
-LIST_HEAD(hist_entry__sort_list);
-
-static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width);
-
-struct sort_entry sort_thread = {
- .se_header = "Command: Pid",
- .se_cmp = sort__thread_cmp,
- .se_snprintf = hist_entry__thread_snprintf,
- .se_width = &threads__col_width,
-};
-
-struct sort_entry sort_comm = {
- .se_header = "Command",
- .se_cmp = sort__comm_cmp,
- .se_collapse = sort__comm_collapse,
- .se_snprintf = hist_entry__comm_snprintf,
- .se_width = &comms__col_width,
-};
-
-struct sort_entry sort_dso = {
- .se_header = "Shared Object",
- .se_cmp = sort__dso_cmp,
- .se_snprintf = hist_entry__dso_snprintf,
- .se_width = &dsos__col_width,
-};
-
-struct sort_entry sort_sym = {
- .se_header = "Symbol",
- .se_cmp = sort__sym_cmp,
- .se_snprintf = hist_entry__sym_snprintf,
-};
-
-struct sort_entry sort_parent = {
- .se_header = "Parent symbol",
- .se_cmp = sort__parent_cmp,
- .se_snprintf = hist_entry__parent_snprintf,
- .se_width = &parent_symbol__col_width,
-};
-
-struct sort_entry sort_cpu = {
- .se_header = "CPU",
- .se_cmp = sort__cpu_cmp,
- .se_snprintf = hist_entry__cpu_snprintf,
- .se_width = &cpus__col_width,
-};
-
-struct sort_dimension {
- const char *name;
- struct sort_entry *entry;
- int taken;
-};
-
-static struct sort_dimension sort_dimensions[] = {
- { .name = "pid", .entry = &sort_thread, },
- { .name = "comm", .entry = &sort_comm, },
- { .name = "dso", .entry = &sort_dso, },
- { .name = "symbol", .entry = &sort_sym, },
- { .name = "parent", .entry = &sort_parent, },
- { .name = "cpu", .entry = &sort_cpu, },
-};
-
-int64_t cmp_null(void *l, void *r)
-{
- if (!l && !r)
- return 0;
- else if (!l)
- return -1;
- else
- return 1;
-}
-
-/* --sort pid */
-
-int64_t
-sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- return right->thread->pid - left->thread->pid;
-}
-
-static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
-{
- int n;
- va_list ap;
-
- va_start(ap, fmt);
- n = vsnprintf(bf, size, fmt, ap);
- if (field_sep && n > 0) {
- char *sep = bf;
-
- while (1) {
- sep = strchr(sep, *field_sep);
- if (sep == NULL)
- break;
- *sep = '.';
- }
- }
- va_end(ap);
- return n;
-}
-
-static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width)
-{
- return repsep_snprintf(bf, size, "%*s:%5d", width,
- self->thread->comm ?: "", self->thread->pid);
-}
-
-static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width)
-{
- return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
-}
-
-/* --sort dso */
-
-int64_t
-sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
- struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
- const char *dso_name_l, *dso_name_r;
-
- if (!dso_l || !dso_r)
- return cmp_null(dso_l, dso_r);
-
- if (verbose) {
- dso_name_l = dso_l->long_name;
- dso_name_r = dso_r->long_name;
- } else {
- dso_name_l = dso_l->short_name;
- dso_name_r = dso_r->short_name;
- }
-
- return strcmp(dso_name_l, dso_name_r);
-}
-
-static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width)
-{
- if (self->ms.map && self->ms.map->dso) {
- const char *dso_name = !verbose ? self->ms.map->dso->short_name :
- self->ms.map->dso->long_name;
- return repsep_snprintf(bf, size, "%-*s", width, dso_name);
- }
-
- return repsep_snprintf(bf, size, "%*Lx", width, self->ip);
-}
-
-/* --sort symbol */
-
-int64_t
-sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- u64 ip_l, ip_r;
-
- if (left->ms.sym == right->ms.sym)
- return 0;
-
- ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
- ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
-
- return (int64_t)(ip_r - ip_l);
-}
-
-static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
-{
- size_t ret = 0;
-
- if (verbose) {
- char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
- ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o);
- }
-
- ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
- if (self->ms.sym)
- ret += repsep_snprintf(bf + ret, size - ret, "%s",
- self->ms.sym->name);
- else
- ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip);
-
- return ret;
-}
-
-/* --sort comm */
-
-int64_t
-sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- return right->thread->pid - left->thread->pid;
-}
-
-int64_t
-sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
-{
- char *comm_l = left->thread->comm;
- char *comm_r = right->thread->comm;
-
- if (!comm_l || !comm_r)
- return cmp_null(comm_l, comm_r);
-
- return strcmp(comm_l, comm_r);
-}
-
-/* --sort parent */
-
-int64_t
-sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- struct symbol *sym_l = left->parent;
- struct symbol *sym_r = right->parent;
-
- if (!sym_l || !sym_r)
- return cmp_null(sym_l, sym_r);
-
- return strcmp(sym_l->name, sym_r->name);
-}
-
-static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width)
-{
- return repsep_snprintf(bf, size, "%-*s", width,
- self->parent ? self->parent->name : "[other]");
-}
-
-/* --sort cpu */
-
-int64_t
-sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
-{
- return right->cpu - left->cpu;
-}
-
-static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width)
-{
- return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
-}
-
-int sort_dimension__add(const char *tok)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
- struct sort_dimension *sd = &sort_dimensions[i];
-
- if (sd->taken)
- continue;
-
- if (strncasecmp(tok, sd->name, strlen(tok)))
- continue;
-
- if (sd->entry->se_collapse)
- sort__need_collapse = 1;
-
- if (sd->entry == &sort_parent) {
- int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
- if (ret) {
- char err[BUFSIZ];
-
- regerror(ret, &parent_regex, err, sizeof(err));
- pr_err("Invalid regex: %s\n%s", parent_pattern, err);
- return -EINVAL;
- }
- sort__has_parent = 1;
- }
-
- if (list_empty(&hist_entry__sort_list)) {
- if (!strcmp(sd->name, "pid"))
- sort__first_dimension = SORT_PID;
- else if (!strcmp(sd->name, "comm"))
- sort__first_dimension = SORT_COMM;
- else if (!strcmp(sd->name, "dso"))
- sort__first_dimension = SORT_DSO;
- else if (!strcmp(sd->name, "symbol"))
- sort__first_dimension = SORT_SYM;
- else if (!strcmp(sd->name, "parent"))
- sort__first_dimension = SORT_PARENT;
- else if (!strcmp(sd->name, "cpu"))
- sort__first_dimension = SORT_CPU;
- }
-
- list_add_tail(&sd->entry->list, &hist_entry__sort_list);
- sd->taken = 1;
-
- return 0;
- }
-
- return -ESRCH;
-}
-
-void setup_sorting(const char * const usagestr[], const struct option *opts)
-{
- char *tmp, *tok, *str = strdup(sort_order);
-
- for (tok = strtok_r(str, ", ", &tmp);
- tok; tok = strtok_r(NULL, ", ", &tmp)) {
- if (sort_dimension__add(tok) < 0) {
- error("Unknown --sort key: `%s'", tok);
- usage_with_options(usagestr, opts);
- }
- }
-
- free(str);
-}
-
-void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
- const char *list_name, FILE *fp)
-{
- if (list && strlist__nr_entries(list) == 1) {
- if (fp != NULL)
- fprintf(fp, "# %s: %s\n", list_name,
- strlist__entry(list, 0)->s);
- self->elide = true;
- }
-}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
deleted file mode 100644
index ccc3045..0000000
--- a/tools/perf/util/sort.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef __PERF_SORT_H
-#define __PERF_SORT_H
-#include "../builtin.h"
-
-#include <lk/util.h>
-
-#include <lk/color.h>
-#include <linux/list.h>
-#include "cache.h"
-#include <linux/rbtree.h>
-#include <perf/symbol.h>
-#include "string.h"
-#include "callchain.h"
-#include <lk/strlist.h>
-#include "values.h"
-
-#include "../perf.h"
-#include <lk/debug.h>
-#include <perf/header.h>
-
-#include "parse-options.h"
-#include <perf/parse-events.h>
-
-#include "thread.h"
-#include "sort.h"
-
-extern regex_t parent_regex;
-extern const char *sort_order;
-extern const char default_parent_pattern[];
-extern const char *parent_pattern;
-extern const char default_sort_order[];
-extern int sort__need_collapse;
-extern int sort__has_parent;
-extern char *field_sep;
-extern struct sort_entry sort_comm;
-extern struct sort_entry sort_dso;
-extern struct sort_entry sort_sym;
-extern struct sort_entry sort_parent;
-extern unsigned int dsos__col_width;
-extern unsigned int comms__col_width;
-extern unsigned int threads__col_width;
-extern unsigned int cpus__col_width;
-extern enum sort_type sort__first_dimension;
-
-struct hist_entry {
- struct rb_node rb_node;
- u64 period;
- u64 period_sys;
- u64 period_us;
- u64 period_guest_sys;
- u64 period_guest_us;
- struct map_symbol ms;
- struct thread *thread;
- u64 ip;
- s32 cpu;
- u32 nr_events;
- char level;
- u8 filtered;
- struct symbol *parent;
- union {
- unsigned long position;
- struct hist_entry *pair;
- struct rb_root sorted_chain;
- };
- struct callchain_node callchain[0];
-};
-
-enum sort_type {
- SORT_PID,
- SORT_COMM,
- SORT_DSO,
- SORT_SYM,
- SORT_PARENT,
- SORT_CPU,
-};
-
-/*
- * configurable sorting bits
- */
-
-struct sort_entry {
- struct list_head list;
-
- const char *se_header;
-
- int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
- int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
- int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
- unsigned int width);
- unsigned int *se_width;
- bool elide;
-};
-
-extern struct sort_entry sort_thread;
-extern struct list_head hist_entry__sort_list;
-
-void setup_sorting(const char * const usagestr[], const struct option *opts);
-
-extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
-extern int64_t cmp_null(void *, void *);
-extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
-int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
-extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
-extern int sort_dimension__add(const char *);
-void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
- const char *list_name, FILE *fp);
-
-#endif /* __PERF_SORT_H */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
deleted file mode 100644
index c5e5207..0000000
--- a/tools/perf/util/thread.c
+++ /dev/null
@@ -1,164 +0,0 @@
-#include "../perf.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <perf/session.h>
-#include "thread.h"
-#include <lk/util.h>
-#include <lk/debug.h>
-
-int find_all_tid(int pid, pid_t ** all_tid)
-{
- char name[256];
- int items;
- struct dirent **namelist = NULL;
- int ret = 0;
- int i;
-
- sprintf(name, "/proc/%d/task", pid);
- items = scandir(name, &namelist, NULL, NULL);
- if (items <= 0)
- return -ENOENT;
- *all_tid = malloc(sizeof(pid_t) * items);
- if (!*all_tid) {
- ret = -ENOMEM;
- goto failure;
- }
-
- for (i = 0; i < items; i++)
- (*all_tid)[i] = atoi(namelist[i]->d_name);
-
- ret = items;
-
-failure:
- for (i=0; i<items; i++)
- free(namelist[i]);
- free(namelist);
-
- return ret;
-}
-
-static struct thread *thread__new(pid_t pid)
-{
- struct thread *self = zalloc(sizeof(*self));
-
- if (self != NULL) {
- map_groups__init(&self->mg);
- self->pid = pid;
- self->comm = malloc(32);
- if (self->comm)
- snprintf(self->comm, 32, ":%d", self->pid);
- }
-
- return self;
-}
-
-int thread__set_comm(struct thread *self, const char *comm)
-{
- int err;
-
- if (self->comm)
- free(self->comm);
- self->comm = strdup(comm);
- err = self->comm == NULL ? -ENOMEM : 0;
- if (!err) {
- self->comm_set = true;
- map_groups__flush(&self->mg);
- }
- return err;
-}
-
-int thread__comm_len(struct thread *self)
-{
- if (!self->comm_len) {
- if (!self->comm)
- return 0;
- self->comm_len = strlen(self->comm);
- }
-
- return self->comm_len;
-}
-
-static size_t thread__fprintf(struct thread *self, FILE *fp)
-{
- return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
- map_groups__fprintf(&self->mg, verbose, fp);
-}
-
-struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
-{
- struct rb_node **p = &self->threads.rb_node;
- struct rb_node *parent = NULL;
- struct thread *th;
-
- /*
- * Font-end cache - PID lookups come in blocks,
- * so most of the time we dont have to look up
- * the full rbtree:
- */
- if (self->last_match && self->last_match->pid == pid)
- return self->last_match;
-
- while (*p != NULL) {
- parent = *p;
- th = rb_entry(parent, struct thread, rb_node);
-
- if (th->pid == pid) {
- self->last_match = th;
- return th;
- }
-
- if (pid < th->pid)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- th = thread__new(pid);
- if (th != NULL) {
- rb_link_node(&th->rb_node, parent, p);
- rb_insert_color(&th->rb_node, &self->threads);
- self->last_match = th;
- }
-
- return th;
-}
-
-void thread__insert_map(struct thread *self, struct map *map)
-{
- map_groups__fixup_overlappings(&self->mg, map, verbose, stderr);
- map_groups__insert(&self->mg, map);
-}
-
-int thread__fork(struct thread *self, struct thread *parent)
-{
- int i;
-
- if (parent->comm_set) {
- if (self->comm)
- free(self->comm);
- self->comm = strdup(parent->comm);
- if (!self->comm)
- return -ENOMEM;
- self->comm_set = true;
- }
-
- for (i = 0; i < MAP__NR_TYPES; ++i)
- if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
- return -ENOMEM;
- return 0;
-}
-
-size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
-{
- size_t ret = 0;
- struct rb_node *nd;
-
- for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
- struct thread *pos = rb_entry(nd, struct thread, rb_node);
-
- ret += thread__fprintf(pos, fp);
- }
-
- return ret;
-}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
deleted file mode 100644
index 93baaea..0000000
--- a/tools/perf/util/thread.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef __PERF_THREAD_H
-#define __PERF_THREAD_H
-
-#include <linux/rbtree.h>
-#include <unistd.h>
-#include <perf/symbol.h>
-
-struct thread {
- struct rb_node rb_node;
- struct map_groups mg;
- pid_t pid;
- char shortname[3];
- bool comm_set;
- char *comm;
- int comm_len;
-};
-
-struct perf_session;
-
-int find_all_tid(int pid, pid_t ** all_tid);
-int thread__set_comm(struct thread *self, const char *comm);
-int thread__comm_len(struct thread *self);
-struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
-void thread__insert_map(struct thread *self, struct map *map);
-int thread__fork(struct thread *self, struct thread *parent);
-size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
-
-static inline struct map *thread__find_map(struct thread *self,
- enum map_type type, u64 addr)
-{
- return self ? map_groups__find(&self->mg, type, addr) : NULL;
-}
-
-void thread__find_addr_map(struct thread *self,
- struct perf_session *session, u8 cpumode,
- enum map_type type, pid_t pid, u64 addr,
- struct addr_location *al);
-
-void thread__find_addr_location(struct thread *self,
- struct perf_session *session, u8 cpumode,
- enum map_type type, pid_t pid, u64 addr,
- struct addr_location *al,
- symbol_filter_t filter);
-#endif /* __PERF_THREAD_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 15/21] perf: Export trace event utils
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (13 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 14/21] perf: Carve out perf bits for general usage, p3 Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-06 22:18 ` Steven Rostedt
2010-07-01 15:55 ` [PATCH 16/21] perf: Add a common misc.c compilation unit Borislav Petkov
` (5 subsequent siblings)
20 siblings, 1 reply; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Those are indirectly needed by parse-events.c too.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 3 +
tools/lib/trace/trace-event-info.c | 563 ++++++
tools/lib/trace/trace-event-parse.c | 3233 +++++++++++++++++++++++++++++++++++
tools/lib/trace/trace-event-read.c | 539 ++++++
tools/perf/Makefile | 3 -
tools/perf/util/trace-event-info.c | 563 ------
tools/perf/util/trace-event-parse.c | 3233 -----------------------------------
tools/perf/util/trace-event-read.c | 539 ------
8 files changed, 4338 insertions(+), 4338 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
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
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index cabef46..795fa3a 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -49,6 +49,9 @@ LIB_OBJS += $(OUTPUT)perf/hist.o
LIB_OBJS += $(OUTPUT)perf/thread.o
LIB_OBJS += $(OUTPUT)perf/sort.o
LIB_OBJS += $(OUTPUT)perf/event.o
+LIB_OBJS += $(OUTPUT)trace/trace-event-read.o
+LIB_OBJS += $(OUTPUT)trace/trace-event-info.o
+LIB_OBJS += $(OUTPUT)trace/trace-event-parse.o
LIBFILE = lklib.a
diff --git a/tools/lib/trace/trace-event-info.c b/tools/lib/trace/trace-event-info.c
new file mode 100644
index 0000000..e1ad23f
--- /dev/null
+++ b/tools/lib/trace/trace-event-info.c
@@ -0,0 +1,563 @@
+/*
+ * 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 <linux/kernel.h>
+
+#include <perf.h>
+#include <util/trace-event.h>
+#include <lk/debugfs.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;
+
+ 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..d76c350
--- /dev/null
+++ b/tools/lib/trace/trace-event-parse.c
@@ -0,0 +1,3233 @@
+/*
+ * 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 <lk/util.h>
+#include <util/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/lib/trace/trace-event-read.c b/tools/lib/trace/trace-event-read.c
new file mode 100644
index 0000000..d7d8ceb
--- /dev/null
+++ b/tools/lib/trace/trace-event-read.c
@@ -0,0 +1,539 @@
+/*
+ * 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 <util/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(data);
+}
+
+static unsigned long long read8(void)
+{
+ unsigned long long data;
+
+ read_or_die(&data, 8);
+ return __data2host8(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(ptr);
+ ptr += 8;
+ switch (header_page_size_size) {
+ case 4:
+ cpu_data[cpu].page_size = data2host4(ptr);
+ ptr += 4;
+ break;
+ case 8:
+ cpu_data[cpu].page_size = data2host8(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(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(ptr);
+ ptr += 4;
+ length *= 4;
+ ptr += length;
+ goto read_again;
+
+ case RINGBUF_TYPE_TIME_EXTEND:
+ extend = data2host4(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(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_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) {
+ print_funcs();
+ return size;
+ }
+ if (show_printk) {
+ print_printk();
+ return size;
+ }
+
+ return size;
+}
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 001831e..590c8f6 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -383,9 +383,6 @@ LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/pager.o
LIB_OBJS += $(OUTPUT)util/callchain.o
LIB_OBJS += $(OUTPUT)util/values.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/probe-event.o
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
deleted file mode 100644
index 0918a63..0000000
--- a/tools/perf/util/trace-event-info.c
+++ /dev/null
@@ -1,563 +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 <linux/kernel.h>
-
-#include "../perf.h"
-#include "trace-event.h"
-#include <lk/debugfs.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;
-
- 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 9a46fa1..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 <lk/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-read.c b/tools/perf/util/trace-event-read.c
deleted file mode 100644
index 2583227..0000000
--- a/tools/perf/util/trace-event-read.c
+++ /dev/null
@@ -1,539 +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(data);
-}
-
-static unsigned long long read8(void)
-{
- unsigned long long data;
-
- read_or_die(&data, 8);
- return __data2host8(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(ptr);
- ptr += 8;
- switch (header_page_size_size) {
- case 4:
- cpu_data[cpu].page_size = data2host4(ptr);
- ptr += 4;
- break;
- case 8:
- cpu_data[cpu].page_size = data2host8(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(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(ptr);
- ptr += 4;
- length *= 4;
- ptr += length;
- goto read_again;
-
- case RINGBUF_TYPE_TIME_EXTEND:
- extend = data2host4(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(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_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) {
- print_funcs();
- return size;
- }
- if (show_printk) {
- print_printk();
- return size;
- }
-
- return size;
-}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH 15/21] perf: Export trace event utils
2010-07-01 15:55 ` [PATCH 15/21] perf: Export trace event utils Borislav Petkov
@ 2010-07-06 22:18 ` Steven Rostedt
2010-07-07 7:57 ` Borislav Petkov
0 siblings, 1 reply; 24+ messages in thread
From: Steven Rostedt @ 2010-07-06 22:18 UTC (permalink / raw)
To: Borislav Petkov; +Cc: linux-kernel
Hi Borislav,
I'm actually working on making a libparsevent.a that does some of what
you are doing.
The idea is, if I can separate out this code from perf, I can then bring
it up to speed with what I have in trace-cmd.
-- Steve
On Thu, 2010-07-01 at 17:55 +0200, Borislav Petkov wrote:
> From: Borislav Petkov <borislav.petkov@amd.com>
>
> Those are indirectly needed by parse-events.c too.
>
> Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
> ---
> tools/lib/Makefile | 3 +
> tools/lib/trace/trace-event-info.c | 563 ++++++
> tools/lib/trace/trace-event-parse.c | 3233 +++++++++++++++++++++++++++++++++++
> tools/lib/trace/trace-event-read.c | 539 ++++++
> tools/perf/Makefile | 3 -
> tools/perf/util/trace-event-info.c | 563 ------
> tools/perf/util/trace-event-parse.c | 3233 -----------------------------------
> tools/perf/util/trace-event-read.c | 539 ------
> 8 files changed, 4338 insertions(+), 4338 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
> 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
>
> diff --git a/tools/lib/Makefile b/tools/lib/Makefile
> index cabef46..795fa3a 100644
> --- a/tools/lib/Makefile
> +++ b/tools/lib/Makefile
> @@ -49,6 +49,9 @@ LIB_OBJS += $(OUTPUT)perf/hist.o
> LIB_OBJS += $(OUTPUT)perf/thread.o
> LIB_OBJS += $(OUTPUT)perf/sort.o
> LIB_OBJS += $(OUTPUT)perf/event.o
> +LIB_OBJS += $(OUTPUT)trace/trace-event-read.o
> +LIB_OBJS += $(OUTPUT)trace/trace-event-info.o
> +LIB_OBJS += $(OUTPUT)trace/trace-event-parse.o
>
> LIBFILE = lklib.a
>
> diff --git a/tools/lib/trace/trace-event-info.c b/tools/lib/trace/trace-event-info.c
> new file mode 100644
> index 0000000..e1ad23f
> --- /dev/null
> +++ b/tools/lib/trace/trace-event-info.c
> @@ -0,0 +1,563 @@
> +/*
> + * 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 <linux/kernel.h>
> +
> +#include <perf.h>
> +#include <util/trace-event.h>
> +#include <lk/debugfs.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;
> +
> + 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..d76c350
> --- /dev/null
> +++ b/tools/lib/trace/trace-event-parse.c
> @@ -0,0 +1,3233 @@
> +/*
> + * 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 <lk/util.h>
> +#include <util/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/lib/trace/trace-event-read.c b/tools/lib/trace/trace-event-read.c
> new file mode 100644
> index 0000000..d7d8ceb
> --- /dev/null
> +++ b/tools/lib/trace/trace-event-read.c
> @@ -0,0 +1,539 @@
> +/*
> + * 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 <util/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(data);
> +}
> +
> +static unsigned long long read8(void)
> +{
> + unsigned long long data;
> +
> + read_or_die(&data, 8);
> + return __data2host8(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(ptr);
> + ptr += 8;
> + switch (header_page_size_size) {
> + case 4:
> + cpu_data[cpu].page_size = data2host4(ptr);
> + ptr += 4;
> + break;
> + case 8:
> + cpu_data[cpu].page_size = data2host8(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(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(ptr);
> + ptr += 4;
> + length *= 4;
> + ptr += length;
> + goto read_again;
> +
> + case RINGBUF_TYPE_TIME_EXTEND:
> + extend = data2host4(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(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_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) {
> + print_funcs();
> + return size;
> + }
> + if (show_printk) {
> + print_printk();
> + return size;
> + }
> +
> + return size;
> +}
> diff --git a/tools/perf/Makefile b/tools/perf/Makefile
> index 001831e..590c8f6 100644
> --- a/tools/perf/Makefile
> +++ b/tools/perf/Makefile
> @@ -383,9 +383,6 @@ LIB_OBJS += $(OUTPUT)util/sigchain.o
> LIB_OBJS += $(OUTPUT)util/pager.o
> LIB_OBJS += $(OUTPUT)util/callchain.o
> LIB_OBJS += $(OUTPUT)util/values.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/probe-event.o
> diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
> deleted file mode 100644
> index 0918a63..0000000
> --- a/tools/perf/util/trace-event-info.c
> +++ /dev/null
> @@ -1,563 +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 <linux/kernel.h>
> -
> -#include "../perf.h"
> -#include "trace-event.h"
> -#include <lk/debugfs.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;
> -
> - 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 9a46fa1..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 <lk/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-read.c b/tools/perf/util/trace-event-read.c
> deleted file mode 100644
> index 2583227..0000000
> --- a/tools/perf/util/trace-event-read.c
> +++ /dev/null
> @@ -1,539 +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(data);
> -}
> -
> -static unsigned long long read8(void)
> -{
> - unsigned long long data;
> -
> - read_or_die(&data, 8);
> - return __data2host8(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(ptr);
> - ptr += 8;
> - switch (header_page_size_size) {
> - case 4:
> - cpu_data[cpu].page_size = data2host4(ptr);
> - ptr += 4;
> - break;
> - case 8:
> - cpu_data[cpu].page_size = data2host8(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(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(ptr);
> - ptr += 4;
> - length *= 4;
> - ptr += length;
> - goto read_again;
> -
> - case RINGBUF_TYPE_TIME_EXTEND:
> - extend = data2host4(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(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_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) {
> - print_funcs();
> - return size;
> - }
> - if (show_printk) {
> - print_printk();
> - return size;
> - }
> -
> - return size;
> -}
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH 15/21] perf: Export trace event utils
2010-07-06 22:18 ` Steven Rostedt
@ 2010-07-07 7:57 ` Borislav Petkov
0 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-07 7:57 UTC (permalink / raw)
To: Steven Rostedt; +Cc: linux-kernel@vger.kernel.org
From: Steven Rostedt <rostedt@goodmis.org>
Date: Tue, Jul 06, 2010 at 06:18:43PM -0400
Hi Steven,
> I'm actually working on making a libparsevent.a that does some of what
> you are doing.
>
> The idea is, if I can separate out this code from perf, I can then bring
> it up to speed with what I have in trace-cmd.
Ok this is good. I moved those to a generic place only because
perf/util/parse-events.c pulled them out as a build dependency but all
the trace events parsing code should be a library-like since it can very
much be reused by a lot of other tools.
We should sync our stuff then, after you've moved those to
tools/lib/trace/ or somewhere similar, and I can rebase my stuff on top. Or
maybe you have a better idea, please keep me posted so that we don't step on
each other's toes.
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] 24+ messages in thread
* [PATCH 16/21] perf: Add a common misc.c compilation unit
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (14 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 15/21] perf: Export trace event utils Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:55 ` [PATCH 17/21] perf: Carve out mmap helpers for general use Borislav Petkov
` (4 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
This is a temporary unit for sharing code between perf and the library.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/Makefile | 2 +
tools/lib/lk/color.c | 16 ------------
tools/lib/lk/color.h | 5 +---
tools/lib/lk/debug.c | 6 +----
tools/lib/lk/debug.h | 2 -
tools/lib/perf/misc.c | 54 +++++++++++++++++++++++++++++++++++++++++
tools/lib/perf/misc.h | 11 ++++++++
tools/perf/builtin-annotate.c | 4 +++
tools/perf/builtin-kvm.c | 4 +--
tools/perf/builtin-record.c | 11 ++++----
tools/perf/builtin-report.c | 1 +
tools/perf/perf.c | 1 +
tools/perf/perf.h | 3 --
tools/perf/util/newt.c | 1 +
14 files changed, 83 insertions(+), 38 deletions(-)
create mode 100644 tools/lib/perf/misc.c
create mode 100644 tools/lib/perf/misc.h
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 795fa3a..780c4fc 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -23,6 +23,7 @@ LIB_H += perf/hist.h
LIB_H += perf/thread.h
LIB_H += perf/sort.h
LIB_H += perf/event.h
+LIB_H += perf/misc.h
LIB_OBJS += $(OUTPUT)lk/bitmap.o
LIB_OBJS += $(OUTPUT)lk/cpumap.o
@@ -49,6 +50,7 @@ LIB_OBJS += $(OUTPUT)perf/hist.o
LIB_OBJS += $(OUTPUT)perf/thread.o
LIB_OBJS += $(OUTPUT)perf/sort.o
LIB_OBJS += $(OUTPUT)perf/event.o
+LIB_OBJS += $(OUTPUT)perf/misc.o
LIB_OBJS += $(OUTPUT)trace/trace-event-read.o
LIB_OBJS += $(OUTPUT)trace/trace-event-info.o
LIB_OBJS += $(OUTPUT)trace/trace-event-parse.o
diff --git a/tools/lib/lk/color.c b/tools/lib/lk/color.c
index 93d6381..b3d067b 100644
--- a/tools/lib/lk/color.c
+++ b/tools/lib/lk/color.c
@@ -6,22 +6,6 @@
*/
static int use_color_default = -1;
-/*
- * This is going into tools/perf/perf.c next
- */
-int spawned_pager, pager_use_color = 1;
-
-int pager_in_use(void)
-{
- const char *env;
-
- if (spawned_pager)
- return 1;
-
- env = getenv("PERF_PAGER_IN_USE");
- return env ? lk_config_bool("PERF_PAGER_IN_USE", env) : 0;
-}
-
static int parse_color(const char *name, int len)
{
static const char * const color_names[] = {
diff --git a/tools/lib/lk/color.h b/tools/lib/lk/color.h
index c962e1d..03927fc 100644
--- a/tools/lib/lk/color.h
+++ b/tools/lib/lk/color.h
@@ -8,6 +8,7 @@
#include <stdarg.h>
#include "util.h"
+#include <perf/misc.h>
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
#define COLOR_MAXLEN 24
@@ -26,10 +27,6 @@
#define MIN_GREEN 0.5
#define MIN_RED 5.0
-extern int spawned_pager, pager_use_color;
-
-extern int pager_in_use(void);
-
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,
diff --git a/tools/lib/lk/debug.c b/tools/lib/lk/debug.c
index 73edbb8..b183c4d 100644
--- a/tools/lib/lk/debug.c
+++ b/tools/lib/lk/debug.c
@@ -8,11 +8,7 @@
#include "debug.h"
#include <lk/util.h>
#include <lk/color.h>
-
-/*
- * will move to tools/perf/perf.c
- */
-int use_browser = -1;
+#include <perf/misc.h>
int verbose = 0;
bool dump_trace = false;
diff --git a/tools/lib/lk/debug.h b/tools/lib/lk/debug.h
index 44fdfab..6badf99 100644
--- a/tools/lib/lk/debug.h
+++ b/tools/lib/lk/debug.h
@@ -8,8 +8,6 @@
extern int verbose;
extern bool dump_trace;
-extern int use_browser;
-
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(event_t *event);
diff --git a/tools/lib/perf/misc.c b/tools/lib/perf/misc.c
new file mode 100644
index 0000000..66506cf
--- /dev/null
+++ b/tools/lib/perf/misc.c
@@ -0,0 +1,54 @@
+/*
+ * This contains miscellaneous bits and pieces which are shared by the
+ * lk library and the rest of the tools. XXX: the aim should be to
+ * remove this unit later making the library independent and the tools
+ * call well-defined interfaces only.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <lk/util.h>
+#include <lk/config.h>
+#include <linux/compiler.h>
+#include <util/parse-options.h>
+
+#include "misc.h"
+
+int spawned_pager, pager_use_color = 1;
+int use_browser = -1;
+
+bool perf_host = 1;
+bool perf_guest;
+
+int pager_in_use(void)
+{
+ const char *env;
+
+ if (spawned_pager)
+ return 1;
+
+ env = getenv("PERF_PAGER_IN_USE");
+ return env ? lk_config_bool("PERF_PAGER_IN_USE", env) : 0;
+}
+
+/*
+ * overridden by tools/perf/util/parse-options.c:usage_with_options()
+ */
+void __weak NORETURN usage_with_options(const char * const *usagestr,
+ const struct option *opts __used)
+{
+ fprintf(stderr, "\n usage: %s\n", *usagestr++);
+ while (*usagestr && **usagestr)
+ fprintf(stderr, " or: %s\n", *usagestr++);
+ while (*usagestr) {
+ fprintf(stderr, "%s%s\n",
+ **usagestr ? " " : "",
+ *usagestr);
+ usagestr++;
+ }
+
+ if (opts->type != OPTION_GROUP)
+ fputc('\n', stderr);
+
+ exit(129);
+}
diff --git a/tools/lib/perf/misc.h b/tools/lib/perf/misc.h
new file mode 100644
index 0000000..2073b8c
--- /dev/null
+++ b/tools/lib/perf/misc.h
@@ -0,0 +1,11 @@
+#ifndef __PERF_MISC_H
+#define __PERF_MISC_H
+
+#include <stdbool.h>
+
+extern int spawned_pager, pager_use_color;
+extern int use_browser;
+extern int pager_in_use(void);
+extern bool perf_host, perf_guest;
+
+#endif /* __PERF_MISC_H */
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 29753d1..87e78ae 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -24,6 +24,10 @@
#include <perf/thread.h>
#include <perf/sort.h>
#include <perf/hist.h>
+#include <perf/misc.h>
+#include <perf/thread.h>
+#include <perf/sort.h>
+#include <perf/hist.h>
#include <perf/session.h>
static char const *input_name = "perf.data";
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 10a8c4d..8e92448 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -3,6 +3,7 @@
#include <lk/util.h>
#include "util/cache.h"
+#include <perf/misc.h>
#include <perf/symbol.h>
#include <perf/thread.h>
#include <perf/header.h>
@@ -22,9 +23,6 @@
static const char *file_name;
static char name_buffer[256];
-bool perf_host = 1;
-bool perf_guest;
-
static const char * const kvm_usage[] = {
"perf kvm [<options>] {top|record|report|diff|buildid-list}",
NULL
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6072b83..241c334 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -11,17 +11,18 @@
#include "perf.h"
-#include <perf/build-id.h>
#include <lk/util.h>
-#include "util/parse-options.h"
+#include <lk/cpumap.h>
+#include <lk/debug.h>
+#include <perf/misc.h>
#include <perf/parse-events.h>
-
+#include <perf/build-id.h>
#include <perf/header.h>
#include <perf/event.h>
-#include <lk/debug.h>
#include <perf/session.h>
#include <perf/symbol.h>
-#include <lk/cpumap.h>
+
+#include "util/parse-options.h"
#include <unistd.h>
#include <sched.h>
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 0803898..9eccc66 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -25,6 +25,7 @@
#include "util/parse-options.h"
#include <perf/parse-events.h>
+#include <perf/misc.h>
#include <perf/thread.h>
#include <perf/sort.h>
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 4268ed4..9a1b725 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -15,6 +15,7 @@
#include <perf/build-id.h>
#include "util/run-command.h"
#include <perf/parse-events.h>
+#include <perf/misc.h>
#include <lk/debugfs.h>
#include <lk/config.h>
#include <lk/debug.h>
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 2344078..86b7ea1 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -131,7 +131,4 @@ struct ip_callchain {
u64 nr;
u64 ips[0];
};
-
-extern bool perf_host, perf_guest;
-
#endif
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index dffe075..3a8c453 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -21,6 +21,7 @@
#include <perf/session.h>
#include <perf/sort.h>
#include <perf/symbol.h>
+#include <perf/misc.h>
#if SLANG_VERSION < 20104
#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 17/21] perf: Carve out mmap helpers for general use
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (15 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 16/21] perf: Add a common misc.c compilation unit Borislav Petkov
@ 2010-07-01 15:55 ` Borislav Petkov
2010-07-01 15:56 ` [PATCH 18/21] perf: Split build-id.c Borislav Petkov
` (3 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:55 UTC (permalink / raw)
To: linux-kernel
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/lib/Makefile | 2 +
tools/lib/perf/mmap.c | 97 +++++++++++++++++++++++++++++++++++++
tools/lib/perf/mmap.h | 15 ++++++
tools/perf/builtin-record.c | 113 +++++--------------------------------------
tools/perf/builtin-top.c | 21 +-------
5 files changed, 129 insertions(+), 119 deletions(-)
create mode 100644 tools/lib/perf/mmap.c
create mode 100644 tools/lib/perf/mmap.h
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 780c4fc..6f7102b 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -13,6 +13,7 @@ LIB_H += lk/strbuf.h
LIB_H += lk/color.h
LIB_H += lk/debug.h
LIB_H += lk/strlist.h
+LIB_H += perf/mmap.h
LIB_H += perf/parse-events.h
LIB_H += perf/header.h
LIB_H += perf/symbol.h
@@ -40,6 +41,7 @@ LIB_OBJS += $(OUTPUT)lk/debug.o
LIB_OBJS += $(OUTPUT)lk/string.o
LIB_OBJS += $(OUTPUT)lk/rbtree.o
LIB_OBJS += $(OUTPUT)lk/strlist.o
+LIB_OBJS += $(OUTPUT)perf/mmap.o
LIB_OBJS += $(OUTPUT)perf/parse-events.o
LIB_OBJS += $(OUTPUT)perf/header.o
LIB_OBJS += $(OUTPUT)perf/symbol.o
diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c
new file mode 100644
index 0000000..02befbd
--- /dev/null
+++ b/tools/lib/perf/mmap.c
@@ -0,0 +1,97 @@
+#include <perf.h>
+#include <lk/util.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) {
+ 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/builtin-record.c b/tools/perf/builtin-record.c
index 241c334..cb33e71 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -8,19 +8,19 @@
#define _FILE_OFFSET_BITS 64
#include "builtin.h"
-
#include "perf.h"
#include <lk/util.h>
-#include <lk/cpumap.h>
#include <lk/debug.h>
+#include <lk/cpumap.h>
#include <perf/misc.h>
-#include <perf/parse-events.h>
-#include <perf/build-id.h>
-#include <perf/header.h>
+#include <perf/mmap.h>
#include <perf/event.h>
-#include <perf/session.h>
+#include <perf/header.h>
#include <perf/symbol.h>
+#include <perf/session.h>
+#include <perf/build-id.h>
+#include <perf/parse-events.h>
#include "util/parse-options.h"
@@ -63,7 +63,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;
@@ -77,37 +77,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;
@@ -135,55 +106,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;
@@ -493,19 +415,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;
@@ -739,10 +648,14 @@ 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 866ba3d..1522ca0 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -28,6 +28,7 @@
#include <linux/rbtree.h>
#include "util/parse-options.h"
#include <perf/parse-events.h>
+#include <perf/mmap.h>
#include <lk/cpumap.h>
#include <lk/debug.h>
@@ -1102,24 +1103,6 @@ static int event__process(event_t *event, struct perf_session *session)
return 0;
}
-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)
{
@@ -1137,7 +1120,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.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 18/21] perf: Split build-id.c
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (16 preceding siblings ...)
2010-07-01 15:55 ` [PATCH 17/21] perf: Carve out mmap helpers for general use Borislav Petkov
@ 2010-07-01 15:56 ` Borislav Petkov
2010-07-01 15:56 ` [PATCH 19/21] x86, mce: Notify about corrected events too Borislav Petkov
` (2 subsequent siblings)
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:56 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
... into perf-specific and generic part.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/lib/perf/build-id.c | 49 --------------------------------------------
tools/lib/perf/build-id.h | 4 +++
tools/perf/Makefile | 1 +
tools/perf/util/build-id.c | 48 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 53 insertions(+), 49 deletions(-)
create mode 100644 tools/perf/util/build-id.c
diff --git a/tools/lib/perf/build-id.c b/tools/lib/perf/build-id.c
index 5969758..c4c1267 100644
--- a/tools/lib/perf/build-id.c
+++ b/tools/lib/perf/build-id.c
@@ -60,52 +60,3 @@ char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
build_id_hex, build_id_hex + 2);
return bf;
}
-
-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 = lk_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/lib/perf/build-id.h b/tools/lib/perf/build-id.h
index 1b73726..224c90b 100644
--- a/tools/lib/perf/build-id.h
+++ b/tools/lib/perf/build-id.h
@@ -6,6 +6,10 @@
extern struct perf_event_ops build_id__mark_dso_hit_ops;
+struct buildid_dir_config {
+ char *dir;
+};
+
char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
extern void set_buildid_dir(void);
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 590c8f6..7d6ee06 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -387,6 +387,7 @@ LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
LIB_OBJS += $(OUTPUT)util/svghelper.o
LIB_OBJS += $(OUTPUT)util/probe-event.o
LIB_OBJS += $(OUTPUT)util/config.o
+LIB_OBJS += $(OUTPUT)util/build-id.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
new file mode 100644
index 0000000..af2f840
--- /dev/null
+++ b/tools/perf/util/build-id.c
@@ -0,0 +1,48 @@
+#include <lk/strbuf.h>
+#include <perf/build-id.h>
+
+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 = lk_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);
+}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 19/21] x86, mce: Notify about corrected events too
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (17 preceding siblings ...)
2010-07-01 15:56 ` [PATCH 18/21] perf: Split build-id.c Borislav Petkov
@ 2010-07-01 15:56 ` Borislav Petkov
2010-07-01 15:56 ` [PATCH 20/21] amd64_edac: Remove polling mechanism Borislav Petkov
2010-07-01 15:56 ` [PATCH 21/21] tools, ras: Add a RAS daemon Borislav Petkov
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:56 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Notify all parties registered on the mce decoder chain about logged
correctable MCEs.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
arch/x86/kernel/cpu/mcheck/mce.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 42d2808..4dc20f0 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -601,6 +601,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
*/
if (!(flags & MCP_DONTLOG) && !mce_dont_log_ce) {
mce_log(&m);
+ atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, &m);
add_taint(TAINT_MACHINE_CHECK);
}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 20/21] amd64_edac: Remove polling mechanism
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (18 preceding siblings ...)
2010-07-01 15:56 ` [PATCH 19/21] x86, mce: Notify about corrected events too Borislav Petkov
@ 2010-07-01 15:56 ` Borislav Petkov
2010-07-01 15:56 ` [PATCH 21/21] tools, ras: Add a RAS daemon Borislav Petkov
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:56 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Switch to reusing the mcheck core's machine check polling mechanism
instead of duplicating functionality by using the EDAC polling routine.
Correct formatting while at it.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
drivers/edac/amd64_edac.c | 118 -------------------------------------------
drivers/edac/edac_mce_amd.c | 16 +++---
2 files changed, 8 insertions(+), 126 deletions(-)
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index cf17dbb..df47daf 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -1975,107 +1975,6 @@ static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
}
/*
- * Check for valid error in the NB Status High register. If so, proceed to read
- * NB Status Low, NB Address Low and NB Address High registers and store data
- * into error structure.
- *
- * Returns:
- * - 1: if hardware regs contains valid error info
- * - 0: if no valid error is indicated
- */
-static int amd64_get_error_info_regs(struct mem_ctl_info *mci,
- struct err_regs *regs)
-{
- struct amd64_pvt *pvt;
- struct pci_dev *misc_f3_ctl;
-
- pvt = mci->pvt_info;
- misc_f3_ctl = pvt->misc_f3_ctl;
-
- if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSH, ®s->nbsh))
- return 0;
-
- if (!(regs->nbsh & K8_NBSH_VALID_BIT))
- return 0;
-
- /* valid error, read remaining error information registers */
- if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSL, ®s->nbsl) ||
- amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAL, ®s->nbeal) ||
- amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAH, ®s->nbeah) ||
- amd64_read_pci_cfg(misc_f3_ctl, K8_NBCFG, ®s->nbcfg))
- return 0;
-
- return 1;
-}
-
-/*
- * This function is called to retrieve the error data from hardware and store it
- * in the info structure.
- *
- * Returns:
- * - 1: if a valid error is found
- * - 0: if no error is found
- */
-static int amd64_get_error_info(struct mem_ctl_info *mci,
- struct err_regs *info)
-{
- struct amd64_pvt *pvt;
- struct err_regs regs;
-
- pvt = mci->pvt_info;
-
- if (!amd64_get_error_info_regs(mci, info))
- return 0;
-
- /*
- * Here's the problem with the K8's EDAC reporting: There are four
- * registers which report pieces of error information. They are shared
- * between CEs and UEs. Furthermore, contrary to what is stated in the
- * BKDG, the overflow bit is never used! Every error always updates the
- * reporting registers.
- *
- * Can you see the race condition? All four error reporting registers
- * must be read before a new error updates them! There is no way to read
- * all four registers atomically. The best than can be done is to detect
- * that a race has occured and then report the error without any kind of
- * precision.
- *
- * What is still positive is that errors are still reported and thus
- * problems can still be detected - just not localized because the
- * syndrome and address are spread out across registers.
- *
- * Grrrrr!!!!! Here's hoping that AMD fixes this in some future K8 rev.
- * UEs and CEs should have separate register sets with proper overflow
- * bits that are used! At very least the problem can be fixed by
- * honoring the ErrValid bit in 'nbsh' and not updating registers - just
- * set the overflow bit - unless the current error is CE and the new
- * error is UE which would be the only situation for overwriting the
- * current values.
- */
-
- regs = *info;
-
- /* Use info from the second read - most current */
- if (unlikely(!amd64_get_error_info_regs(mci, info)))
- return 0;
-
- /* clear the error bits in hardware */
- pci_write_bits32(pvt->misc_f3_ctl, K8_NBSH, 0, K8_NBSH_VALID_BIT);
-
- /* Check for the possible race condition */
- if ((regs.nbsh != info->nbsh) ||
- (regs.nbsl != info->nbsl) ||
- (regs.nbeah != info->nbeah) ||
- (regs.nbeal != info->nbeal)) {
- amd64_mc_printk(mci, KERN_WARNING,
- "hardware STATUS read access race condition "
- "detected!\n");
- return 0;
- }
- return 1;
-}
-
-/*
* Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
* ADDRESS and process.
*/
@@ -2199,20 +2098,6 @@ void amd64_decode_bus_error(int node_id, struct err_regs *regs)
}
/*
- * The main polling 'check' function, called FROM the edac core to perform the
- * error checking and if an error is encountered, error processing.
- */
-static void amd64_check(struct mem_ctl_info *mci)
-{
- struct err_regs regs;
-
- if (amd64_get_error_info(mci, ®s)) {
- struct amd64_pvt *pvt = mci->pvt_info;
- amd_decode_nb_mce(pvt->mc_node_id, ®s, 1);
- }
-}
-
-/*
* Input:
* 1) struct amd64_pvt which contains pvt->dram_f2_ctl pointer
* 2) AMD Family index value
@@ -2739,9 +2624,6 @@ static void amd64_setup_mci_misc_attributes(struct mem_ctl_info *mci)
mci->dev_name = pci_name(pvt->dram_f2_ctl);
mci->ctl_page_to_phys = NULL;
- /* IMPORTANT: Set the polling 'check' function in this module */
- mci->edac_check = amd64_check;
-
/* memory scrubber interface */
mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
diff --git a/drivers/edac/edac_mce_amd.c b/drivers/edac/edac_mce_amd.c
index 97e64bc..bae9351 100644
--- a/drivers/edac/edac_mce_amd.c
+++ b/drivers/edac/edac_mce_amd.c
@@ -133,7 +133,7 @@ static void amd_decode_dc_mce(u64 mc0_status)
u32 ec = mc0_status & 0xffff;
u32 xec = (mc0_status >> 16) & 0xf;
- pr_emerg(" Data Cache Error");
+ pr_emerg("Data Cache Error");
if (xec == 1 && TLB_ERROR(ec))
pr_cont(": %s TLB multimatch.\n", LL_MSG(ec));
@@ -176,7 +176,7 @@ static void amd_decode_ic_mce(u64 mc1_status)
u32 ec = mc1_status & 0xffff;
u32 xec = (mc1_status >> 16) & 0xf;
- pr_emerg(" Instruction Cache Error");
+ pr_emerg("Instruction Cache Error");
if (xec == 1 && TLB_ERROR(ec))
pr_cont(": %s TLB multimatch.\n", LL_MSG(ec));
@@ -233,7 +233,7 @@ static void amd_decode_bu_mce(u64 mc2_status)
u32 ec = mc2_status & 0xffff;
u32 xec = (mc2_status >> 16) & 0xf;
- pr_emerg(" Bus Unit Error");
+ pr_emerg("Bus Unit Error");
if (xec == 0x1)
pr_cont(" in the write data buffers.\n");
@@ -275,7 +275,7 @@ static void amd_decode_ls_mce(u64 mc3_status)
u32 ec = mc3_status & 0xffff;
u32 xec = (mc3_status >> 16) & 0xf;
- pr_emerg(" Load Store Error");
+ pr_emerg("Load Store Error");
if (xec == 0x0) {
u8 rrrr = (ec >> 4) & 0xf;
@@ -304,7 +304,7 @@ void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors)
if (TLB_ERROR(ec) && !report_gart_errors)
return;
- pr_emerg(" Northbridge Error, node %d", node_id);
+ pr_emerg("Northbridge Error, node %d", node_id);
/*
* F10h, revD can disable ErrCpu[3:0] so check that first and also the
@@ -342,13 +342,13 @@ static void amd_decode_fr_mce(u64 mc5_status)
static inline void amd_decode_err_code(unsigned int ec)
{
if (TLB_ERROR(ec)) {
- pr_emerg(" Transaction: %s, Cache Level %s\n",
+ pr_emerg("Transaction: %s, Cache Level %s\n",
TT_MSG(ec), LL_MSG(ec));
} else if (MEM_ERROR(ec)) {
- pr_emerg(" Transaction: %s, Type: %s, Cache Level: %s",
+ pr_emerg("Transaction: %s, Type: %s, Cache Level: %s",
RRRR_MSG(ec), TT_MSG(ec), LL_MSG(ec));
} else if (BUS_ERROR(ec)) {
- pr_emerg(" Transaction type: %s(%s), %s, Cache Level: %s, "
+ pr_emerg("Transaction type: %s(%s), %s, Cache Level: %s, "
"Participating Processor: %s\n",
RRRR_MSG(ec), II_MSG(ec), TO_MSG(ec), LL_MSG(ec),
PP_MSG(ec));
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH 21/21] tools, ras: Add a RAS daemon
2010-07-01 15:55 [RFC PATCH 00/21] RAS daemon prototype, v1 Borislav Petkov
` (19 preceding siblings ...)
2010-07-01 15:56 ` [PATCH 20/21] amd64_edac: Remove polling mechanism Borislav Petkov
@ 2010-07-01 15:56 ` Borislav Petkov
20 siblings, 0 replies; 24+ messages in thread
From: Borislav Petkov @ 2010-07-01 15:56 UTC (permalink / raw)
To: linux-kernel
From: Borislav Petkov <borislav.petkov@amd.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
---
tools/Makefile | 4 +
tools/lib/Makefile | 2 +-
tools/ras/Makefile | 18 ++++++
tools/ras/rasd.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 178 insertions(+), 1 deletions(-)
create mode 100644 tools/ras/Makefile
create mode 100644 tools/ras/rasd.c
diff --git a/tools/Makefile b/tools/Makefile
index 54dea0e..270c027 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -100,11 +100,15 @@ export BASIC_CFLAGS EXTLIBS
perf: lib .FORCE
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1)
+ras: lib .FORCE
+ $(QUIET_SUBDIR0)ras/ $(QUIET_SUBDIR1)
+
lib: .FORCE
$(QUIET_SUBDIR0)lib/ $(QUIET_SUBDIR1)
clean:
$(QUIET_SUBDIR0)lib/ $(QUIET_SUBDIR1) clean
$(QUIET_SUBDIR0)perf/ $(QUIET_SUBDIR1) clean
+ $(QUIET_SUBDIR0)ras/ $(QUIET_SUBDIR1) clean
.PHONY: clean .FORCE
diff --git a/tools/lib/Makefile b/tools/lib/Makefile
index 6f7102b..0786588 100644
--- a/tools/lib/Makefile
+++ b/tools/lib/Makefile
@@ -83,6 +83,6 @@ $(OUTPUT)%.o: %.S
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
clean:
- $(RM) $(LIB_OBJS)
+ $(RM) $(LIB_OBJS) $(LIBFILE)
.PHONY: clean
diff --git a/tools/ras/Makefile b/tools/ras/Makefile
new file mode 100644
index 0000000..26525e7
--- /dev/null
+++ b/tools/ras/Makefile
@@ -0,0 +1,18 @@
+include ../Makefile.lib
+
+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)
+LIBFILE = ../lib/lklib.a
+
+rasd: rasd.o $(LIBFILE)
+ $(QUIET_CC)$(CC) $(ALL_CFLAGS) -o $@ $^ $(EXTLIBS)
+
+%.o: %.c
+ $(QUIET_CC)$(CC) $(ALL_CFLAGS) -c $<
+
+clean:
+ rm -rf *.o rasd
+
+.PHONY: clean
diff --git a/tools/ras/rasd.c b/tools/ras/rasd.c
new file mode 100644
index 0000000..af44e42
--- /dev/null
+++ b/tools/ras/rasd.c
@@ -0,0 +1,155 @@
+/*
+ * 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/debugfs.h>
+#include <perf/mmap.h>
+#include <perf/parse-events.h>
+#include <linux/compiler.h>
+
+#define MMAP_PAGES 128
+
+#define PFX "rasd: "
+
+static int fds[MAX_NR_CPUS];
+static struct mmap_data mmaps[MAX_NR_CPUS];
+
+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 int ras_init(void)
+{
+ int cpu;
+
+ fprintf(stderr, PFX "Starting daemon.");
+
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ if (get_debugfs_mntpt()) {
+ error(PFX "Cannot mount debugfs, exiting...\n");
+ return 1;
+ }
+
+ set_debugfs_path();
+
+ if (parse_events(NULL, "mce:mce_record", 0)) {
+ error(PFX "Error parsing MCE tracepoint\n");
+ return 1;
+ }
+
+ nr_cpus = read_cpu_map(NULL);
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ fds[cpu] = sys_perf_event_open(&attrs[0], -1, cpu, -1,
+ PERF_FLAG_EVENT_PERSISTENT);
+
+ if (fds[cpu] < 0) {
+ error("Error opening perf event on cpu %d\n", cpu);
+ return 1;
+ }
+
+ fcntl(fds[cpu], F_SETFL, O_NONBLOCK);
+ }
+
+ 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|PROT_WRITE, 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;
+}
+
+int main(void)
+{
+ pid_t pid, sid;
+ int i;
+
+ 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, write_output))
+ fprintf(stderr, "Read some mmapped data");
+
+ if (done)
+ for (i = 0; i < nr_cpus; i++)
+ ioctl(fds[i], PERF_EVENT_IOC_DISABLE);
+
+ sleep(30);
+ }
+
+}
--
1.7.1
^ permalink raw reply related [flat|nested] 24+ messages in thread