From: Borislav Petkov <[email protected]>
(resubmitting since lkml wasn't on CC list)
Hi guys,
here's the first rough version of all the jerky code that attempts to
implement a RAS daemon listening for MCEs using perf. This is a preview
code only, I still need to figure out how to do the sample parsing
the easiest and flesh out the daemon functionality a bit more. Also,
I wanted to reuse as much code as possible therefore a lot had to be
reengineered with the perf tool and all its library-like compilation
units.
With this, you can do
make perf
or
make ras
and build the respective tool.
Even though I tried to split the patchset for easier review, please bear
in mind that there are some fat guys there (241K is the biggest one).
However, they don't do anything special except moving code around. As
such, they might not appear on lkml due to vger size constraints so I've
upped the whole patchset also at
git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git perf-v1
The patchset is based on tip/perf/core from last week. Here are some
more details to some of the patches individually:
2: enables the mce tracepoint unconditionally. I had a problem with
perf_event_attr.sample_period which is checked in perf_swevent_add().
Currently, I'm setting it to ULLONG_MAX but this is icky. I'd much
rather have the check do something like
if (event->type != PERF_EVENT_TYPE_PERSISTENT)
if (!hwc->sample_period)
return;
or similar.
4: sys_perf_event_open needs to know about already allocated and
enabled events.
5-10: carves out a bunch of generic perf compilation units into a common
lib. Split into 5 patches for easier review.
12-14: those are pulled in when exporting parse_events.c for external use.
16: lib/perf/misc.c contains functions and global variables like
pager_in_use() or perf_guest or usage_with_options() which are used in
generic utilities but are strictly perf-specific. Long term we should
strive in making the library self-contained and getting rid of those.
19-20: those are only needed for testing, they'll go in over the edac
tree in the next merge window. Added for completeness here.
21: only bare-bones implementation, needs a lot of fleshing out
Anyways, please have a look and let me know either way :)
Thanks,
Boris.
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
... for other in-kernel users.
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
Those are indirectly needed by parse-events.c too.
Signed-off-by: Borislav Petkov <[email protected]>
---
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 <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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 <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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 <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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 <[email protected]>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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 <[email protected]>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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 <[email protected]>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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
From: Borislav Petkov <[email protected]>
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
Notify all parties registered on the mce decoder chain about logged
correctable MCEs.
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
... into perf-specific and generic part.
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
tools/Makefile | 39 +-
tools/lib/Makefile | 6 +
tools/lib/perf/header.c | 4 +-
tools/lib/perf/header.h | 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
diff --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;
diff --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
From: Borislav Petkov <[email protected]>
Carve out debug.[ch] and string.c and make them generic.
Signed-off-by: Borislav Petkov <[email protected]>
---
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 +-
tools/perf/util/header.c | 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 <[email protected]>
*/
#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"
diff --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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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 ++++++
tools/lib/perf/header.h | 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 <[email protected]>
+ */
+#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 */
diff --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 <[email protected]>
- */
-#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
From: Borislav Petkov <[email protected]>
That is, take perf-specific compilation units used by other tools and
put them in tools/lib/perf/.
Signed-off-by: Borislav Petkov <[email protected]>
---
tools/Makefile | 17 +-
tools/lib/Makefile | 4 +
tools/lib/perf/header.c | 1199 ++++++++++++++++++++++++++++++++++++
tools/lib/perf/header.h | 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 +-
tools/perf/util/header.c | 1199 ------------------------------------
tools/perf/util/header.h | 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
diff --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;
+}
diff --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
From: Borislav Petkov <[email protected]>
This is a temporary unit for sharing code between perf and the library.
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
Export the mmap_read* helpers into tools/lib/perf/mmap.[ch]
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
Carve out strlist.[ch] and rbtree.c and make them generic.
Signed-off-by: Borislav Petkov <[email protected]>
---
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 <[email protected]>
+ *
+ * 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 <[email protected]>
- *
- * 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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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 +-
tools/perf/util/pager.c | 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 <[email protected]>
*/
#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)
diff --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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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 <[email protected]>
+ */
+
+#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 <[email protected]>
- */
-
-#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
From: Borislav Petkov <[email protected]>
Carve out relevant debugfs setup utils for general use.
Signed-off-by: Borislav Petkov <[email protected]>
---
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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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 --
tools/perf/util/header.c | 2 +-
tools/perf/util/header.h | 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 <[email protected]>
*/
-#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__ */
diff --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"
diff --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 <[email protected]>
*/
-#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
From: Borislav Petkov <[email protected]>
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 <[email protected]>
---
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
From: Arnaldo Carvalho de Melo <[email protected]>
Date: Thu, Jul 01, 2010 at 12:10:54PM -0400
Hi Arnaldo,
> Em Thu, Jul 01, 2010 at 12:47:37PM -0300, Arnaldo Carvalho de Melo escreveu:
> > Em Thu, Jul 01, 2010 at 03:49:19PM +0200, Borislav Petkov escreveu:
> > > From: Borislav Petkov <[email protected]>
> > >
> > > 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.
> >
> > Testing this now, thanks,
>
> Needs the patch below for "make O=~/build/perf tools" to continue
> working. Needs some more polishing so as not to break the non O= case.
>
> After lunch will investigate why my usual way to build the tools isn't working
> anymore, some missing -I probably:
>
> [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf install
> make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> CC /home/acme/git/build/perf/builtin-record.o
> builtin-record.c:24:23: error: lk/cpumap.h: No such file or directory
> cc1: warnings being treated as errors
> builtin-record.c: In function ‘__cmd_record’:
> builtin-record.c:622: error: implicit declaration of function ‘read_cpu_map’
> builtin-record.c:632: error: ‘cpumap’ undeclared (first use in this function)
> builtin-record.c:632: error: (Each undeclared identifier is reported only once
> builtin-record.c:632: error: for each function it appears in.)
> make: *** [/home/acme/git/build/perf/builtin-record.o] Error 1
> make: Leaving directory `/home/acme/git/linux-2.6-tip/tools/perf'
> [acme@emilia linux-2.6-tip]$
>
> diff --git a/tools/lib/Makefile b/tools/lib/Makefile
> index 48f8e21..814673e 100644
> --- a/tools/lib/Makefile
> +++ b/tools/lib/Makefile
> @@ -5,6 +5,9 @@ LIB_H =
> LIB_OBJS =
>
> LIB_H += lk/cpumap.h
> +OUTPUT := $(OUTPUT)/lib/
> +
> +$(shell sh -c 'mkdir -p $(OUTPUT)/lk' 2> /dev/null)
>
> LIB_OBJS += $(OUTPUT)lk/bitmap.o
> LIB_OBJS += $(OUTPUT)lk/cpumap.o
I'll fix that up, thanks for testing.
--
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
From: Frederic Weisbecker <[email protected]>
Date: Thu, Jul 01, 2010 at 12:14:50PM -0400
Hi Frederic,
> On Thu, Jul 01, 2010 at 06:12:45PM +0200, Peter Zijlstra wrote:
> > On Thu, 2010-07-01 at 18:11 +0200, Frederic Weisbecker wrote:
> > > I suspect we need another syscall that can list all the persistent events
> > > with a unique id and the attrs that follow.
> > >
> > > So you get a unique id for all of them and you can create an fd on top
> > > of this id by using a PERF_FLAG_REQUEST_PERSISTENT and this id put in
> > > attr.config.
> >
> > Isn't that what filesystems were invented for?
>
>
> The problem is when you create a persistent event, you lose the fd.
> So you need to retrieve it somehow.
actually the idea is to decouple those from the fd alltogether and
provide specific file_operations in debugfs and such, as Peter
suggested. Which sounds much more sane to me especially since, at least
in the MCE case, all the entities that register into that event need to
see the same samples (and read the same buffers etc).
And let's try not to read too much into those persistent events - it may
just as well be that we need them only for MCEs and nothing else :)
--
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
On Thu, Jul 01, 2010 at 06:24:24PM +0200, Borislav Petkov wrote:
> From: Frederic Weisbecker <[email protected]>
> Date: Thu, Jul 01, 2010 at 12:14:50PM -0400
>
> Hi Frederic,
>
> > On Thu, Jul 01, 2010 at 06:12:45PM +0200, Peter Zijlstra wrote:
> > > On Thu, 2010-07-01 at 18:11 +0200, Frederic Weisbecker wrote:
> > > > I suspect we need another syscall that can list all the persistent events
> > > > with a unique id and the attrs that follow.
> > > >
> > > > So you get a unique id for all of them and you can create an fd on top
> > > > of this id by using a PERF_FLAG_REQUEST_PERSISTENT and this id put in
> > > > attr.config.
> > >
> > > Isn't that what filesystems were invented for?
> >
> >
> > The problem is when you create a persistent event, you lose the fd.
> > So you need to retrieve it somehow.
>
> actually the idea is to decouple those from the fd alltogether and
> provide specific file_operations in debugfs and such, as Peter
> suggested. Which sounds much more sane to me especially since, at least
> in the MCE case, all the entities that register into that event need to
> see the same samples (and read the same buffers etc).
Sure the idea of putting that in fs is better.
Note the idea of a new syscall was quite close: it would have listed
unique ids of the persistent events, but not fds, you'd have yet to create
those fds on top of the ids.
>
> And let's try not to read too much into those persistent events - it may
> just as well be that we need them only for MCEs and nothing else :)
Not really. It would be useful for boot tracing, amongst various other things
like flight recorder tracing, etc...
Em Thu, Jul 01, 2010 at 06:17:38PM +0200, Borislav Petkov escreveu:
> From: Arnaldo Carvalho de Melo <[email protected]>
> Date: Thu, Jul 01, 2010 at 12:10:54PM -0400
>
> Hi Arnaldo,
>
> > Em Thu, Jul 01, 2010 at 12:47:37PM -0300, Arnaldo Carvalho de Melo escreveu:
> > > Em Thu, Jul 01, 2010 at 03:49:19PM +0200, Borislav Petkov escreveu:
> > > > From: Borislav Petkov <[email protected]>
> > > >
> > > > 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.
> > >
> > > Testing this now, thanks,
> >
> > Needs the patch below for "make O=~/build/perf tools" to continue
> > working. Needs some more polishing so as not to break the non O= case.
> >
> > After lunch will investigate why my usual way to build the tools isn't working
> > anymore, some missing -I probably:
> >
> > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf install
> > make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > CC /home/acme/git/build/perf/builtin-record.o
> > builtin-record.c:24:23: error: lk/cpumap.h: No such file or directory
> > cc1: warnings being treated as errors
The patch below fixes it.
But we have one other problem:
[acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/
make: Entering directory `/home/acme/git/linux-2.6-tip/tools'
make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/lib'
make[1]: `lklib.a' is up to date.
make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/lib'
make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
GEN perf-archive
make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/perf'
make: Leaving directory `/home/acme/git/linux-2.6-tip/tools'
[acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf/
make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
* new build flags or prefix
CC /home/acme/git/build/perf/perf.o
CC /home/acme/git/build/perf/builtin-annotate.o
CC /home/acme/git/build/perf/builtin-bench.o
The logic that detects "new build flags or prefix" gets confused when we
alternate between tools/ and tools/perf/, checking that.
- Arnaldo
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 200fc13..72ccd17 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -268,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 -I../lib/
BASIC_LDFLAGS =
# Guard against environment variables
From: Arnaldo Carvalho de Melo <[email protected]>
Date: Thu, Jul 01, 2010 at 01:14:49PM -0400
> > > Needs the patch below for "make O=~/build/perf tools" to continue
> > > working. Needs some more polishing so as not to break the non O= case.
> > >
> > > After lunch will investigate why my usual way to build the tools isn't working
> > > anymore, some missing -I probably:
> > >
> > > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf install
> > > make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > > CC /home/acme/git/build/perf/builtin-record.o
> > > builtin-record.c:24:23: error: lk/cpumap.h: No such file or directory
> > > cc1: warnings being treated as errors
>
> The patch below fixes it.
>
> But we have one other problem:
>
> [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/
> make: Entering directory `/home/acme/git/linux-2.6-tip/tools'
> make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/lib'
> make[1]: `lklib.a' is up to date.
> make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/lib'
> make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> GEN perf-archive
> make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/perf'
> make: Leaving directory `/home/acme/git/linux-2.6-tip/tools'
> [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf/
> make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> * new build flags or prefix
> CC /home/acme/git/build/perf/perf.o
> CC /home/acme/git/build/perf/builtin-annotate.o
> CC /home/acme/git/build/perf/builtin-bench.o
>
> The logic that detects "new build flags or prefix" gets confused when we
> alternate between tools/ and tools/perf/, checking that.
This might need an up-to-date check for the lklib.a archive first and
not do "new build flags or prefix" if it is. Yeah, this is a different
library and the logic doesn't know about it.
> - Arnaldo
>
> diff --git a/tools/perf/Makefile b/tools/perf/Makefile
> index 200fc13..72ccd17 100644
> --- a/tools/perf/Makefile
> +++ b/tools/perf/Makefile
> @@ -268,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 -I../lib/
> BASIC_LDFLAGS =
Right, the whole scheme with the include paths is kinda shaky. I have
the -I$(CURDIR)/lib BASIC_CFLAGS in the top Makefile for the case where
we're in tools/ but this doesn't help with the -C switch as you show
above.
Maybe we should add more logic later to make the include path guessing
more robust no matter from which directory you execute make instead of
polluting BASIC_CFLAGS with duplicated path entries ... Hmmm...
--
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
From: Arnaldo Carvalho de Melo <[email protected]>
Date: Thu, Jul 01, 2010 at 05:13:31PM -0400
> Em Thu, Jul 01, 2010 at 10:14:18PM +0200, Borislav Petkov escreveu:
> > From: Arnaldo Carvalho de Melo <[email protected]>
> > Date: Thu, Jul 01, 2010 at 01:14:49PM -0400
> > > But we have one other problem:
>
> > > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/
> > > make: Entering directory `/home/acme/git/linux-2.6-tip/tools'
> > > make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/lib'
> > > make[1]: `lklib.a' is up to date.
> > > make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/lib'
> > > make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > > GEN perf-archive
> > > make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > > make: Leaving directory `/home/acme/git/linux-2.6-tip/tools'
> > > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf/
> > > make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > > * new build flags or prefix
> > > CC /home/acme/git/build/perf/perf.o
> > > CC /home/acme/git/build/perf/builtin-annotate.o
> > >
> > > The logic that detects "new build flags or prefix" gets confused when we
> > > alternate between tools/ and tools/perf/, checking that.
> >
> > This might need an up-to-date check for the lklib.a archive first and
> > not do "new build flags or prefix" if it is. Yeah, this is a different
> > library and the logic doesn't know about it.
>
> I got sidetracked, but this is what is in $(O)/PERF-CFLAGS for
> make -C tools/
>
> [acme@emilia linux-2.6-tip]$ cat ../build/perf/PERF-CFLAGS
> -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Wformat-y2k -Wshadow -Winit-self -Wpacked -Wredundant-decls -Wstack-protector -Wstrict-aliasing=3 -Wswitch-default -Wswitch-enum -Wno-system-headers -Wundef -Wvolatile-register-var -Wwrite-strings -Wbad-function-cast -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wstrict-prototypes -Wdeclaration-after-statement -fstack-protector-all -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/home/acme/git/linux-2.6-tip/tools/lib -I/home/acme/git/linux-2.6-tip/tools/perf -I/home/acme/git/linux-2.6-tip/tools/perf/util/include -Iutil/include -Iarch/x86/include -I../lib/ -I/home/acme/git/build/perf/ -DLIBELF_NO_MMAP -I/usr/include/elfutils -DDWARF_SUPPORT -I/usr/include/slang -DSHA1_HEADER='<openssl/sha.h>' : /home/acme/bin:libexec/perf-core:share/perf-core/templates:/home/acme
> [acme@emilia linux-2.6-tip]$
>
> And this is its contents if we use "make -C tools/perf/"
>
> -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 -fstack-protector-all -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Iutil/include -Iarch/x86/include -I../lib/ -I/home/acme/git/build/perf/ -DLIBELF_NO_MMAP -I/usr/include/elfutils -DDWARF_SUPPORT -I/usr/include/slang -DSHA1_HEADER='<openssl/sha.h>' : /home/acme/bin:libexec/perf-core:share/perf-core/templates:/home/acme
>
> Its just a matter of making both match :-)
Oh yeah, I see the difference. The second one is missing all the -Wxxx
settings and the BASIS_CFLAGS with all the include paths which get
computed in the top Makefile and exported to the Makefiles in the
subdirectories. Hmm, maybe we should reroute the "make -C tools/perf/"
invocation to go up one dir and execute the top-Makefile after all...
I'll look into it soon, I'm almost falling asleep now :)
Good night.
--
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
Em Thu, Jul 01, 2010 at 10:14:18PM +0200, Borislav Petkov escreveu:
> From: Arnaldo Carvalho de Melo <[email protected]>
> Date: Thu, Jul 01, 2010 at 01:14:49PM -0400
> > But we have one other problem:
> > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/
> > make: Entering directory `/home/acme/git/linux-2.6-tip/tools'
> > make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/lib'
> > make[1]: `lklib.a' is up to date.
> > make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/lib'
> > make[1]: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > GEN perf-archive
> > make[1]: Leaving directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > make: Leaving directory `/home/acme/git/linux-2.6-tip/tools'
> > [acme@emilia linux-2.6-tip]$ make O=~/git/build/perf -C tools/perf/
> > make: Entering directory `/home/acme/git/linux-2.6-tip/tools/perf'
> > * new build flags or prefix
> > CC /home/acme/git/build/perf/perf.o
> > CC /home/acme/git/build/perf/builtin-annotate.o
> >
> > The logic that detects "new build flags or prefix" gets confused when we
> > alternate between tools/ and tools/perf/, checking that.
>
> This might need an up-to-date check for the lklib.a archive first and
> not do "new build flags or prefix" if it is. Yeah, this is a different
> library and the logic doesn't know about it.
I got sidetracked, but this is what is in $(O)/PERF-CFLAGS for
make -C tools/
[acme@emilia linux-2.6-tip]$ cat ../build/perf/PERF-CFLAGS
-ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Wformat-y2k -Wshadow -Winit-self -Wpacked -Wredundant-decls -Wstack-protector -Wstrict-aliasing=3 -Wswitch-default -Wswitch-enum -Wno-system-headers -Wundef -Wvolatile-register-var -Wwrite-strings -Wbad-function-cast -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wstrict-prototypes -Wdeclaration-after-statement -fstack-protector-all -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/home/acme/git/linux-2.6-tip/tools/lib -I/home/acme/git/linux-2.6-tip/tools/perf -I/home/acme/git/linux-2.6-tip/tools/perf/util/include -Iutil/include -Iarch/x86/include -I../lib/ -I/home/acme/git/build/perf/ -DLIBELF_NO_MMAP -I/usr/include/elfutils -DDWARF_SUPPORT -I/usr/include/slang -DSHA1_HEADER='<openssl/sha.h>' : /home/acme/bin:libexec/perf-core:share/perf-core/templates:/home/acme
[acme@emilia linux-2.6-tip]$
And this is its contents if we use "make -C tools/perf/"
-ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 -fstack-protector-all -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Iutil/include -Iarch/x86/include -I../lib/ -I/home/acme/git/build/perf/ -DLIBELF_NO_MMAP -I/usr/include/elfutils -DDWARF_SUPPORT -I/usr/include/slang -DSHA1_HEADER='<openssl/sha.h>' : /home/acme/bin:libexec/perf-core:share/perf-core/templates:/home/acme
Its just a matter of making both match :-)
- Arnaldo
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 <[email protected]>
>
> Those are indirectly needed by parse-events.c too.
>
> Signed-off-by: Borislav Petkov <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * 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 <[email protected]>
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * 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 <[email protected]>
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * 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 <[email protected]>
> - *
> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> - *
> - * 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 <[email protected]>
> - *
> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> - *
> - * 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 <[email protected]>
> - *
> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> - *
> - * 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
From: Steven Rostedt <[email protected]>
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