summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/cpu/perf_event.c4
-rw-r--r--kernel/perf_event.c4
-rw-r--r--tools/perf/.gitignore1
-rw-r--r--tools/perf/Documentation/perf-kmem.txt44
-rw-r--r--tools/perf/Makefile3
-rw-r--r--tools/perf/bench/bench.h1
-rw-r--r--tools/perf/bench/mem-memcpy.c193
-rw-r--r--tools/perf/builtin-annotate.c80
-rw-r--r--tools/perf/builtin-bench.c15
-rw-r--r--tools/perf/builtin-help.c4
-rw-r--r--tools/perf/builtin-kmem.c354
-rw-r--r--tools/perf/builtin-probe.c4
-rw-r--r--tools/perf/builtin-report.c86
-rw-r--r--tools/perf/builtin-sched.c16
-rw-r--r--tools/perf/builtin-top.c68
-rw-r--r--tools/perf/builtin-trace.c2
-rw-r--r--tools/perf/command-list.txt1
-rw-r--r--tools/perf/util/data_map.c8
-rw-r--r--tools/perf/util/data_map.h2
-rw-r--r--tools/perf/util/event.h7
-rw-r--r--tools/perf/util/header.c8
-rw-r--r--tools/perf/util/include/asm/bug.h22
-rw-r--r--tools/perf/util/include/linux/bitops.h2
-rw-r--r--tools/perf/util/map.c14
-rw-r--r--tools/perf/util/parse-events.c2
-rw-r--r--tools/perf/util/process_event.c53
-rw-r--r--tools/perf/util/process_event.h29
-rw-r--r--tools/perf/util/process_events.c64
-rw-r--r--tools/perf/util/process_events.h35
-rw-r--r--tools/perf/util/symbol.c110
-rw-r--r--tools/perf/util/symbol.h17
-rw-r--r--tools/perf/util/thread.c2
-rw-r--r--tools/perf/util/thread.h4
-rw-r--r--tools/perf/util/util.h16
-rw-r--r--tools/perf/util/wrapper.c61
35 files changed, 916 insertions, 420 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index bd874302420..c1bbed1021d 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -2229,10 +2229,10 @@ validate_event(struct cpu_hw_events *cpuc, struct perf_event *event)
{
struct hw_perf_event fake_event = event->hw;
- if (event->pmu != &pmu)
+ if (event->pmu && event->pmu != &pmu)
return 0;
- return x86_schedule_event(cpuc, &fake_event);
+ return x86_schedule_event(cpuc, &fake_event) >= 0;
}
static int validate_group(struct perf_event *event)
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 9425c9600c8..35df94e344f 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -1831,7 +1831,7 @@ static int perf_event_read_group(struct perf_event *event,
size = n * sizeof(u64);
- if (copy_to_user(buf + size, values, size)) {
+ if (copy_to_user(buf + ret, values, size)) {
ret = -EFAULT;
goto unlock;
}
@@ -3914,7 +3914,7 @@ void perf_swevent_put_recursion_context(int rctx)
{
struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
barrier();
- cpuctx->recursion[rctx]++;
+ cpuctx->recursion[rctx]--;
put_cpu_var(perf_cpu_context);
}
EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context);
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 0854f110bf7..fe08660ce0b 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -12,6 +12,7 @@ perf*.1
perf*.xml
perf*.html
common-cmds.h
+perf.data
tags
TAGS
cscope*
diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
new file mode 100644
index 00000000000..44b0ce35c28
--- /dev/null
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -0,0 +1,44 @@
+perf-kmem(1)
+==============
+
+NAME
+----
+perf-kmem - Tool to trace/measure kernel memory(slab) properties
+
+SYNOPSIS
+--------
+[verse]
+'perf kmem' {record} [<options>]
+
+DESCRIPTION
+-----------
+There's two variants of perf kmem:
+
+ 'perf kmem record <command>' to record the kmem events
+ of an arbitrary workload.
+
+ 'perf kmem' to report kernel memory statistics.
+
+OPTIONS
+-------
+-i <file>::
+--input=<file>::
+ Select the input file (default: perf.data)
+
+--stat=<caller|alloc>::
+ Select per callsite or per allocation statistics
+
+-s <key[,key2...]>::
+--sort=<key[,key2...]>::
+ Sort the output (default: frag,hit,bytes)
+
+-l <num>::
+--line=<num>::
+ Print n lines only
+
+--raw-ip::
+ Print raw ip instead of symbol
+
+SEE ALSO
+--------
+linkperf:perf-record[1]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 3ef6621bf6c..de37d492e10 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -369,6 +369,7 @@ LIB_H += util/sort.h
LIB_H += util/hist.h
LIB_H += util/thread.h
LIB_H += util/data_map.h
+LIB_H += util/process_events.h
LIB_OBJS += util/abspath.o
LIB_OBJS += util/alias.o
@@ -411,6 +412,7 @@ LIB_OBJS += util/svghelper.o
LIB_OBJS += util/sort.o
LIB_OBJS += util/hist.o
LIB_OBJS += util/data_map.o
+LIB_OBJS += util/process_events.o
BUILTIN_OBJS += builtin-annotate.o
@@ -419,6 +421,7 @@ BUILTIN_OBJS += builtin-bench.o
# Benchmark modules
BUILTIN_OBJS += bench/sched-messaging.o
BUILTIN_OBJS += bench/sched-pipe.o
+BUILTIN_OBJS += bench/mem-memcpy.o
BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-sched.o
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 9fbd8d745fa..f7781c6267c 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -3,6 +3,7 @@
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);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
new file mode 100644
index 00000000000..89773178e89
--- /dev/null
+++ b/tools/perf/bench/mem-memcpy.c
@@ -0,0 +1,193 @@
+/*
+ * mem-memcpy.c
+ *
+ * memcpy: Simple memory copy in various ways
+ *
+ * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ */
+#include <ctype.h>
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../util/string.h"
+#include "../util/header.h"
+#include "bench.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define K 1024
+
+static const char *length_str = "1MB";
+static const char *routine = "default";
+static int use_clock = 0;
+static int clock_fd;
+
+static const struct option options[] = {
+ OPT_STRING('l', "length", &length_str, "1MB",
+ "Specify length of memory to copy. "
+ "available unit: B, MB, GB (upper and lower)"),
+ OPT_STRING('r', "routine", &routine, "default",
+ "Specify routine to copy"),
+ OPT_BOOLEAN('c', "clock", &use_clock,
+ "Use CPU clock for measuring"),
+ OPT_END()
+};
+
+struct routine {
+ const char *name;
+ const char *desc;
+ void * (*fn)(void *dst, const void *src, size_t len);
+};
+
+struct routine routines[] = {
+ { "default",
+ "Default memcpy() provided by glibc",
+ memcpy },
+ { NULL,
+ NULL,
+ NULL }
+};
+
+static const char * const bench_mem_memcpy_usage[] = {
+ "perf bench mem memcpy <options>",
+ NULL
+};
+
+static struct perf_event_attr clock_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES
+};
+
+static void init_clock(void)
+{
+ clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
+
+ if (clock_fd < 0 && errno == ENOSYS)
+ die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ else
+ BUG_ON(clock_fd < 0);
+}
+
+static u64 get_clock(void)
+{
+ int ret;
+ u64 clk;
+
+ ret = read(clock_fd, &clk, sizeof(u64));
+ BUG_ON(ret != sizeof(u64));
+
+ return clk;
+}
+
+static double timeval2double(struct timeval *ts)
+{
+ return (double)ts->tv_sec +
+ (double)ts->tv_usec / (double)1000000;
+}
+
+int bench_mem_memcpy(int argc, const char **argv,
+ const char *prefix __used)
+{
+ int i;
+ void *dst, *src;
+ size_t length;
+ double bps = 0.0;
+ struct timeval tv_start, tv_end, tv_diff;
+ u64 clock_start, clock_end, clock_diff;
+
+ clock_start = clock_end = clock_diff = 0ULL;
+ argc = parse_options(argc, argv, options,
+ bench_mem_memcpy_usage, 0);
+
+ tv_diff.tv_sec = 0;
+ tv_diff.tv_usec = 0;
+ length = (size_t)perf_atoll((char *)length_str);
+
+ if ((s64)length <= 0) {
+ fprintf(stderr, "Invalid length:%s\n", length_str);
+ return 1;
+ }
+
+ for (i = 0; routines[i].name; i++) {
+ if (!strcmp(routines[i].name, routine))
+ break;
+ }
+ if (!routines[i].name) {
+ printf("Unknown routine:%s\n", routine);
+ printf("Available routines...\n");
+ for (i = 0; routines[i].name; i++) {
+ printf("\t%s ... %s\n",
+ routines[i].name, routines[i].desc);
+ }
+ return 1;
+ }
+
+ dst = zalloc(length);
+ if (!dst)
+ die("memory allocation failed - maybe length is too large?\n");
+
+ src = zalloc(length);
+ if (!src)
+ die("memory allocation failed - maybe length is too large?\n");
+
+ if (bench_format == BENCH_FORMAT_DEFAULT) {
+ printf("# Copying %s Bytes from %p to %p ...\n\n",
+ length_str, src, dst);
+ }
+
+ if (use_clock) {
+ init_clock();
+ clock_start = get_clock();
+ } else {
+ BUG_ON(gettimeofday(&tv_start, NULL));
+ }
+
+ routines[i].fn(dst, src, length);
+
+ if (use_clock) {
+ clock_end = get_clock();
+ clock_diff = clock_end - clock_start;
+ } else {
+ BUG_ON(gettimeofday(&tv_end, NULL));
+ timersub(&tv_end, &tv_start, &tv_diff);
+ bps = (double)((double)length / timeval2double(&tv_diff));
+ }
+
+ switch (bench_format) {
+ case BENCH_FORMAT_DEFAULT:
+ if (use_clock) {
+ printf(" %14lf Clock/Byte\n",
+ (double)clock_diff / (double)length);
+ } else {
+ if (bps < K)
+ printf(" %14lf B/Sec\n", bps);
+ else if (bps < K * K)
+ printf(" %14lfd KB/Sec\n", bps / 1024);
+ else if (bps < K * K * K)
+ printf(" %14lf MB/Sec\n", bps / 1024 / 1024);
+ else {
+ printf(" %14lf GB/Sec\n",
+ bps / 1024 / 1024 / 1024);
+ }
+ }
+ break;
+ case BENCH_FORMAT_SIMPLE:
+ if (use_clock) {
+ printf("%14lf\n",
+ (double)clock_diff / (double)length);
+ } else
+ printf("%lf\n", bps);
+ break;
+ default:
+ /* reaching this means there's some disaster: */
+ die("unknown format: %d\n", bench_format);
+ break;
+ }
+
+ return 0;
+}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 6b13a1ecf1e..18ac5eaefc3 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -24,6 +24,7 @@
#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
+#include "util/process_events.h"
static char const *input_name = "perf.data";
@@ -33,11 +34,9 @@ static int input;
static int full_paths;
static int print_line;
-static bool use_modules;
static unsigned long page_size;
static unsigned long mmap_window = 32;
-const char *vmlinux_name;
struct sym_hist {
u64 sum;
@@ -55,6 +54,11 @@ struct sym_priv {
struct sym_ext *ext;
};
+static struct symbol_conf symbol_conf = {
+ .priv_size = sizeof(struct sym_priv),
+ .try_vmlinux_path = true,
+};
+
static const char *sym_hist_filter;
static int symbol_filter(struct map *map __used, struct symbol *sym)
@@ -158,7 +162,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
level = 'k';
- sym = kernel_maps__find_symbol(ip, &map, symbol_filter);
+ sym = kernel_maps__find_function(ip, &map, symbol_filter);
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
} else if (event->header.misc & PERF_RECORD_MISC_USER) {
@@ -167,7 +171,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (map != NULL) {
got_map:
ip = map->map_ip(map, ip);
- sym = map__find_symbol(map, ip, symbol_filter);
+ sym = map__find_function(map, ip, symbol_filter);
} else {
/*
* If this is outside of all known maps,
@@ -202,32 +206,6 @@ got_map:
}
static int
-process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
-{
- struct map *map = map__new(&event->mmap, NULL, 0);
- struct thread *thread = threads__findnew(event->mmap.pid);
-
- dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->mmap.pid,
- (void *)(long)event->mmap.start,
- (void *)(long)event->mmap.len,
- (void *)(long)event->mmap.pgoff,
- event->mmap.filename);
-
- if (thread == NULL || map == NULL) {
- dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
- return 0;
- }
-
- thread__insert_map(thread, map);
- total_mmap++;
-
- return 0;
-}
-
-static int
process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread = threads__findnew(event->comm.pid);
@@ -248,33 +226,6 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_fork_event(event_t *event, unsigned long offset, unsigned long head)
-{
- struct thread *thread = threads__findnew(event->fork.pid);
- struct thread *parent = threads__findnew(event->fork.ppid);
-
- dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->fork.pid, event->fork.ppid);
-
- /*
- * A thread clone will have the same PID for both
- * parent and child.
- */
- if (thread == parent)
- return 0;
-
- if (!thread || !parent || thread__fork(thread, parent)) {
- dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
- return -1;
- }
- total_fork++;
-
- return 0;
-}
-
-static int
process_event(event_t *event, unsigned long offset, unsigned long head)
{
switch (event->header.type) {
@@ -288,7 +239,7 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return process_comm_event(event, offset, head);
case PERF_RECORD_FORK:
- return process_fork_event(event, offset, head);
+ return process_task_event(event, offset, head);
/*
* We dont process them right now but they are fine:
*/
@@ -638,11 +589,6 @@ static int __cmd_annotate(void)
exit(0);
}
- if (kernel_maps__init(vmlinux_name, true, use_modules) < 0) {
- pr_err("failed to create kernel maps for symbol resolution\b");
- return -1;
- }
-
remap:
buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset);
@@ -743,8 +689,9 @@ static const struct option options[] = {
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
- OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
- OPT_BOOLEAN('m', "modules", &use_modules,
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('l', "print-line", &print_line,
"print matching source lines (may be slow)"),
@@ -770,7 +717,8 @@ static void setup_sorting(void)
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
- symbol__init(sizeof(struct sym_priv));
+ if (symbol__init(&symbol_conf) < 0)
+ return -1;
page_size = getpagesize();
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 90c39baae0d..e043eb83092 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -12,6 +12,7 @@
*
* Available subsystem list:
* sched ... scheduler and IPC mechanism
+ * mem ... memory access performance
*
*/
@@ -43,6 +44,15 @@ static struct bench_suite sched_suites[] = {
NULL }
};
+static struct bench_suite mem_suites[] = {
+ { "memcpy",
+ "Simple memory copy in various ways",
+ bench_mem_memcpy },
+ { NULL,
+ NULL,
+ NULL }
+};
+
struct bench_subsys {
const char *name;
const char *summary;
@@ -53,9 +63,12 @@ static struct bench_subsys subsystems[] = {
{ "sched",
"scheduler and IPC mechanism",
sched_suites },
+ { "mem",
+ "memory access performance",
+ mem_suites },
{ NULL,
NULL,
- NULL }
+ NULL }
};
static void dump_suites(int subsys_index)
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 768f9c82631..9f810b17c25 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -179,7 +179,7 @@ static void add_man_viewer(const char *name)
while (*p)
p = &((*p)->next);
- *p = calloc(1, (sizeof(**p) + len + 1));
+ *p = zalloc(sizeof(**p) + len + 1);
strncpy((*p)->name, name, len);
}
@@ -194,7 +194,7 @@ static void do_add_man_viewer_info(const char *name,
size_t len,
const char *value)
{
- struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1);
+ struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
strncpy(new->name, name, len);
new->info = strdup(value);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 256d18fa047..35722fafc4d 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -26,26 +26,28 @@ static u64 sample_type;
static int alloc_flag;
static int caller_flag;
-sort_fn_t alloc_sort_fn;
-sort_fn_t caller_sort_fn;
-
static int alloc_lines = -1;
static int caller_lines = -1;
+static bool raw_ip;
+
+static char default_sort_order[] = "frag,hit,bytes";
+
static char *cwd;
static int cwdlen;
+static int *cpunode_map;
+static int max_cpu_num;
+
struct alloc_stat {
- union {
- struct {
- char *name;
- u64 call_site;
- };
- u64 ptr;
- };
+ u64 call_site;
+ u64 ptr;
u64 bytes_req;
u64 bytes_alloc;
u32 hit;
+ u32 pingpong;
+
+ short alloc_cpu;
struct rb_node node;
};
@@ -56,12 +58,74 @@ static struct rb_root root_caller_stat;
static struct rb_root root_caller_sorted;
static unsigned long total_requested, total_allocated;
+static unsigned long nr_allocs, nr_cross_allocs;
struct raw_event_sample {
u32 size;
char data[0];
};
+#define PATH_SYS_NODE "/sys/devices/system/node"
+
+static void init_cpunode_map(void)
+{
+ FILE *fp;
+ int i;
+
+ fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
+ if (!fp) {
+ max_cpu_num = 4096;
+ return;
+ }
+
+ if (fscanf(fp, "%d", &max_cpu_num) < 1)
+ die("Failed to read 'kernel_max' from sysfs");
+ max_cpu_num++;
+
+ cpunode_map = calloc(max_cpu_num, sizeof(int));
+ if (!cpunode_map)
+ die("calloc");
+ for (i = 0; i < max_cpu_num; i++)
+ cpunode_map[i] = -1;
+ fclose(fp);
+}
+
+static void setup_cpunode_map(void)
+{
+ struct dirent *dent1, *dent2;
+ DIR *dir1, *dir2;
+ unsigned int cpu, mem;
+ char buf[PATH_MAX];
+
+ init_cpunode_map();
+
+ dir1 = opendir(PATH_SYS_NODE);
+ if (!dir1)
+ return;
+
+ while (true) {
+ dent1 = readdir(dir1);
+ if (!dent1)
+ break;
+
+ if (sscanf(dent1->d_name, "node%u", &mem) < 1)
+ continue;
+
+ snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
+ dir2 = opendir(buf);
+ if (!dir2)
+ continue;
+ while (true) {
+ dent2 = readdir(dir2);
+ if (!dent2)
+ break;
+ if (sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
+ continue;
+ cpunode_map[cpu] = mem;
+ }
+ }
+}
+
static int
process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
@@ -81,16 +145,13 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
-static void insert_alloc_stat(unsigned long ptr,
- int bytes_req, int bytes_alloc)
+static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
+ int bytes_req, int bytes_alloc, int cpu)
{
struct rb_node **node = &root_alloc_stat.rb_node;
struct rb_node *parent = NULL;
struct alloc_stat *data = NULL;
- if (!alloc_flag)
- return;
-
while (*node) {
parent = *node;
data = rb_entry(*node, struct alloc_stat, node);
@@ -109,7 +170,10 @@ static void insert_alloc_stat(unsigned long ptr,
data->bytes_alloc += bytes_req;
} else {
data = malloc(sizeof(*data));
+ if (!data)
+ die("malloc");
data->ptr = ptr;
+ data->pingpong = 0;
data->hit = 1;
data->bytes_req = bytes_req;
data->bytes_alloc = bytes_alloc;
@@ -117,6 +181,8 @@ static void insert_alloc_stat(unsigned long ptr,
rb_link_node(&data->node, parent, node);
rb_insert_color(&data->node, &root_alloc_stat);
}
+ data->call_site = call_site;
+ data->alloc_cpu = cpu;
}
static void insert_caller_stat(unsigned long call_site,
@@ -126,9 +192,6 @@ static void insert_caller_stat(unsigned long call_site,
struct rb_node *parent = NULL;
struct alloc_stat *data = NULL;
- if (!caller_flag)
- return;
-
while (*node) {
parent = *node;
data = rb_entry(*node, struct alloc_stat, node);
@@ -147,7 +210,10 @@ static void insert_caller_stat(unsigned long call_site,
data->bytes_alloc += bytes_req;
} else {
data = malloc(sizeof(*data));
+ if (!data)
+ die("malloc");
data->call_site = call_site;
+ data->pingpong = 0;
data->hit = 1;
data->bytes_req = bytes_req;
data->bytes_alloc = bytes_alloc;
@@ -159,34 +225,89 @@ static void insert_caller_stat(unsigned long call_site,
static void process_alloc_event(struct raw_event_sample *raw,
struct event *event,
- int cpu __used,
+ int cpu,
u64 timestamp __used,
struct thread *thread __used,
- int node __used)
+ int node)
{
unsigned long call_site;
unsigned long ptr;
int bytes_req;
int bytes_alloc;
+ int node1, node2;
ptr = raw_field_value(event, "ptr", raw->data);
call_site = raw_field_value(event, "call_site", raw->data);
bytes_req = raw_field_value(event, "bytes_req", raw->data);
bytes_alloc = raw_field_value(event, "bytes_alloc", raw->data);
- insert_alloc_stat(ptr, bytes_req, bytes_alloc);
+ insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
insert_caller_stat(call_site, bytes_req, bytes_alloc);
total_requested += bytes_req;
total_allocated += bytes_alloc;
+
+ if (node) {
+ node1 = cpunode_map[cpu];
+ node2 = raw_field_value(event, "node", raw->data);
+ if (node1 != node2)
+ nr_cross_allocs++;
+ }
+ nr_allocs++;
+}
+
+static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
+static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
+
+static struct alloc_stat *search_alloc_stat(unsigned long ptr,
+ unsigned long call_site,
+ struct rb_root *root,
+ sort_fn_t sort_fn)
+{
+ struct rb_node *node = root->rb_node;
+ struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
+
+ while (node) {
+ struct alloc_stat *data;
+ int cmp;
+
+ data = rb_entry(node, struct alloc_stat, node);
+
+ cmp = sort_fn(&key, data);
+ if (cmp < 0)
+ node = node->rb_left;
+ else if (cmp > 0)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
}
-static void process_free_event(struct raw_event_sample *raw __used,
- struct event *event __used,
- int cpu __used,
+static void process_free_event(struct raw_event_sample *raw,
+ struct event *event,
+ int cpu,
u64 timestamp __used,
struct thread *thread __used)
{
+ unsigned long ptr;
+ struct alloc_stat *s_alloc, *s_caller;
+
+ ptr = raw_field_value(event, "ptr", raw->data);
+
+ s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
+ if (!s_alloc)
+ return;
+
+ if (cpu != s_alloc->alloc_cpu) {
+ s_alloc->pingpong++;
+
+ s_caller = search_alloc_stat(0, s_alloc->call_site,
+ &root_caller_stat, callsite_cmp);
+ assert(s_caller);
+ s_caller->pingpong++;
+ }
+ s_alloc->alloc_cpu = -1;
}
static void
@@ -291,7 +412,7 @@ static int read_events(void)
register_idle_thread();
register_perf_file_handler(&file_handler);
- return mmap_dispatch_perf_file(&header, input_name, NULL, false, 0, 0,
+ return mmap_dispatch_perf_file(&header, input_name, 0, 0,
&cwdlen, &cwd);
}
@@ -307,10 +428,10 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
{
struct rb_node *next;
- printf("%.78s\n", graph_dotted_line);
- printf("%-28s|", is_caller ? "Callsite": "Alloc Ptr");
- printf("Total_alloc/Per | Total_req/Per | Hit | Frag\n");
- printf("%.78s\n", graph_dotted_line);
+ printf("%.102s\n", graph_dotted_line);
+ printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
+ printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
+ printf("%.102s\n", graph_dotted_line);
next = rb_first(root);
@@ -318,36 +439,39 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node);
struct symbol *sym = NULL;
- char bf[BUFSIZ];
+ char buf[BUFSIZ];
u64 addr;
if (is_caller) {
addr = data->call_site;
- sym = kernel_maps__find_symbol(addr, NULL, NULL);
+ if (!raw_ip)
+ sym = kernel_maps__find_function(addr, NULL, NULL);
} else
addr = data->ptr;
if (sym != NULL)
- snprintf(bf, sizeof(bf), "%s/%Lx", sym->name,
+ snprintf(buf, sizeof(buf), "%s+%Lx", sym->name,
addr - sym->start);
else
- snprintf(bf, sizeof(bf), "%#Lx", addr);
+ snprintf(buf, sizeof(buf), "%#Lx", addr);
+ printf(" %-34s |", buf);
- printf("%-28s|%8llu/%-6lu |%8llu/%-6lu|%6lu|%8.3f%%\n",
- bf, (unsigned long long)data->bytes_alloc,
+ printf(" %9llu/%-5lu | %9llu/%-5lu | %6lu | %8lu | %6.3f%%\n",
+ (unsigned long long)data->bytes_alloc,
(unsigned long)data->bytes_alloc / data->hit,
(unsigned long long)data->bytes_req,
(unsigned long)data->bytes_req / data->hit,
(unsigned long)data->hit,
+ (unsigned long)data->pingpong,
fragmentation(data->bytes_req, data->bytes_alloc));
next = rb_next(next);
}
if (n_lines == -1)
- printf(" ... | ... | ... | ... | ... \n");
+ printf(" ... | ... | ... | ... | ... | ... \n");
- printf(" ------------------------------------------------------------------------------\n");
+ printf("%.102s\n", graph_dotted_line);
}
static void print_summary(void)
@@ -359,6 +483,7 @@ static void print_summary(void)
total_allocated - total_requested);
printf("Internal fragmentation: %f%%\n",
fragmentation(total_requested, total_allocated));
+ printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
}
static void print_result(void)
@@ -370,20 +495,34 @@ static void print_result(void)
print_summary();
}
+struct sort_dimension {
+ const char name[20];
+ sort_fn_t cmp;
+ struct list_head list;
+};
+
+static LIST_HEAD(caller_sort);
+static LIST_HEAD(alloc_sort);
+
static void sort_insert(struct rb_root *root, struct alloc_stat *data,
- sort_fn_t sort_fn)
+ struct list_head *sort_list)
{
struct rb_node **new = &(root->rb_node);
struct rb_node *parent = NULL;
+ struct sort_dimension *sort;
while (*new) {
struct alloc_stat *this;
- int cmp;
+ int cmp = 0;
this = rb_entry(*new, struct alloc_stat, node);
parent = *new;
- cmp = sort_fn(data, this);
+ list_for_each_entry(sort, sort_list, list) {
+ cmp = sort->cmp(data, this);
+ if (cmp)
+ break;
+ }
if (cmp > 0)
new = &((*new)->rb_left);
@@ -396,7 +535,7 @@ static void sort_insert(struct rb_root *root, struct alloc_stat *data,
}
static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
- sort_fn_t sort_fn)
+ struct list_head *sort_list)
{
struct rb_node *node;
struct alloc_stat *data;
@@ -408,14 +547,14 @@ static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
rb_erase(node, root);
data = rb_entry(node, struct alloc_stat, node);
- sort_insert(root_sorted, data, sort_fn);
+ sort_insert(root_sorted, data, sort_list);
}
}
static void sort_result(void)
{
- __sort_result(&root_alloc_stat, &root_alloc_sorted, alloc_sort_fn);
- __sort_result(&root_caller_stat, &root_caller_sorted, caller_sort_fn);
+ __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
+ __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
}
static int __cmd_kmem(void)
@@ -433,7 +572,6 @@ static const char * const kmem_usage[] = {
NULL
};
-
static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
{
if (l->ptr < r->ptr)
@@ -443,6 +581,11 @@ static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
return 0;
}
+static struct sort_dimension ptr_sort_dimension = {
+ .name = "ptr",
+ .cmp = ptr_cmp,
+};
+
static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
{
if (l->call_site < r->call_site)
@@ -452,6 +595,11 @@ static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
return 0;
}
+static struct sort_dimension callsite_sort_dimension = {
+ .name = "callsite",
+ .cmp = callsite_cmp,
+};
+
static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
{
if (l->hit < r->hit)
@@ -461,6 +609,11 @@ static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
return 0;
}
+static struct sort_dimension hit_sort_dimension = {
+ .name = "hit",
+ .cmp = hit_cmp,
+};
+
static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
{
if (l->bytes_alloc < r->bytes_alloc)
@@ -470,6 +623,11 @@ static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
return 0;
}
+static struct sort_dimension bytes_sort_dimension = {
+ .name = "bytes",
+ .cmp = bytes_cmp,
+};
+
static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
{
double x, y;
@@ -484,31 +642,88 @@ static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
return 0;
}
+static struct sort_dimension frag_sort_dimension = {
+ .name = "frag",
+ .cmp = frag_cmp,
+};
+
+static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
+{
+ if (l->pingpong < r->pingpong)
+ return -1;
+ else if (l->pingpong > r->pingpong)
+ return 1;
+ return 0;
+}
+
+static struct sort_dimension pingpong_sort_dimension = {
+ .name = "pingpong",
+ .cmp = pingpong_cmp,
+};
+
+static struct sort_dimension *avail_sorts[] = {
+ &ptr_sort_dimension,
+ &callsite_sort_dimension,
+ &hit_sort_dimension,
+ &bytes_sort_dimension,
+ &frag_sort_dimension,
+ &pingpong_sort_dimension,
+};
+
+#define NUM_AVAIL_SORTS \
+ (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
+
+static int sort_dimension__add(const char *tok, struct list_head *list)
+{
+ struct sort_dimension *sort;
+ int i;
+
+ for (i = 0; i < NUM_AVAIL_SORTS; i++) {
+ if (!strcmp(avail_sorts[i]->name, tok)) {
+ sort = malloc(sizeof(*sort));
+ if (!sort)
+ die("malloc");
+ memcpy(sort, avail_sorts[i], sizeof(*sort));
+ list_add_tail(&sort->list, list);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int setup_sorting(struct list_head *sort_list, const char *arg)
+{
+ char *tok;
+ char *str = strdup(arg);
+
+ if (!str)
+ die("strdup");
+
+ while (true) {
+ tok = strsep(&str, ",");
+ if (!tok)
+ break;
+ if (sort_dimension__add(tok, sort_list) < 0) {
+ error("Unknown --sort key: '%s'", tok);
+ return -1;
+ }
+ }
+
+ free(str);
+ return 0;
+}
+
static int parse_sort_opt(const struct option *opt __used,
const char *arg, int unset __used)
{
- sort_fn_t sort_fn;
-
if (!arg)
return -1;
- if (strcmp(arg, "ptr") == 0)
- sort_fn = ptr_cmp;
- else if (strcmp(arg, "call_site") == 0)
- sort_fn = callsite_cmp;
- else if (strcmp(arg, "hit") == 0)
- sort_fn = hit_cmp;
- else if (strcmp(arg, "bytes") == 0)
- sort_fn = bytes_cmp;
- else if (strcmp(arg, "frag") == 0)
- sort_fn = frag_cmp;
- else
- return -1;
-
if (caller_flag > alloc_flag)
- caller_sort_fn = sort_fn;
+ return setup_sorting(&caller_sort, arg);
else
- alloc_sort_fn = sort_fn;
+ return setup_sorting(&alloc_sort, arg);
return 0;
}
@@ -552,12 +767,13 @@ static const struct option kmem_options[] = {
OPT_CALLBACK(0, "stat", NULL, "<alloc>|<caller>",
"stat selector, Pass 'alloc' or 'caller'.",
parse_stat_opt),
- OPT_CALLBACK('s', "sort", NULL, "key",
- "sort by key: ptr, call_site, hit, bytes, frag",
+ OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
+ "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
parse_sort_opt),
OPT_CALLBACK('l', "line", NULL, "num",
"show n lins",
parse_line_opt),
+ OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
OPT_END()
};
@@ -604,10 +820,12 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __used)
else if (argc)
usage_with_options(kmem_usage, kmem_options);
- if (!alloc_sort_fn)
- alloc_sort_fn = bytes_cmp;
- if (!caller_sort_fn)
- caller_sort_fn = bytes_cmp;
+ if (list_empty(&caller_sort))
+ setup_sorting(&caller_sort, default_sort_order);
+ if (list_empty(&alloc_sort))
+ setup_sorting(&alloc_sort, default_sort_order);
+
+ setup_cpunode_map();
return __cmd_kmem();
}
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index d78a3d94549..a2f6daf01ec 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -309,9 +309,9 @@ static int synthesize_probe_event(struct probe_point *pp)
{
char *buf;
int i, len, ret;
- pp->probes[0] = buf = (char *)calloc(MAX_CMDLEN, sizeof(char));
+ pp->probes[0] = buf = zalloc(MAX_CMDLEN);
if (!buf)
- die("Failed to allocate memory by calloc.");
+ die("Failed to allocate memory by zalloc.");
ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
if (ret <= 0 || ret >= MAX_CMDLEN)
goto error;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index fe474b7f8ad..e4b1004e76e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -30,6 +30,7 @@
#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
+#include "util/process_events.h"
static char const *input_name = "perf.data";
@@ -38,7 +39,6 @@ static char *dso_list_str, *comm_list_str, *sym_list_str,
static struct strlist *dso_list, *comm_list, *sym_list;
static int force;
-static bool use_modules;
static int full_paths;
static int show_nr_samples;
@@ -52,15 +52,13 @@ static char *pretty_printing_style = default_pretty_printing_style;
static int exclude_other = 1;
static char callchain_default_opt[] = "fractal,0.5";
-const char *vmlinux_name;
-
-static char *cwd;
-static int cwdlen;
static struct perf_header *header;
static u64 sample_type;
+struct symbol_conf symbol_conf;
+
static size_t
callchain__fprintf_left_margin(FILE *fp, int left_margin)
@@ -450,14 +448,14 @@ got_map:
* trick of looking in the whole kernel symbol list.
*/
if ((long long)ip < 0)
- return kernel_maps__find_symbol(ip, mapp, NULL);
+ return kernel_maps__find_function(ip, mapp, NULL);
}
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
*ipp = ip;
- return map ? map__find_symbol(map, ip, NULL) : NULL;
+ return map ? map__find_function(map, ip, NULL) : NULL;
}
static int call__match(struct symbol *sym)
@@ -497,7 +495,7 @@ static struct symbol **resolve_callchain(struct thread *thread,
case PERF_CONTEXT_HV:
break;
case PERF_CONTEXT_KERNEL:
- sym = kernel_maps__find_symbol(ip, NULL, NULL);
+ sym = kernel_maps__find_function(ip, NULL, NULL);
break;
default:
sym = resolve_symbol(thread, NULL, &ip);
@@ -717,7 +715,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (cpumode == PERF_RECORD_MISC_KERNEL) {
level = 'k';
- sym = kernel_maps__find_symbol(ip, &map, NULL);
+ sym = kernel_maps__find_function(ip, &map, NULL);
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
} else if (cpumode == PERF_RECORD_MISC_USER) {
@@ -751,33 +749,6 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
-{
- struct map *map = map__new(&event->mmap, cwd, cwdlen);
- struct thread *thread = threads__findnew(event->mmap.pid);
-
- dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->mmap.pid,
- event->mmap.tid,
- (void *)(long)event->mmap.start,
- (void *)(long)event->mmap.len,
- (void *)(long)event->mmap.pgoff,
- event->mmap.filename);
-
- if (thread == NULL || map == NULL) {
- dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
- return 0;
- }
-
- thread__insert_map(thread, map);
- total_mmap++;
-
- return 0;
-}
-
-static int
process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread = threads__findnew(event->comm.pid);
@@ -798,38 +769,6 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_task_event(event_t *event, unsigned long offset, unsigned long head)
-{
- struct thread *thread = threads__findnew(event->fork.pid);
- struct thread *parent = threads__findnew(event->fork.ppid);
-
- dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT",
- event->fork.pid, event->fork.tid,
- event->fork.ppid, event->fork.ptid);
-
- /*
- * A thread clone will have the same PID for both
- * parent and child.
- */
- if (thread == parent)
- return 0;
-
- if (event->header.type == PERF_RECORD_EXIT)
- return 0;
-
- if (!thread || !parent || thread__fork(thread, parent)) {
- dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
- return -1;
- }
- total_fork++;
-
- return 0;
-}
-
-static int
process_lost_event(event_t *event, unsigned long offset, unsigned long head)
{
dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n",
@@ -926,8 +865,7 @@ static int __cmd_report(void)
register_perf_file_handler(&file_handler);
- ret = mmap_dispatch_perf_file(&header, input_name, vmlinux_name,
- !vmlinux_name, force,
+ ret = mmap_dispatch_perf_file(&header, input_name, force,
full_paths, &cwdlen, &cwd);
if (ret)
return ret;
@@ -1024,9 +962,10 @@ static const struct option options[] = {
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
- OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
- OPT_BOOLEAN('m', "modules", &use_modules,
+ OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
"Show a column with the number of samples"),
@@ -1096,7 +1035,8 @@ static void setup_list(struct strlist **list, const char *list_str,
int cmd_report(int argc, const char **argv, const char *prefix __used)
{
- symbol__init(0);
+ if (symbol__init(&symbol_conf) < 0)
+ return -1;
argc = parse_options(argc, argv, options, report_usage, 0);
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 260f57a72ee..19eb708a706 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -225,7 +225,7 @@ static void calibrate_sleep_measurement_overhead(void)
static struct sched_atom *
get_new_event(struct task_desc *task, u64 timestamp)
{
- struct sched_atom *event = calloc(1, sizeof(*event));
+ struct sched_atom *event = zalloc(sizeof(*event));
unsigned long idx = task->nr_events;
size_t size;
@@ -293,7 +293,7 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
return;
}
- wakee_event->wait_sem = calloc(1, sizeof(*wakee_event->wait_sem));
+ wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem));
sem_init(wakee_event->wait_sem, 0, 0);
wakee_event->specific_wait = 1;
event->wait_sem = wakee_event->wait_sem;
@@ -323,7 +323,7 @@ static struct task_desc *register_pid(unsigned long pid, const char *comm)
if (task)
return task;
- task = calloc(1, sizeof(*task));
+ task = zalloc(sizeof(*task));
task->pid = pid;
task->nr = nr_tasks;
strcpy(task->comm, comm);
@@ -962,9 +962,7 @@ __thread_latency_insert(struct rb_root *root, struct work_atoms *data,
static void thread_atoms_insert(struct thread *thread)
{
- struct work_atoms *atoms;
-
- atoms = calloc(sizeof(*atoms), 1);
+ struct work_atoms *atoms = zalloc(sizeof(*atoms));
if (!atoms)
die("No memory");
@@ -996,9 +994,7 @@ add_sched_out_event(struct work_atoms *atoms,
char run_state,
u64 timestamp)
{
- struct work_atom *atom;
-
- atom = calloc(sizeof(*atom), 1);
+ struct work_atom *atom = zalloc(sizeof(*atom));
if (!atom)
die("Non memory");
@@ -1718,7 +1714,7 @@ static int read_events(void)
register_idle_thread();
register_perf_file_handler(&file_handler);
- return mmap_dispatch_perf_file(&header, input_name, NULL, false, 0, 0,
+ return mmap_dispatch_perf_file(&header, input_name, 0, 0,
&cwdlen, &cwd);
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 6a5de90e9b8..ded6cf65ad9 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -79,7 +79,7 @@ static int dump_symtab = 0;
static bool hide_kernel_symbols = false;
static bool hide_user_symbols = false;
static struct winsize winsize;
-const char *vmlinux_name;
+struct symbol_conf symbol_conf;
/*
* Source
@@ -128,7 +128,7 @@ struct sym_entry {
static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
{
- return ((void *)self) + symbol__priv_size;
+ return ((void *)self) + symbol_conf.priv_size;
}
static void get_term_dimensions(struct winsize *ws)
@@ -181,7 +181,7 @@ static void parse_source(struct sym_entry *syme)
return;
if (syme->src == NULL) {
- syme->src = calloc(1, sizeof(*source));
+ syme->src = zalloc(sizeof(*source));
if (syme->src == NULL)
return;
pthread_mutex_init(&syme->src->lock, NULL);
@@ -451,9 +451,8 @@ static void print_sym_table(void)
struct sym_entry *syme, *n;
struct rb_root tmp = RB_ROOT;
struct rb_node *nd;
- int sym_width = 0, dso_width = 0;
+ int sym_width = 0, dso_width = 0, max_dso_width;
const int win_width = winsize.ws_col - 1;
- struct dso *unique_dso = NULL, *first_dso = NULL;
samples = userspace_samples = 0;
@@ -539,11 +538,6 @@ static void print_sym_table(void)
(int)syme->snap_count < count_filter)
continue;
- if (first_dso == NULL)
- unique_dso = first_dso = syme->map->dso;
- else if (syme->map->dso != first_dso)
- unique_dso = NULL;
-
if (syme->map->dso->long_name_len > dso_width)
dso_width = syme->map->dso->long_name_len;
@@ -553,14 +547,10 @@ static void print_sym_table(void)
printed = 0;
- if (unique_dso)
- printf("DSO: %s\n", unique_dso->long_name);
- else {
- int max_dso_width = winsize.ws_col - sym_width - 29;
- if (dso_width > max_dso_width)
- dso_width = max_dso_width;
- putchar('\n');
- }
+ max_dso_width = winsize.ws_col - sym_width - 29;
+ if (dso_width > max_dso_width)
+ dso_width = max_dso_width;
+ putchar('\n');
if (nr_counters == 1)
printf(" samples pcnt");
else
@@ -568,17 +558,13 @@ static void print_sym_table(void)
if (verbose)
printf(" RIP ");
- printf(" %-*.*s", sym_width, sym_width, "function");
- if (!unique_dso)
- printf(" DSO");
- putchar('\n');
+ printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
printf(" %s _______ _____",
nr_counters == 1 ? " " : "______");
if (verbose)
printf(" ________________");
printf(" %-*.*s", sym_width, sym_width, graph_line);
- if (!unique_dso)
- printf(" %-*.*s", dso_width, dso_width, graph_line);
+ printf(" %-*.*s", dso_width, dso_width, graph_line);
puts("\n");
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
@@ -603,12 +589,10 @@ static void print_sym_table(void)
if (verbose)
printf(" %016llx", sym->start);
printf(" %-*.*s", sym_width, sym_width, sym->name);
- if (!unique_dso)
- printf(" %-*.*s", dso_width, dso_width,
- dso_width >= syme->map->dso->long_name_len ?
- syme->map->dso->long_name :
- syme->map->dso->short_name);
- printf("\n");
+ printf(" %-*.*s\n", dso_width, dso_width,
+ dso_width >= syme->map->dso->long_name_len ?
+ syme->map->dso->long_name :
+ syme->map->dso->short_name);
}
}
@@ -711,7 +695,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
- if (vmlinux_name) {
+ if (symbol_conf.vmlinux_name) {
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
fprintf(stdout, "\t[S] stop annotation.\n");
@@ -748,7 +732,7 @@ static int key_mapped(int c)
case 'F':
case 's':
case 'S':
- return vmlinux_name ? 1 : 0;
+ return symbol_conf.vmlinux_name ? 1 : 0;
default:
break;
}
@@ -964,7 +948,7 @@ static void event__process_sample(const event_t *self, int counter)
map = thread__find_map(thread, ip);
if (map != NULL) {
ip = map->map_ip(map, ip);
- sym = map__find_symbol(map, ip, symbol_filter);
+ sym = map__find_function(map, ip, symbol_filter);
if (sym == NULL)
return;
userspace_samples++;
@@ -984,7 +968,7 @@ static void event__process_sample(const event_t *self, int counter)
if (hide_kernel_symbols)
return;
- sym = kernel_maps__find_symbol(ip, &map, symbol_filter);
+ sym = kernel_maps__find_function(ip, &map, symbol_filter);
if (sym == NULL)
return;
break;
@@ -1277,7 +1261,8 @@ static const struct option options[] = {
"system-wide collection from all CPUs"),
OPT_INTEGER('C', "CPU", &profile_cpu,
"CPU to profile on"),
- OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
"hide kernel symbols"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages,
@@ -1311,7 +1296,7 @@ static const struct option options[] = {
int cmd_top(int argc, const char **argv, const char *prefix __used)
{
- int counter, err;
+ int counter;
page_size = sysconf(_SC_PAGE_SIZE);
@@ -1329,15 +1314,16 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
if (!nr_counters)
nr_counters = 1;
- symbol__init(sizeof(struct sym_entry) +
- (nr_counters + 1) * sizeof(unsigned long));
+ symbol_conf.priv_size = (sizeof(struct sym_entry) +
+ (nr_counters + 1) * sizeof(unsigned long));
+ if (symbol_conf.vmlinux_name == NULL)
+ symbol_conf.try_vmlinux_path = true;
+ if (symbol__init(&symbol_conf) < 0)
+ return -1;
if (delay_secs < 1)
delay_secs = 1;
- err = kernel_maps__init(vmlinux_name, !vmlinux_name, true);
- if (err < 0)
- return err;
parse_source(sym_filter_entry);
/*
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index b71198e5dc1..75972fd073d 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -131,7 +131,7 @@ static int __cmd_trace(void)
register_idle_thread();
register_perf_file_handler(&file_handler);
- return mmap_dispatch_perf_file(&header, input_name, NULL, false,
+ return mmap_dispatch_perf_file(&header, input_name,
0, 0, &cwdlen, &cwd);
}
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index d3a6e18e4a5..02b09ea17a3 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -14,3 +14,4 @@ perf-timechart mainporcelain common
perf-top mainporcelain common
perf-trace mainporcelain common
perf-probe mainporcelain common
+perf-kmem mainporcelain common
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c
index f318d19b256..b238462b898 100644
--- a/tools/perf/util/data_map.c
+++ b/tools/perf/util/data_map.c
@@ -101,8 +101,6 @@ out:
int mmap_dispatch_perf_file(struct perf_header **pheader,
const char *input_name,
- const char *vmlinux_name,
- bool try_vmlinux_path,
int force,
int full_paths,
int *cwdlen,
@@ -172,12 +170,6 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
curr_handler->sample_type_check(sample_type) < 0)
goto out_delete;
- err = -ENOMEM;
- if (kernel_maps__init(vmlinux_name, try_vmlinux_path, true) < 0) {
- pr_err("failed to setup the kernel maps to resolve symbols\n");
- goto out_delete;
- }
-
if (!full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
pr_err("failed to get the current directory\n");
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h
index 3f0d21b3819..ae036ecd762 100644
--- a/tools/perf/util/data_map.h
+++ b/tools/perf/util/data_map.h
@@ -23,8 +23,6 @@ struct perf_file_handler {
void register_perf_file_handler(struct perf_file_handler *handler);
int mmap_dispatch_perf_file(struct perf_header **pheader,
const char *input_name,
- const char *vmlinux_name,
- bool try_vmlinux_path,
int force,
int full_paths,
int *cwdlen,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index f1e39261265..882a9531db9 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -119,9 +119,10 @@ 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);
-struct symbol *map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter);
-void map__fixup_start(struct map *self);
-void map__fixup_end(struct map *self);
+struct symbol *map__find_function(struct map *self, u64 ip,
+ symbol_filter_t filter);
+void map__fixup_start(struct map *self, struct rb_root *symbols);
+void map__fixup_end(struct map *self, struct rb_root *symbols);
int event__synthesize_thread(pid_t pid, int (*process)(event_t *event));
void event__synthesize_threads(int (*process)(event_t *event));
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 1332f8ec04a..4b586569bb0 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -63,7 +63,7 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
*/
struct perf_header *perf_header__new(void)
{
- struct perf_header *self = calloc(sizeof(*self), 1);
+ struct perf_header *self = zalloc(sizeof(*self));
if (self != NULL) {
self->size = 1;
@@ -253,12 +253,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
buildid_sec = &feat_sec[idx++];
- /*
- * Read the kernel buildid nad the list of loaded modules with
- * its build_ids:
- */
- kernel_maps__init(NULL, false, true);
-
/* Write build-ids */
buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
err = dsos__write_buildid_table(fd);
diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h
new file mode 100644
index 00000000000..7fcc6810adc
--- /dev/null
+++ b/tools/perf/util/include/asm/bug.h
@@ -0,0 +1,22 @@
+#ifndef _PERF_ASM_GENERIC_BUG_H
+#define _PERF_ASM_GENERIC_BUG_H
+
+#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0)
+
+#define WARN(condition, format...) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) \
+ __WARN_printf(format); \
+ unlikely(__ret_warn_on); \
+})
+
+#define WARN_ONCE(condition, format...) ({ \
+ static int __warned; \
+ int __ret_warn_once = !!(condition); \
+ \
+ if (unlikely(__ret_warn_once)) \
+ if (WARN(!__warned, format)) \
+ __warned = 1; \
+ unlikely(__ret_warn_once); \
+})
+#endif
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index ace57c36d1d..8d63116e943 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -7,6 +7,8 @@
#define CONFIG_GENERIC_FIND_FIRST_BIT
#include "../../../../include/linux/bitops.h"
+#undef __KERNEL__
+
static inline void set_bit(int nr, unsigned long *addr)
{
addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 09412321a80..41c5c4a2001 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -80,18 +80,18 @@ void map__delete(struct map *self)
free(self);
}
-void map__fixup_start(struct map *self)
+void map__fixup_start(struct map *self, struct rb_root *symbols)
{
- struct rb_node *nd = rb_first(&self->dso->syms);
+ 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)
+void map__fixup_end(struct map *self, struct rb_root *symbols)
{
- struct rb_node *nd = rb_last(&self->dso->syms);
+ struct rb_node *nd = rb_last(symbols);
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
self->end = sym->end;
@@ -100,8 +100,8 @@ void map__fixup_end(struct map *self)
#define DSO__DELETED "(deleted)"
-struct symbol *
-map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter)
+struct symbol *map__find_function(struct map *self, u64 ip,
+ symbol_filter_t filter)
{
if (!self->dso->loaded) {
int nr = dso__load(self->dso, self, filter);
@@ -136,7 +136,7 @@ map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter)
}
}
- return self->dso->find_symbol(self->dso, ip);
+ return self->dso->find_function(self->dso, ip);
}
struct map *map__clone(struct map *self)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 07002746927..9e5dbd66d34 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -197,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
if (id == config) {
closedir(evt_dir);
closedir(sys_dir);
- path = calloc(1, sizeof(path));
+ path = zalloc(sizeof(path));
path->system = malloc(MAX_EVENT_LENGTH);
if (!path->system) {
free(path);
diff --git a/tools/perf/util/process_event.c b/tools/perf/util/process_event.c
new file mode 100644
index 00000000000..a970789581a
--- /dev/null
+++ b/tools/perf/util/process_event.c
@@ -0,0 +1,53 @@
+#include "process_event.h"
+
+char *cwd;
+int cwdlen;
+
+int
+process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
+{
+ struct map *map = map__new(&event->mmap, cwd, cwdlen);
+ struct thread *thread = threads__findnew(event->mmap.pid);
+
+ dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
+ (void *)(offset + head),
+ (void *)(long)(event->header.size),
+ event->mmap.pid,
+ event->mmap.tid,
+ (void *)(long)event->mmap.start,
+ (void *)(long)event->mmap.len,
+ (void *)(long)event->mmap.pgoff,
+ event->mmap.filename);
+
+ if (thread == NULL || map == NULL) {
+ dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
+ return 0;
+ }
+
+ thread__insert_map(thread, map);
+ total_mmap++;
+
+ return 0;
+
+}
+
+int
+process_comm_event(event_t *event, unsigned long offset, unsigned long head)
+{
+ struct thread *thread = threads__findnew(event->comm.pid);
+
+ dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
+ (void *)(offset + head),
+ (void *)(long)(event->header.size),
+ event->comm.comm, event->comm.pid);
+
+ if (thread == NULL ||
+ thread__set_comm_adjust(thread, event->comm.comm)) {
+ dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
+ return -1;
+ }
+ total_comm++;
+
+ return 0;
+}
+
diff --git a/tools/perf/util/process_event.h b/tools/perf/util/process_event.h
new file mode 100644
index 00000000000..6f68c69736c
--- /dev/null
+++ b/tools/perf/util/process_event.h
@@ -0,0 +1,29 @@
+#ifndef __PROCESS_EVENT_H
+#define __PROCESS_EVENT_H
+
+#include "../builtin.h"
+#include "util.h"
+
+#include "color.h"
+#include <linux/list.h>
+#include "cache.h"
+#include <linux/rbtree.h>
+#include "symbol.h"
+#include "string.h"
+
+#include "../perf.h"
+#include "debug.h"
+
+#include "parse-options.h"
+#include "parse-events.h"
+
+#include "thread.h"
+#include "sort.h"
+#include "hist.h"
+
+extern char *cwd;
+extern int cwdlen;
+extern int process_mmap_event(event_t *, unsigned long, unsigned long);
+extern int process_comm_event(event_t *, unsigned long , unsigned long);
+
+#endif /* __PROCESS_H */
diff --git a/tools/perf/util/process_events.c b/tools/perf/util/process_events.c
new file mode 100644
index 00000000000..a9204363efd
--- /dev/null
+++ b/tools/perf/util/process_events.c
@@ -0,0 +1,64 @@
+#include "process_events.h"
+
+char *cwd;
+int cwdlen;
+
+int
+process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
+{
+ struct map *map = map__new(&event->mmap, cwd, cwdlen);
+ struct thread *thread = threads__findnew(event->mmap.pid);
+
+ dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
+ (void *)(offset + head),
+ (void *)(long)(event->header.size),
+ event->mmap.pid,
+ event->mmap.tid,
+ (void *)(long)event->mmap.start,
+ (void *)(long)event->mmap.len,
+ (void *)(long)event->mmap.pgoff,
+ event->mmap.filename);
+
+ if (thread == NULL || map == NULL) {
+ dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
+ return 0;
+ }
+
+ thread__insert_map(thread, map);
+ total_mmap++;
+
+ return 0;
+}
+
+int
+process_task_event(event_t *event, unsigned long offset, unsigned long head)
+{
+ struct thread *thread = threads__findnew(event->fork.pid);
+ struct thread *parent = threads__findnew(event->fork.ppid);
+
+ dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
+ (void *)(offset + head),
+ (void *)(long)(event->header.size),
+ event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT",
+ event->fork.pid, event->fork.tid,
+ event->fork.ppid, event->fork.ptid);
+
+ /*
+ * A thread clone will have the same PID for both
+ * parent and child.
+ */
+ if (thread == parent)
+ return 0;
+
+ if (event->header.type == PERF_RECORD_EXIT)
+ return 0;
+
+ if (!thread || !parent || thread__fork(thread, parent)) {
+ dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
+ return -1;
+ }
+ total_fork++;
+
+ return 0;
+}
+
diff --git a/tools/perf/util/process_events.h b/tools/perf/util/process_events.h
new file mode 100644
index 00000000000..73d092f8328
--- /dev/null
+++ b/tools/perf/util/process_events.h
@@ -0,0 +1,35 @@
+#ifndef __PROCESS_EVENTS_H
+#define __PROCESS_EVENTS_H
+
+#include "../builtin.h"
+
+#include "util.h"
+#include "color.h"
+#include <linux/list.h>
+#include "cache.h"
+#include <linux/rbtree.h>
+#include "symbol.h"
+#include "string.h"
+#include "callchain.h"
+#include "strlist.h"
+#include "values.h"
+
+#include "../perf.h"
+#include "debug.h"
+#include "header.h"
+
+#include "parse-options.h"
+#include "parse-events.h"
+
+#include "data_map.h"
+#include "thread.h"
+#include "sort.h"
+#include "hist.h"
+
+extern char *cwd;
+extern int cwdlen;
+
+extern int process_mmap_event(event_t *, unsigned long , unsigned long);
+extern int process_task_event(event_t *, unsigned long, unsigned long);
+
+#endif /* __PROCESS_EVENTS_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 44d81d5ae8c..4ed379b915f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -6,6 +6,7 @@
#include "debug.h"
+#include <asm/bug.h>
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
@@ -37,11 +38,16 @@ unsigned int symbol__priv_size;
static int vmlinux_path__nr_entries;
static char **vmlinux_path;
+static struct symbol_conf symbol_conf__defaults = {
+ .use_modules = true,
+ .try_vmlinux_path = true,
+};
+
static struct rb_root kernel_maps;
-static void dso__fixup_sym_end(struct dso *self)
+static void symbols__fixup_end(struct rb_root *self)
{
- struct rb_node *nd, *prevnd = rb_first(&self->syms);
+ struct rb_node *nd, *prevnd = rb_first(self);
struct symbol *curr, *prev;
if (prevnd == NULL)
@@ -88,15 +94,14 @@ static void kernel_maps__fixup_end(void)
static struct symbol *symbol__new(u64 start, u64 len, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *self = calloc(1, (symbol__priv_size +
- sizeof(*self) + namelen));
- if (!self)
+ struct symbol *self = zalloc(symbol__priv_size +
+ sizeof(*self) + namelen);
+ if (self == NULL)
return NULL;
- if (symbol__priv_size) {
- memset(self, 0, symbol__priv_size);
+ if (symbol__priv_size)
self = ((void *)self) + symbol__priv_size;
- }
+
self->start = start;
self->end = len ? start + len - 1 : start;
@@ -139,8 +144,8 @@ struct dso *dso__new(const char *name)
strcpy(self->name, name);
dso__set_long_name(self, self->name);
self->short_name = self->name;
- self->syms = RB_ROOT;
- self->find_symbol = dso__find_symbol;
+ self->functions = RB_ROOT;
+ self->find_function = dso__find_function;
self->slen_calculated = 0;
self->origin = DSO__ORIG_NOT_FOUND;
self->loaded = 0;
@@ -150,22 +155,22 @@ struct dso *dso__new(const char *name)
return self;
}
-static void dso__delete_symbols(struct dso *self)
+static void symbols__delete(struct rb_root *self)
{
struct symbol *pos;
- struct rb_node *next = rb_first(&self->syms);
+ 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->syms);
+ rb_erase(&pos->rb_node, self);
symbol__delete(pos);
}
}
void dso__delete(struct dso *self)
{
- dso__delete_symbols(self);
+ symbols__delete(&self->functions);
if (self->long_name != self->name)
free(self->long_name);
free(self);
@@ -177,9 +182,9 @@ void dso__set_build_id(struct dso *self, void *build_id)
self->has_build_id = 1;
}
-static void dso__insert_symbol(struct dso *self, struct symbol *sym)
+static void symbols__insert(struct rb_root *self, struct symbol *sym)
{
- struct rb_node **p = &self->syms.rb_node;
+ struct rb_node **p = &self->rb_node;
struct rb_node *parent = NULL;
const u64 ip = sym->start;
struct symbol *s;
@@ -193,17 +198,17 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym)
p = &(*p)->rb_right;
}
rb_link_node(&sym->rb_node, parent, p);
- rb_insert_color(&sym->rb_node, &self->syms);
+ rb_insert_color(&sym->rb_node, self);
}
-struct symbol *dso__find_symbol(struct dso *self, u64 ip)
+static struct symbol *symbols__find(struct rb_root *self, u64 ip)
{
struct rb_node *n;
if (self == NULL)
return NULL;
- n = self->syms.rb_node;
+ n = self->rb_node;
while (n) {
struct symbol *s = rb_entry(n, struct symbol, rb_node);
@@ -219,6 +224,11 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip)
return NULL;
}
+struct symbol *dso__find_function(struct dso *self, u64 ip)
+{
+ return symbols__find(&self->functions, ip);
+}
+
int build_id__sprintf(u8 *self, int len, char *bf)
{
char *bid = bf;
@@ -248,9 +258,9 @@ size_t dso__fprintf(struct dso *self, FILE *fp)
size_t ret = fprintf(fp, "dso: %s (", self->short_name);
ret += dso__fprintf_buildid(self, fp);
- ret += fprintf(fp, ")\n");
+ ret += fprintf(fp, ")\nFunctions:\n");
- for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&self->functions); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
ret += symbol__fprintf(pos, fp);
}
@@ -315,7 +325,7 @@ static int kernel_maps__load_all_kallsyms(void)
* kernel_maps__split_kallsyms, when we have split the
* maps per module
*/
- dso__insert_symbol(kernel_map->dso, sym);
+ symbols__insert(&kernel_map->dso->functions, sym);
}
free(line);
@@ -339,7 +349,7 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter)
struct map *map = kernel_map;
struct symbol *pos;
int count = 0;
- struct rb_node *next = rb_first(&kernel_map->dso->syms);
+ struct rb_node *next = rb_first(&kernel_map->dso->functions);
int kernel_range = 0;
while (next) {
@@ -389,12 +399,13 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter)
}
if (filter && filter(map, pos)) {
- rb_erase(&pos->rb_node, &kernel_map->dso->syms);
+ rb_erase(&pos->rb_node, &kernel_map->dso->functions);
symbol__delete(pos);
} else {
if (map != kernel_map) {
- rb_erase(&pos->rb_node, &kernel_map->dso->syms);
- dso__insert_symbol(map->dso, pos);
+ rb_erase(&pos->rb_node,
+ &kernel_map->dso->functions);
+ symbols__insert(&map->dso->functions, pos);
}
count++;
}
@@ -409,7 +420,7 @@ static int kernel_maps__load_kallsyms(symbol_filter_t filter)
if (kernel_maps__load_all_kallsyms())
return -1;
- dso__fixup_sym_end(kernel_map->dso);
+ symbols__fixup_end(&kernel_map->dso->functions);
kernel_map->dso->origin = DSO__ORIG_KERNEL;
return kernel_maps__split_kallsyms(filter);
@@ -480,7 +491,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
if (filter && filter(map, sym))
symbol__delete(sym);
else {
- dso__insert_symbol(self, sym);
+ symbols__insert(&self->functions, sym);
nr_syms++;
}
}
@@ -678,7 +689,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
if (filter && filter(map, f))
symbol__delete(f);
else {
- dso__insert_symbol(self, f);
+ symbols__insert(&self->functions, f);
++nr;
}
}
@@ -700,7 +711,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
if (filter && filter(map, f))
symbol__delete(f);
else {
- dso__insert_symbol(self, f);
+ symbols__insert(&self->functions, f);
++nr;
}
}
@@ -874,7 +885,7 @@ new_symbol:
if (filter && filter(curr_map, f))
symbol__delete(f);
else {
- dso__insert_symbol(curr_dso, f);
+ symbols__insert(&curr_dso->functions, f);
nr++;
}
}
@@ -883,7 +894,7 @@ new_symbol:
* For misannotated, zeroed, ASM function sizes.
*/
if (nr > 0)
- dso__fixup_sym_end(self);
+ symbols__fixup_end(&self->functions);
err = nr;
out_elf_end:
elf_end(elf);
@@ -1155,8 +1166,8 @@ static void kernel_maps__insert(struct map *map)
maps__insert(&kernel_maps, map);
}
-struct symbol *kernel_maps__find_symbol(u64 ip, struct map **mapp,
- symbol_filter_t filter)
+struct symbol *kernel_maps__find_function(u64 ip, struct map **mapp,
+ symbol_filter_t filter)
{
struct map *map = maps__find(&kernel_maps, ip);
@@ -1165,8 +1176,10 @@ struct symbol *kernel_maps__find_symbol(u64 ip, struct map **mapp,
if (map) {
ip = map->map_ip(map, ip);
- return map__find_symbol(map, ip, filter);
- }
+ return map__find_function(map, ip, filter);
+ } else
+ WARN_ONCE(RB_EMPTY_ROOT(&kernel_maps),
+ "Empty kernel_maps, was symbol__init() called?\n");
return NULL;
}
@@ -1425,8 +1438,8 @@ do_kallsyms:
if (err > 0) {
out_fixup:
- map__fixup_start(map);
- map__fixup_end(map);
+ map__fixup_start(map, &map->dso->functions);
+ map__fixup_end(map, &map->dso->functions);
}
return err;
@@ -1485,9 +1498,9 @@ size_t dsos__fprintf_buildid(FILE *fp)
return ret;
}
-static int kernel_maps__create_kernel_map(const char *vmlinux_name)
+static int kernel_maps__create_kernel_map(const struct symbol_conf *conf)
{
- struct dso *kernel = dso__new(vmlinux_name ?: "[kernel.kallsyms]");
+ struct dso *kernel = dso__new(conf->vmlinux_name ?: "[kernel.kallsyms]");
if (kernel == NULL)
return -1;
@@ -1577,18 +1590,21 @@ out_fail:
return -1;
}
-int kernel_maps__init(const char *vmlinux_name, bool try_vmlinux_path,
- bool use_modules)
+static int kernel_maps__init(const struct symbol_conf *conf)
{
- if (try_vmlinux_path && vmlinux_path__init() < 0)
+ const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults;
+
+ symbol__priv_size = pconf->priv_size;
+
+ if (pconf->try_vmlinux_path && vmlinux_path__init() < 0)
return -1;
- if (kernel_maps__create_kernel_map(vmlinux_name) < 0) {
+ if (kernel_maps__create_kernel_map(pconf) < 0) {
vmlinux_path__exit();
return -1;
}
- if (use_modules && kernel_maps__create_module_maps() < 0)
+ if (pconf->use_modules && kernel_maps__create_module_maps() < 0)
pr_debug("Failed to load list of modules in use, "
"continuing...\n");
/*
@@ -1598,8 +1614,8 @@ int kernel_maps__init(const char *vmlinux_name, bool try_vmlinux_path,
return 0;
}
-void symbol__init(unsigned int priv_size)
+int symbol__init(struct symbol_conf *conf)
{
elf_version(EV_CURRENT);
- symbol__priv_size = priv_size;
+ return kernel_maps__init(conf);
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 8c4d026e067..65846d0c5df 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -49,6 +49,13 @@ struct symbol {
char name[0];
};
+struct symbol_conf {
+ unsigned short priv_size;
+ bool try_vmlinux_path,
+ use_modules;
+ const char *vmlinux_name;
+};
+
extern unsigned int symbol__priv_size;
static inline void *symbol__priv(struct symbol *self)
@@ -58,8 +65,8 @@ static inline void *symbol__priv(struct symbol *self)
struct dso {
struct list_head node;
- struct rb_root syms;
- struct symbol *(*find_symbol)(struct dso *, u64 ip);
+ struct rb_root functions;
+ struct symbol *(*find_function)(struct dso *, u64 ip);
u8 adjust_symbols:1;
u8 slen_calculated:1;
u8 loaded:1;
@@ -76,7 +83,7 @@ struct dso {
struct dso *dso__new(const char *name);
void dso__delete(struct dso *self);
-struct symbol *dso__find_symbol(struct dso *self, u64 ip);
+struct symbol *dso__find_function(struct dso *self, u64 ip);
struct dso *dsos__findnew(const char *name);
int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
@@ -93,11 +100,9 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size);
bool dsos__read_build_ids(void);
int build_id__sprintf(u8 *self, int len, char *bf);
-int kernel_maps__init(const char *vmlinux_name, bool try_vmlinux_path,
- bool use_modules);
size_t kernel_maps__fprintf(FILE *fp);
-void symbol__init(unsigned int priv_size);
+int symbol__init(struct symbol_conf *conf);
extern struct list_head dsos;
extern struct map *kernel_map;
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 0f6d78c9863..1796625f778 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -11,7 +11,7 @@ static struct thread *last_match;
static struct thread *thread__new(pid_t pid)
{
- struct thread *self = calloc(1, sizeof(*self));
+ struct thread *self = zalloc(sizeof(*self));
if (self != NULL) {
self->pid = pid;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index e4b8d437725..74cba6487ed 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -26,8 +26,8 @@ size_t threads__fprintf(FILE *fp);
void maps__insert(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 ip);
-struct symbol *kernel_maps__find_symbol(const u64 ip, struct map **mapp,
- symbol_filter_t filter);
+struct symbol *kernel_maps__find_function(const u64 ip, struct map **mapp,
+ symbol_filter_t filter);
struct map *kernel_maps__find_by_dso_name(const char *name);
static inline struct map *thread__find_map(struct thread *self, u64 ip)
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index e1c623e0c99..c673d882588 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -290,17 +290,15 @@ static inline char *gitstrchrnul(const char *s, int c)
* Wrappers:
*/
extern char *xstrdup(const char *str);
-extern void *xmalloc(size_t size);
+extern void *xmalloc(size_t size) __attribute__((weak));
extern void *xmemdupz(const void *data, size_t len);
extern char *xstrndup(const char *str, size_t len);
-extern void *xrealloc(void *ptr, size_t size);
-extern void *xcalloc(size_t nmemb, size_t size);
-extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern ssize_t xread(int fd, void *buf, size_t len);
-extern ssize_t xwrite(int fd, const void *buf, size_t len);
-extern int xdup(int fd);
-extern FILE *xfdopen(int fd, const char *mode);
-extern int xmkstemp(char *template);
+extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
+
+static inline void *zalloc(size_t size)
+{
+ return calloc(1, size);
+}
static inline size_t xsize_t(off_t len)
{
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c
index 4574ac28396..bf44ca85d23 100644
--- a/tools/perf/util/wrapper.c
+++ b/tools/perf/util/wrapper.c
@@ -79,43 +79,12 @@ void *xrealloc(void *ptr, size_t size)
return ret;
}
-void *xcalloc(size_t nmemb, size_t size)
-{
- void *ret = calloc(nmemb, size);
- if (!ret && (!nmemb || !size))
- ret = calloc(1, 1);
- if (!ret) {
- release_pack_memory(nmemb * size, -1);
- ret = calloc(nmemb, size);
- if (!ret && (!nmemb || !size))
- ret = calloc(1, 1);
- if (!ret)
- die("Out of memory, calloc failed");
- }
- return ret;
-}
-
-void *xmmap(void *start, size_t length,
- int prot, int flags, int fd, off_t offset)
-{
- void *ret = mmap(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED) {
- if (!length)
- return NULL;
- release_pack_memory(length, fd);
- ret = mmap(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED)
- die("Out of memory? mmap failed: %s", strerror(errno));
- }
- return ret;
-}
-
/*
* xread() is the same a read(), but it automatically restarts read()
* operations with a recoverable error (EAGAIN and EINTR). xread()
* DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
*/
-ssize_t xread(int fd, void *buf, size_t len)
+static ssize_t xread(int fd, void *buf, size_t len)
{
ssize_t nr;
while (1) {
@@ -131,7 +100,7 @@ ssize_t xread(int fd, void *buf, size_t len)
* operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
* GUARANTEE that "len" bytes is written even if the operation is successful.
*/
-ssize_t xwrite(int fd, const void *buf, size_t len)
+static ssize_t xwrite(int fd, const void *buf, size_t len)
{
ssize_t nr;
while (1) {
@@ -179,29 +148,3 @@ ssize_t write_in_full(int fd, const void *buf, size_t count)
return total;
}
-
-int xdup(int fd)
-{
- int ret = dup(fd);
- if (ret < 0)
- die("dup failed: %s", strerror(errno));
- return ret;
-}
-
-FILE *xfdopen(int fd, const char *mode)
-{
- FILE *stream = fdopen(fd, mode);
- if (stream == NULL)
- die("Out of memory? fdopen failed: %s", strerror(errno));
- return stream;
-}
-
-int xmkstemp(char *template)
-{
- int fd;
-
- fd = mkstemp(template);
- if (fd < 0)
- die("Unable to create temporary file: %s", strerror(errno));
- return fd;
-}