Commit d380eaae authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf/core' of...

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
parents dda99116 ef1d1af2
...@@ -73,6 +73,10 @@ OPTIONS ...@@ -73,6 +73,10 @@ OPTIONS
(Only for --vars) Show external defined variables in addition to local (Only for --vars) Show external defined variables in addition to local
variables. variables.
-F::
--funcs::
Show available functions in given module or kernel.
-f:: -f::
--force:: --force::
Forcibly add events with existing name. Forcibly add events with existing name.
......
...@@ -402,6 +402,7 @@ LIB_H += util/debug.h ...@@ -402,6 +402,7 @@ LIB_H += util/debug.h
LIB_H += util/debugfs.h LIB_H += util/debugfs.h
LIB_H += util/event.h LIB_H += util/event.h
LIB_H += util/evsel.h LIB_H += util/evsel.h
LIB_H += util/evlist.h
LIB_H += util/exec_cmd.h LIB_H += util/exec_cmd.h
LIB_H += util/types.h LIB_H += util/types.h
LIB_H += util/levenshtein.h LIB_H += util/levenshtein.h
...@@ -425,6 +426,7 @@ LIB_H += util/values.h ...@@ -425,6 +426,7 @@ LIB_H += util/values.h
LIB_H += util/sort.h LIB_H += util/sort.h
LIB_H += util/hist.h LIB_H += util/hist.h
LIB_H += util/thread.h LIB_H += util/thread.h
LIB_H += util/thread_map.h
LIB_H += util/trace-event.h LIB_H += util/trace-event.h
LIB_H += util/probe-finder.h LIB_H += util/probe-finder.h
LIB_H += util/probe-event.h LIB_H += util/probe-event.h
...@@ -440,6 +442,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o ...@@ -440,6 +442,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/debugfs.o LIB_OBJS += $(OUTPUT)util/debugfs.o
LIB_OBJS += $(OUTPUT)util/environment.o LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/evlist.o
LIB_OBJS += $(OUTPUT)util/evsel.o LIB_OBJS += $(OUTPUT)util/evsel.o
LIB_OBJS += $(OUTPUT)util/exec_cmd.o LIB_OBJS += $(OUTPUT)util/exec_cmd.o
LIB_OBJS += $(OUTPUT)util/help.o LIB_OBJS += $(OUTPUT)util/help.o
...@@ -469,6 +472,7 @@ LIB_OBJS += $(OUTPUT)util/map.o ...@@ -469,6 +472,7 @@ LIB_OBJS += $(OUTPUT)util/map.o
LIB_OBJS += $(OUTPUT)util/pstack.o LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o LIB_OBJS += $(OUTPUT)util/thread.o
LIB_OBJS += $(OUTPUT)util/thread_map.o
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o LIB_OBJS += $(OUTPUT)util/trace-event-info.o
......
...@@ -52,6 +52,7 @@ static struct { ...@@ -52,6 +52,7 @@ static struct {
bool show_lines; bool show_lines;
bool show_vars; bool show_vars;
bool show_ext_vars; bool show_ext_vars;
bool show_funcs;
bool mod_events; bool mod_events;
int nevents; int nevents;
struct perf_probe_event events[MAX_PROBES]; struct perf_probe_event events[MAX_PROBES];
...@@ -221,6 +222,8 @@ static const struct option options[] = { ...@@ -221,6 +222,8 @@ static const struct option options[] = {
OPT__DRY_RUN(&probe_event_dry_run), OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", &params.max_probe_points, OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
"Set how many probe points can be found for a probe."), "Set how many probe points can be found for a probe."),
OPT_BOOLEAN('F', "funcs", &params.show_funcs,
"Show potential probe-able functions."),
OPT_END() OPT_END()
}; };
...@@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
params.max_probe_points = MAX_PROBES; params.max_probe_points = MAX_PROBES;
if ((!params.nevents && !params.dellist && !params.list_events && if ((!params.nevents && !params.dellist && !params.list_events &&
!params.show_lines)) !params.show_lines && !params.show_funcs))
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
/* /*
...@@ -267,12 +270,36 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -267,12 +270,36 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Don't use --list with --vars.\n"); pr_err(" Error: Don't use --list with --vars.\n");
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
} }
if (params.show_funcs) {
pr_err(" Error: Don't use --list with --funcs.\n");
usage_with_options(probe_usage, options);
}
ret = show_perf_probe_events(); ret = show_perf_probe_events();
if (ret < 0) if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n", pr_err(" Error: Failed to show event list. (%d)\n",
ret); ret);
return ret; return ret;
} }
if (params.show_funcs) {
if (params.nevents != 0 || params.dellist) {
pr_err(" Error: Don't use --funcs with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
pr_err(" Error: Don't use --funcs with --line.\n");
usage_with_options(probe_usage, options);
}
if (params.show_vars) {
pr_err(" Error: Don't use --funcs with --vars.\n");
usage_with_options(probe_usage, options);
}
ret = show_available_funcs(params.target_module);
if (ret < 0)
pr_err(" Error: Failed to show functions."
" (%d)\n", ret);
return ret;
}
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
if (params.show_lines) { if (params.show_lines) {
......
...@@ -18,17 +18,20 @@ ...@@ -18,17 +18,20 @@
#include "util/header.h" #include "util/header.h"
#include "util/event.h" #include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h" #include "util/evsel.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/session.h" #include "util/session.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/cpumap.h" #include "util/cpumap.h"
#include "util/thread_map.h"
#include <unistd.h> #include <unistd.h>
#include <sched.h> #include <sched.h>
#include <sys/mman.h> #include <sys/mman.h>
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define SID(e, x, y) xyarray__entry(e->id, x, y)
enum write_mode_t { enum write_mode_t {
WRITE_FORCE, WRITE_FORCE,
...@@ -46,7 +49,7 @@ static unsigned int user_freq = UINT_MAX; ...@@ -46,7 +49,7 @@ static unsigned int user_freq = UINT_MAX;
static int freq = 1000; static int freq = 1000;
static int output; static int output;
static int pipe_output = 0; static int pipe_output = 0;
static const char *output_name = "perf.data"; static const char *output_name = NULL;
static int group = 0; static int group = 0;
static int realtime_prio = 0; static int realtime_prio = 0;
static bool nodelay = false; static bool nodelay = false;
...@@ -66,51 +69,17 @@ static bool sample_address = false; ...@@ -66,51 +69,17 @@ static bool sample_address = false;
static bool sample_time = false; static bool sample_time = false;
static bool no_buildid = false; static bool no_buildid = false;
static bool no_buildid_cache = false; static bool no_buildid_cache = false;
static struct perf_evlist *evsel_list;
static long samples = 0; static long samples = 0;
static u64 bytes_written = 0; static u64 bytes_written = 0;
static struct pollfd *event_array;
static int nr_poll = 0;
static int nr_cpu = 0;
static int file_new = 1; static int file_new = 1;
static off_t post_processing_offset; static off_t post_processing_offset;
static struct perf_session *session; static struct perf_session *session;
static const char *cpu_list; static const char *cpu_list;
struct mmap_data {
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) static void advance_output(size_t size)
{ {
bytes_written += size; bytes_written += size;
...@@ -139,9 +108,9 @@ static int process_synthesized_event(event_t *event, ...@@ -139,9 +108,9 @@ static int process_synthesized_event(event_t *event,
return 0; return 0;
} }
static void mmap_read(struct mmap_data *md) static void mmap_read(struct perf_mmap *md)
{ {
unsigned int head = mmap_read_head(md); unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev; unsigned int old = md->prev;
unsigned char *data = md->base + page_size; unsigned char *data = md->base + page_size;
unsigned long size; unsigned long size;
...@@ -185,7 +154,7 @@ static void mmap_read(struct mmap_data *md) ...@@ -185,7 +154,7 @@ static void mmap_read(struct mmap_data *md)
write_output(buf, size); write_output(buf, size);
md->prev = old; md->prev = old;
mmap_write_tail(md, old); perf_mmap__write_tail(md, old);
} }
static volatile int done = 0; static volatile int done = 0;
...@@ -209,8 +178,6 @@ static void sig_atexit(void) ...@@ -209,8 +178,6 @@ static void sig_atexit(void)
kill(getpid(), signr); kill(getpid(), signr);
} }
static int group_fd;
static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
{ {
struct perf_header_attr *h_attr; struct perf_header_attr *h_attr;
...@@ -234,28 +201,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu) ...@@ -234,28 +201,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
char *filter = evsel->filter; char *filter = evsel->filter;
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
struct perf_header_attr *h_attr; struct perf_header_attr *h_attr;
int track = !evsel->idx; /* only the first counter needs these */ struct perf_sample_id *sid;
int thread_index; int thread_index;
int ret; int ret;
struct {
u64 count; for (thread_index = 0; thread_index < threads->nr; thread_index++) {
u64 time_enabled; h_attr = get_header_attr(attr, evsel->idx);
u64 time_running; if (h_attr == NULL)
u64 id; die("nomem\n");
} read_data;
/* if (!file_new) {
* Check if parse_single_tracepoint_event has already asked for if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
* PERF_SAMPLE_TIME. fprintf(stderr, "incompatible append\n");
* exit(-1);
* XXX this is kludgy but short term fix for problems introduced by }
* eac23d1c that broke 'perf script' by having different sample_types }
* when using multiple tracepoint events when we use a perf binary
* that tries to use sample_id_all on an older kernel. sid = SID(evsel, cpu, thread_index);
* if (perf_header_attr__add_id(h_attr, sid->id) < 0) {
* We need to move counter creation to perf_session, support pr_warning("Not enough memory to add id\n");
* different sample_types, etc. exit(-1);
*/ }
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
if (filter != NULL) {
ret = ioctl(FD(evsel, cpu, thread_index),
PERF_EVENT_IOC_SET_FILTER, filter);
if (ret) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
}
}
if (!sample_type)
sample_type = attr->sample_type;
}
static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
{
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_TOTAL_TIME_RUNNING |
...@@ -263,7 +249,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) ...@@ -263,7 +249,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (nr_counters > 1) if (evlist->nr_entries > 1)
attr->sample_type |= PERF_SAMPLE_ID; attr->sample_type |= PERF_SAMPLE_ID;
/* /*
...@@ -315,19 +301,39 @@ static void create_counter(struct perf_evsel *evsel, int cpu) ...@@ -315,19 +301,39 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
attr->mmap = track; attr->mmap = track;
attr->comm = track; attr->comm = track;
attr->inherit = !no_inherit;
if (target_pid == -1 && target_tid == -1 && !system_wide) { if (target_pid == -1 && target_tid == -1 && !system_wide) {
attr->disabled = 1; attr->disabled = 1;
attr->enable_on_exec = 1; attr->enable_on_exec = 1;
} }
retry_sample_id: }
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
for (thread_index = 0; thread_index < threads->nr; thread_index++) { static void open_counters(struct perf_evlist *evlist)
try_again: {
FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); struct perf_evsel *pos;
int cpu;
if (FD(evsel, nr_cpu, thread_index) < 0) { list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
/*
* Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME.
*
* XXX this is kludgy but short term fix for problems introduced by
* eac23d1c that broke 'perf script' by having different sample_types
* when using multiple tracepoint events when we use a perf binary
* that tries to use sample_id_all on an older kernel.
*
* We need to move counter creation to perf_session, support
* different sample_types, etc.
*/
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
config_attr(pos, evlist);
retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
try_again:
if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) {
int err = errno; int err = errno;
if (err == EPERM || err == EACCES) if (err == EPERM || err == EACCES)
...@@ -364,7 +370,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) ...@@ -364,7 +370,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
} }
printf("\n"); printf("\n");
error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
FD(evsel, nr_cpu, thread_index), strerror(err)); err, strerror(err));
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
...@@ -375,90 +381,16 @@ static void create_counter(struct perf_evsel *evsel, int cpu) ...@@ -375,90 +381,16 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
#endif #endif
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
exit(-1);
}
h_attr = get_header_attr(attr, evsel->idx);
if (h_attr == NULL)
die("nomem\n");
if (!file_new) {
if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
fprintf(stderr, "incompatible append\n");
exit(-1);
}
}
if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) {
perror("Unable to read perf file descriptor");
exit(-1);
}
if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
pr_warning("Not enough memory to add id\n");
exit(-1);
}
assert(FD(evsel, nr_cpu, thread_index) >= 0);
fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK);
/*
* First counter acts as the group leader:
*/
if (group && group_fd == -1)
group_fd = FD(evsel, nr_cpu, thread_index);
if (evsel->idx || thread_index) {
struct perf_evsel *first;
first = list_entry(evsel_list.next, struct perf_evsel, node);
ret = ioctl(FD(evsel, nr_cpu, thread_index),
PERF_EVENT_IOC_SET_OUTPUT,
FD(first, nr_cpu, 0));
if (ret) {
error("failed to set output: %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
} else {
mmap_array[nr_cpu].prev = 0;
mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0);
if (mmap_array[nr_cpu].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno));
exit(-1);
}
event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index);
event_array[nr_poll].events = POLLIN;
nr_poll++;
}
if (filter != NULL) {
ret = ioctl(FD(evsel, nr_cpu, thread_index),
PERF_EVENT_IOC_SET_FILTER, filter);
if (ret) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
} }
} }
if (!sample_type) if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, false) < 0)
sample_type = attr->sample_type; die("failed to mmap with %d (%s)\n", errno, strerror(errno));
}
static void open_counters(int cpu)
{
struct perf_evsel *pos;
group_fd = -1;
list_for_each_entry(pos, &evsel_list, node)
create_counter(pos, cpu);
nr_cpu++; for (cpu = 0; cpu < cpus->nr; ++cpu) {
list_for_each_entry(pos, &evlist->entries, node)
create_counter(pos, cpu);
}
} }
static int process_buildids(void) static int process_buildids(void)
...@@ -481,9 +413,9 @@ static void atexit_header(void) ...@@ -481,9 +413,9 @@ static void atexit_header(void)
if (!no_buildid) if (!no_buildid)
process_buildids(); process_buildids();
perf_header__write(&session->header, output, true); perf_header__write(&session->header, evsel_list, output, true);
perf_session__delete(session); perf_session__delete(session);
perf_evsel_list__delete(); perf_evlist__delete(evsel_list);
symbol__exit(); symbol__exit();
} }
} }
...@@ -533,9 +465,9 @@ static void mmap_read_all(void) ...@@ -533,9 +465,9 @@ static void mmap_read_all(void)
{ {
int i; int i;
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < cpus->nr; i++) {
if (mmap_array[i].base) if (evsel_list->mmap[i].base)
mmap_read(&mmap_array[i]); mmap_read(&evsel_list->mmap[i]);
} }
if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
...@@ -566,18 +498,26 @@ static int __cmd_record(int argc, const char **argv) ...@@ -566,18 +498,26 @@ static int __cmd_record(int argc, const char **argv)
exit(-1); exit(-1);
} }
if (!strcmp(output_name, "-")) if (!output_name) {
pipe_output = 1; if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
else if (!stat(output_name, &st) && st.st_size) { pipe_output = 1;
if (write_mode == WRITE_FORCE) { else
char oldname[PATH_MAX]; output_name = "perf.data";
snprintf(oldname, sizeof(oldname), "%s.old", }
output_name); if (output_name) {
unlink(oldname); if (!strcmp(output_name, "-"))
rename(output_name, oldname); pipe_output = 1;
else if (!stat(output_name, &st) && st.st_size) {
if (write_mode == WRITE_FORCE) {
char oldname[PATH_MAX];
snprintf(oldname, sizeof(oldname), "%s.old",
output_name);
unlink(oldname);
rename(output_name, oldname);
}
} else if (write_mode == WRITE_APPEND) {
write_mode = WRITE_FORCE;
} }
} else if (write_mode == WRITE_APPEND) {
write_mode = WRITE_FORCE;
} }
flags = O_CREAT|O_RDWR; flags = O_CREAT|O_RDWR;
...@@ -611,7 +551,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -611,7 +551,7 @@ static int __cmd_record(int argc, const char **argv)
goto out_delete_session; goto out_delete_session;
} }
if (have_tracepoints(&evsel_list)) if (have_tracepoints(&evsel_list->entries))
perf_header__set_feat(&session->header, HEADER_TRACE_INFO); perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
/* /*
...@@ -673,12 +613,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -673,12 +613,7 @@ static int __cmd_record(int argc, const char **argv)
close(child_ready_pipe[0]); close(child_ready_pipe[0]);
} }
if (!system_wide && no_inherit && !cpu_list) { open_counters(evsel_list);
open_counters(-1);
} else {
for (i = 0; i < cpus->nr; i++)
open_counters(cpus->map[i]);
}
perf_session__set_sample_type(session, sample_type); perf_session__set_sample_type(session, sample_type);
...@@ -687,7 +622,8 @@ static int __cmd_record(int argc, const char **argv) ...@@ -687,7 +622,8 @@ static int __cmd_record(int argc, const char **argv)
if (err < 0) if (err < 0)
return err; return err;
} else if (file_new) { } else if (file_new) {
err = perf_header__write(&session->header, output, false); err = perf_header__write(&session->header, evsel_list,
output, false);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -712,7 +648,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -712,7 +648,7 @@ static int __cmd_record(int argc, const char **argv)
return err; return err;
} }
if (have_tracepoints(&evsel_list)) { if (have_tracepoints(&evsel_list->entries)) {
/* /*
* FIXME err <= 0 here actually means that * FIXME err <= 0 here actually means that
* there were no tracepoints so its not really * there were no tracepoints so its not really
...@@ -721,7 +657,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -721,7 +657,7 @@ static int __cmd_record(int argc, const char **argv)
* return this more properly and also * return this more properly and also
* propagate errors that now are calling die() * propagate errors that now are calling die()
*/ */
err = event__synthesize_tracing_data(output, &evsel_list, err = event__synthesize_tracing_data(output, evsel_list,
process_synthesized_event, process_synthesized_event,
session); session);
if (err <= 0) { if (err <= 0) {
...@@ -789,15 +725,15 @@ static int __cmd_record(int argc, const char **argv) ...@@ -789,15 +725,15 @@ static int __cmd_record(int argc, const char **argv)
if (hits == samples) { if (hits == samples) {
if (done) if (done)
break; break;
err = poll(event_array, nr_poll, -1); err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1);
waking++; waking++;
} }
if (done) { if (done) {
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < cpus->nr; i++) {
struct perf_evsel *pos; struct perf_evsel *pos;
list_for_each_entry(pos, &evsel_list, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
for (thread = 0; for (thread = 0;
thread < threads->nr; thread < threads->nr;
thread++) thread++)
...@@ -838,10 +774,10 @@ static const char * const record_usage[] = { ...@@ -838,10 +774,10 @@ static const char * const record_usage[] = {
static bool force, append_file; static bool force, append_file;
const struct option record_options[] = { const struct option record_options[] = {
OPT_CALLBACK('e', "event", NULL, "event", OPT_CALLBACK('e', "event", &evsel_list, "event",
"event selector. use 'perf list' to list available events", "event selector. use 'perf list' to list available events",
parse_events), parse_events),
OPT_CALLBACK(0, "filter", NULL, "filter", OPT_CALLBACK(0, "filter", &evsel_list, "filter",
"event filter", parse_filter), "event filter", parse_filter),
OPT_INTEGER('p', "pid", &target_pid, OPT_INTEGER('p', "pid", &target_pid,
"record events on existing process id"), "record events on existing process id"),
...@@ -892,6 +828,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -892,6 +828,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
int err = -ENOMEM; int err = -ENOMEM;
struct perf_evsel *pos; struct perf_evsel *pos;
evsel_list = perf_evlist__new();
if (evsel_list == NULL)
return -ENOMEM;
argc = parse_options(argc, argv, record_options, record_usage, argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && target_tid == -1 && if (!argc && target_pid == -1 && target_tid == -1 &&
...@@ -913,7 +853,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -913,7 +853,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
if (no_buildid_cache || no_buildid) if (no_buildid_cache || no_buildid)
disable_buildid_cache(); disable_buildid_cache();
if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { if (evsel_list->nr_entries == 0 &&
perf_evlist__add_default(evsel_list) < 0) {
pr_err("Not enough memory for event selector list\n"); pr_err("Not enough memory for event selector list\n");
goto out_symbol_exit; goto out_symbol_exit;
} }
...@@ -927,21 +868,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -927,21 +868,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
usage_with_options(record_usage, record_options); usage_with_options(record_usage, record_options);
} }
cpus = cpu_map__new(cpu_list); if (target_tid != -1)
if (cpus == NULL) { cpus = cpu_map__dummy_new();
perror("failed to parse CPUs map"); else
return -1; cpus = cpu_map__new(cpu_list);
}
list_for_each_entry(pos, &evsel_list, node) { if (cpus == NULL)
usage_with_options(record_usage, record_options);
list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
goto out_free_fd; goto out_free_fd;
if (perf_header__push_event(pos->attr.config, event_name(pos))) if (perf_header__push_event(pos->attr.config, event_name(pos)))
goto out_free_fd; goto out_free_fd;
} }
event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS *
MAX_COUNTERS * threads->nr)); if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0)
if (!event_array)
goto out_free_fd; goto out_free_fd;
if (user_interval != ULLONG_MAX) if (user_interval != ULLONG_MAX)
...@@ -959,13 +901,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -959,13 +901,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
} else { } else {
fprintf(stderr, "frequency and count are zero, aborting\n"); fprintf(stderr, "frequency and count are zero, aborting\n");
err = -EINVAL; err = -EINVAL;
goto out_free_event_array; goto out_free_fd;
} }
err = __cmd_record(argc, argv); err = __cmd_record(argc, argv);
out_free_event_array:
free(event_array);
out_free_fd: out_free_fd:
thread_map__delete(threads); thread_map__delete(threads);
threads = NULL; threads = NULL;
......
...@@ -81,18 +81,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, ...@@ -81,18 +81,17 @@ static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al, struct addr_location *al,
struct sample_data *data) struct sample_data *data)
{ {
struct map_symbol *syms = NULL;
struct symbol *parent = NULL; struct symbol *parent = NULL;
int err = -ENOMEM; int err = 0;
struct hist_entry *he; struct hist_entry *he;
struct hists *hists; struct hists *hists;
struct perf_event_attr *attr; struct perf_event_attr *attr;
if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
syms = perf_session__resolve_callchain(self, al->thread, err = perf_session__resolve_callchain(self, al->thread,
data->callchain, &parent); data->callchain, &parent);
if (syms == NULL) if (err)
return -ENOMEM; return err;
} }
attr = perf_header__find_attr(data->id, &self->header); attr = perf_header__find_attr(data->id, &self->header);
...@@ -101,16 +100,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, ...@@ -101,16 +100,17 @@ static int perf_session__add_hist_entry(struct perf_session *self,
else else
hists = perf_session__hists_findnew(self, data->id, 0, 0); hists = perf_session__hists_findnew(self, data->id, 0, 0);
if (hists == NULL) if (hists == NULL)
goto out_free_syms; return -ENOMEM;
he = __hists__add_entry(hists, al, parent, data->period); he = __hists__add_entry(hists, al, parent, data->period);
if (he == NULL) if (he == NULL)
goto out_free_syms; return -ENOMEM;
err = 0;
if (symbol_conf.use_callchain) { if (symbol_conf.use_callchain) {
err = callchain_append(he->callchain, data->callchain, syms, err = callchain_append(he->callchain, &self->callchain_cursor,
data->period); data->period);
if (err) if (err)
goto out_free_syms; return err;
} }
/* /*
* Only in the newt browser we are doing integrated annotation, * Only in the newt browser we are doing integrated annotation,
...@@ -119,8 +119,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, ...@@ -119,8 +119,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
*/ */
if (use_browser > 0) if (use_browser > 0)
err = hist_entry__inc_addr_samples(he, al->addr); err = hist_entry__inc_addr_samples(he, al->addr);
out_free_syms:
free(syms);
return err; return err;
} }
...@@ -222,7 +221,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) ...@@ -222,7 +221,7 @@ static int perf_session__setup_sample_type(struct perf_session *self)
} else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
!symbol_conf.use_callchain) { !symbol_conf.use_callchain) {
symbol_conf.use_callchain = true; symbol_conf.use_callchain = true;
if (register_callchain_param(&callchain_param) < 0) { if (callchain_register_param(&callchain_param) < 0) {
fprintf(stderr, "Can't register callchain" fprintf(stderr, "Can't register callchain"
" params\n"); " params\n");
return -EINVAL; return -EINVAL;
...@@ -424,7 +423,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, ...@@ -424,7 +423,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
if (tok2) if (tok2)
callchain_param.print_limit = strtod(tok2, &endptr); callchain_param.print_limit = strtod(tok2, &endptr);
setup: setup:
if (register_callchain_param(&callchain_param) < 0) { if (callchain_register_param(&callchain_param) < 0) {
fprintf(stderr, "Can't register callchain params\n"); fprintf(stderr, "Can't register callchain params\n");
return -1; return -1;
} }
......
...@@ -43,11 +43,13 @@ ...@@ -43,11 +43,13 @@
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/event.h" #include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h" #include "util/evsel.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/header.h" #include "util/header.h"
#include "util/cpumap.h" #include "util/cpumap.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/thread_map.h"
#include <sys/prctl.h> #include <sys/prctl.h>
#include <math.h> #include <math.h>
...@@ -71,6 +73,8 @@ static struct perf_event_attr default_attrs[] = { ...@@ -71,6 +73,8 @@ static struct perf_event_attr default_attrs[] = {
}; };
struct perf_evlist *evsel_list;
static bool system_wide = false; static bool system_wide = false;
static struct cpu_map *cpus; static struct cpu_map *cpus;
static int run_idx = 0; static int run_idx = 0;
...@@ -166,7 +170,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) ...@@ -166,7 +170,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
PERF_FORMAT_TOTAL_TIME_RUNNING; PERF_FORMAT_TOTAL_TIME_RUNNING;
if (system_wide) if (system_wide)
return perf_evsel__open_per_cpu(evsel, cpus); return perf_evsel__open_per_cpu(evsel, cpus, false, false);
attr->inherit = !no_inherit; attr->inherit = !no_inherit;
if (target_pid == -1 && target_tid == -1) { if (target_pid == -1 && target_tid == -1) {
...@@ -174,7 +178,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) ...@@ -174,7 +178,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
attr->enable_on_exec = 1; attr->enable_on_exec = 1;
} }
return perf_evsel__open_per_thread(evsel, threads); return perf_evsel__open_per_thread(evsel, threads, false, false);
} }
/* /*
...@@ -309,7 +313,7 @@ static int run_perf_stat(int argc __used, const char **argv) ...@@ -309,7 +313,7 @@ static int run_perf_stat(int argc __used, const char **argv)
close(child_ready_pipe[0]); close(child_ready_pipe[0]);
} }
list_for_each_entry(counter, &evsel_list, node) { list_for_each_entry(counter, &evsel_list->entries, node) {
if (create_perf_stat_counter(counter) < 0) { if (create_perf_stat_counter(counter) < 0) {
if (errno == -EPERM || errno == -EACCES) { if (errno == -EPERM || errno == -EACCES) {
error("You may not have permission to collect %sstats.\n" error("You may not have permission to collect %sstats.\n"
...@@ -347,12 +351,12 @@ static int run_perf_stat(int argc __used, const char **argv) ...@@ -347,12 +351,12 @@ static int run_perf_stat(int argc __used, const char **argv)
update_stats(&walltime_nsecs_stats, t1 - t0); update_stats(&walltime_nsecs_stats, t1 - t0);
if (no_aggr) { if (no_aggr) {
list_for_each_entry(counter, &evsel_list, node) { list_for_each_entry(counter, &evsel_list->entries, node) {
read_counter(counter); read_counter(counter);
perf_evsel__close_fd(counter, cpus->nr, 1); perf_evsel__close_fd(counter, cpus->nr, 1);
} }
} else { } else {
list_for_each_entry(counter, &evsel_list, node) { list_for_each_entry(counter, &evsel_list->entries, node) {
read_counter_aggr(counter); read_counter_aggr(counter);
perf_evsel__close_fd(counter, cpus->nr, threads->nr); perf_evsel__close_fd(counter, cpus->nr, threads->nr);
} }
...@@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv) ...@@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv)
} }
if (no_aggr) { if (no_aggr) {
list_for_each_entry(counter, &evsel_list, node) list_for_each_entry(counter, &evsel_list->entries, node)
print_counter(counter); print_counter(counter);
} else { } else {
list_for_each_entry(counter, &evsel_list, node) list_for_each_entry(counter, &evsel_list->entries, node)
print_counter_aggr(counter); print_counter_aggr(counter);
} }
...@@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used, ...@@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used,
} }
static const struct option options[] = { static const struct option options[] = {
OPT_CALLBACK('e', "event", NULL, "event", OPT_CALLBACK('e', "event", &evsel_list, "event",
"event selector. use 'perf list' to list available events", "event selector. use 'perf list' to list available events",
parse_events), parse_events),
OPT_BOOLEAN('i', "no-inherit", &no_inherit, OPT_BOOLEAN('i', "no-inherit", &no_inherit,
...@@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
evsel_list = perf_evlist__new();
if (evsel_list == NULL)
return -ENOMEM;
argc = parse_options(argc, argv, options, stat_usage, argc = parse_options(argc, argv, options, stat_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
...@@ -679,17 +687,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -679,17 +687,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
usage_with_options(stat_usage, options); usage_with_options(stat_usage, options);
/* Set attrs and nr_counters if no event is selected and !null_run */ /* Set attrs and nr_counters if no event is selected and !null_run */
if (!null_run && !nr_counters) { if (!null_run && !evsel_list->nr_entries) {
size_t c; size_t c;
nr_counters = ARRAY_SIZE(default_attrs);
for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) {
pos = perf_evsel__new(&default_attrs[c], pos = perf_evsel__new(&default_attrs[c], c);
nr_counters);
if (pos == NULL) if (pos == NULL)
goto out; goto out;
list_add(&pos->node, &evsel_list); perf_evlist__add(evsel_list, pos);
} }
} }
...@@ -713,7 +718,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -713,7 +718,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
return -1; return -1;
} }
list_for_each_entry(pos, &evsel_list, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_evsel__alloc_stat_priv(pos) < 0 || if (perf_evsel__alloc_stat_priv(pos) < 0 ||
perf_evsel__alloc_counts(pos, cpus->nr) < 0 || perf_evsel__alloc_counts(pos, cpus->nr) < 0 ||
perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
...@@ -741,9 +746,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -741,9 +746,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
if (status != -1) if (status != -1)
print_stat(argc, argv); print_stat(argc, argv);
out_free_fd: out_free_fd:
list_for_each_entry(pos, &evsel_list, node) list_for_each_entry(pos, &evsel_list->entries, node)
perf_evsel__free_stat_priv(pos); perf_evsel__free_stat_priv(pos);
perf_evsel_list__delete(); perf_evlist__delete(evsel_list);
out: out:
thread_map__delete(threads); thread_map__delete(threads);
threads = NULL; threads = NULL;
......
...@@ -7,10 +7,11 @@ ...@@ -7,10 +7,11 @@
#include "util/cache.h" #include "util/cache.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/evlist.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/session.h" #include "util/parse-events.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/thread.h" #include "util/thread_map.h"
static long page_size; static long page_size;
...@@ -238,14 +239,14 @@ static int test__vmlinux_matches_kallsyms(void) ...@@ -238,14 +239,14 @@ static int test__vmlinux_matches_kallsyms(void)
#include "util/evsel.h" #include "util/evsel.h"
#include <sys/types.h> #include <sys/types.h>
static int trace_event__id(const char *event_name) static int trace_event__id(const char *evname)
{ {
char *filename; char *filename;
int err = -1, fd; int err = -1, fd;
if (asprintf(&filename, if (asprintf(&filename,
"/sys/kernel/debug/tracing/events/syscalls/%s/id", "/sys/kernel/debug/tracing/events/syscalls/%s/id",
event_name) < 0) evname) < 0)
return -1; return -1;
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
...@@ -289,7 +290,7 @@ static int test__open_syscall_event(void) ...@@ -289,7 +290,7 @@ static int test__open_syscall_event(void)
goto out_thread_map_delete; goto out_thread_map_delete;
} }
if (perf_evsel__open_per_thread(evsel, threads) < 0) { if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) {
pr_debug("failed to open counter: %s, " pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n", "tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno)); strerror(errno));
...@@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void) ...@@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void)
} }
cpus = cpu_map__new(NULL); cpus = cpu_map__new(NULL);
if (threads == NULL) { if (cpus == NULL) {
pr_debug("thread_map__new\n"); pr_debug("cpu_map__new\n");
return -1; goto out_thread_map_delete;
} }
...@@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void) ...@@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void)
goto out_thread_map_delete; goto out_thread_map_delete;
} }
if (perf_evsel__open(evsel, cpus, threads) < 0) { if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) {
pr_debug("failed to open counter: %s, " pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n", "tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno)); strerror(errno));
...@@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void) ...@@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void)
goto out_close_fd; goto out_close_fd;
} }
err = 0;
for (cpu = 0; cpu < cpus->nr; ++cpu) { for (cpu = 0; cpu < cpus->nr; ++cpu) {
unsigned int expected; unsigned int expected;
...@@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void) ...@@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void)
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
pr_debug("perf_evsel__open_read_on_cpu\n"); pr_debug("perf_evsel__open_read_on_cpu\n");
goto out_close_fd; err = -1;
break;
} }
expected = nr_open_calls + cpu; expected = nr_open_calls + cpu;
if (evsel->counts->cpu[cpu].val != expected) { if (evsel->counts->cpu[cpu].val != expected) {
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
goto out_close_fd; err = -1;
} }
} }
err = 0;
out_close_fd: out_close_fd:
perf_evsel__close_fd(evsel, 1, threads->nr); perf_evsel__close_fd(evsel, 1, threads->nr);
out_evsel_delete: out_evsel_delete:
...@@ -437,6 +440,159 @@ static int test__open_syscall_event_on_all_cpus(void) ...@@ -437,6 +440,159 @@ static int test__open_syscall_event_on_all_cpus(void)
return err; return err;
} }
/*
* This test will generate random numbers of calls to some getpid syscalls,
* then establish an mmap for a group of events that are created to monitor
* the syscalls.
*
* It will receive the events, using mmap, use its PERF_SAMPLE_ID generated
* sample.id field to map back to its respective perf_evsel instance.
*
* Then it checks if the number of syscalls reported as perf events by
* the kernel corresponds to the number of syscalls made.
*/
static int test__basic_mmap(void)
{
int err = -1;
event_t *event;
struct thread_map *threads;
struct cpu_map *cpus;
struct perf_evlist *evlist;
struct perf_event_attr attr = {
.type = PERF_TYPE_TRACEPOINT,
.read_format = PERF_FORMAT_ID,
.sample_type = PERF_SAMPLE_ID,
.watermark = 0,
};
cpu_set_t cpu_set;
const char *syscall_names[] = { "getsid", "getppid", "getpgrp",
"getpgid", };
pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp,
(void*)getpgid };
#define nsyscalls ARRAY_SIZE(syscall_names)
int ids[nsyscalls];
unsigned int nr_events[nsyscalls],
expected_nr_events[nsyscalls], i, j;
struct perf_evsel *evsels[nsyscalls], *evsel;
for (i = 0; i < nsyscalls; ++i) {
char name[64];
snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
ids[i] = trace_event__id(name);
if (ids[i] < 0) {
pr_debug("Is debugfs mounted on /sys/kernel/debug?\n");
return -1;
}
nr_events[i] = 0;
expected_nr_events[i] = random() % 257;
}
threads = thread_map__new(-1, getpid());
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
}
cpus = cpu_map__new(NULL);
if (threads == NULL) {
pr_debug("thread_map__new\n");
goto out_free_threads;
}
CPU_ZERO(&cpu_set);
CPU_SET(cpus->map[0], &cpu_set);
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
cpus->map[0], strerror(errno));
goto out_free_cpus;
}
evlist = perf_evlist__new();
if (threads == NULL) {
pr_debug("perf_evlist__new\n");
goto out_free_cpus;
}
/* anonymous union fields, can't be initialized above */
attr.wakeup_events = 1;
attr.sample_period = 1;
for (i = 0; i < nsyscalls; ++i) {
attr.config = ids[i];
evsels[i] = perf_evsel__new(&attr, i);
if (evsels[i] == NULL) {
pr_debug("perf_evsel__new\n");
goto out_free_evlist;
}
perf_evlist__add(evlist, evsels[i]);
if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
goto out_close_fd;
}
}
if (perf_evlist__mmap(evlist, cpus, threads, 128, true) < 0) {
pr_debug("failed to mmap events: %d (%s)\n", errno,
strerror(errno));
goto out_close_fd;
}
for (i = 0; i < nsyscalls; ++i)
for (j = 0; j < expected_nr_events[i]; ++j) {
int foo = syscalls[i]();
++foo;
}
while ((event = perf_evlist__read_on_cpu(evlist, 0)) != NULL) {
struct sample_data sample;
if (event->header.type != PERF_RECORD_SAMPLE) {
pr_debug("unexpected %s event\n",
event__get_event_name(event->header.type));
goto out_munmap;
}
event__parse_sample(event, attr.sample_type, false, &sample);
evsel = perf_evlist__id2evsel(evlist, sample.id);
if (evsel == NULL) {
pr_debug("event with id %" PRIu64
" doesn't map to an evsel\n", sample.id);
goto out_munmap;
}
nr_events[evsel->idx]++;
}
list_for_each_entry(evsel, &evlist->entries, node) {
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
pr_debug("expected %d %s events, got %d\n",
expected_nr_events[evsel->idx],
event_name(evsel), nr_events[evsel->idx]);
goto out_munmap;
}
}
err = 0;
out_munmap:
perf_evlist__munmap(evlist, 1);
out_close_fd:
for (i = 0; i < nsyscalls; ++i)
perf_evsel__close_fd(evsels[i], 1, threads->nr);
out_free_evlist:
perf_evlist__delete(evlist);
out_free_cpus:
cpu_map__delete(cpus);
out_free_threads:
thread_map__delete(threads);
return err;
#undef nsyscalls
}
static struct test { static struct test {
const char *desc; const char *desc;
int (*func)(void); int (*func)(void);
...@@ -453,6 +609,10 @@ static struct test { ...@@ -453,6 +609,10 @@ static struct test {
.desc = "detect open syscall event on all cpus", .desc = "detect open syscall event on all cpus",
.func = test__open_syscall_event_on_all_cpus, .func = test__open_syscall_event_on_all_cpus,
}, },
{
.desc = "read samples using the mmap interface",
.func = test__basic_mmap,
},
{ {
.func = NULL, .func = NULL,
}, },
......
...@@ -21,10 +21,12 @@ ...@@ -21,10 +21,12 @@
#include "perf.h" #include "perf.h"
#include "util/color.h" #include "util/color.h"
#include "util/evlist.h"
#include "util/evsel.h" #include "util/evsel.h"
#include "util/session.h" #include "util/session.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/thread_map.h"
#include "util/util.h" #include "util/util.h"
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include "util/parse-options.h" #include "util/parse-options.h"
...@@ -60,6 +62,8 @@ ...@@ -60,6 +62,8 @@
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
struct perf_evlist *evsel_list;
static bool system_wide = false; static bool system_wide = false;
static int default_interval = 0; static int default_interval = 0;
...@@ -75,7 +79,7 @@ static struct cpu_map *cpus; ...@@ -75,7 +79,7 @@ static struct cpu_map *cpus;
static int realtime_prio = 0; static int realtime_prio = 0;
static bool group = false; static bool group = false;
static unsigned int page_size; static unsigned int page_size;
static unsigned int mmap_pages = 16; static unsigned int mmap_pages = 128;
static int freq = 1000; /* 1 KHz */ static int freq = 1000; /* 1 KHz */
static int delay_secs = 2; static int delay_secs = 2;
...@@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme) ...@@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme)
line = syme->src->lines; line = syme->src->lines;
while (line) { while (line) {
for (i = 0; i < nr_counters; i++) for (i = 0; i < evsel_list->nr_entries; i++)
line->count[i] = 0; line->count[i] = 0;
line = line->next; line = line->next;
} }
...@@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym) ...@@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym)
if (!display_weighted) if (!display_weighted)
return weight; return weight;
for (counter = 1; counter < nr_counters-1; counter++) for (counter = 1; counter < evsel_list->nr_entries - 1; counter++)
weight *= sym->count[counter]; weight *= sym->count[counter];
weight /= (sym->count[counter] + 1); weight /= (sym->count[counter] + 1);
...@@ -501,7 +505,7 @@ static void print_sym_table(void) ...@@ -501,7 +505,7 @@ static void print_sym_table(void)
rb_insert_active_sym(&tmp, syme); rb_insert_active_sym(&tmp, syme);
sum_ksamples += syme->snap_count; sum_ksamples += syme->snap_count;
for (j = 0; j < nr_counters; j++) for (j = 0; j < evsel_list->nr_entries; j++)
syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8;
} else } else
list_remove_active_sym(syme); list_remove_active_sym(syme);
...@@ -535,9 +539,9 @@ static void print_sym_table(void) ...@@ -535,9 +539,9 @@ static void print_sym_table(void)
esamples_percent); esamples_percent);
} }
if (nr_counters == 1 || !display_weighted) { if (evsel_list->nr_entries == 1 || !display_weighted) {
struct perf_evsel *first; struct perf_evsel *first;
first = list_entry(evsel_list.next, struct perf_evsel, node); first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
printf("%" PRIu64, (uint64_t)first->attr.sample_period); printf("%" PRIu64, (uint64_t)first->attr.sample_period);
if (freq) if (freq)
printf("Hz "); printf("Hz ");
...@@ -547,7 +551,7 @@ static void print_sym_table(void) ...@@ -547,7 +551,7 @@ static void print_sym_table(void)
if (!display_weighted) if (!display_weighted)
printf("%s", event_name(sym_evsel)); printf("%s", event_name(sym_evsel));
else list_for_each_entry(counter, &evsel_list, node) { else list_for_each_entry(counter, &evsel_list->entries, node) {
if (counter->idx) if (counter->idx)
printf("/"); printf("/");
...@@ -606,7 +610,7 @@ static void print_sym_table(void) ...@@ -606,7 +610,7 @@ static void print_sym_table(void)
sym_width = winsize.ws_col - dso_width - 29; sym_width = winsize.ws_col - dso_width - 29;
} }
putchar('\n'); putchar('\n');
if (nr_counters == 1) if (evsel_list->nr_entries == 1)
printf(" samples pcnt"); printf(" samples pcnt");
else else
printf(" weight samples pcnt"); printf(" weight samples pcnt");
...@@ -615,7 +619,7 @@ static void print_sym_table(void) ...@@ -615,7 +619,7 @@ static void print_sym_table(void)
printf(" RIP "); printf(" RIP ");
printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
printf(" %s _______ _____", printf(" %s _______ _____",
nr_counters == 1 ? " " : "______"); evsel_list->nr_entries == 1 ? " " : "______");
if (verbose) if (verbose)
printf(" ________________"); printf(" ________________");
printf(" %-*.*s", sym_width, sym_width, graph_line); printf(" %-*.*s", sym_width, sym_width, graph_line);
...@@ -634,7 +638,7 @@ static void print_sym_table(void) ...@@ -634,7 +638,7 @@ static void print_sym_table(void)
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
sum_ksamples)); sum_ksamples));
if (nr_counters == 1 || !display_weighted) if (evsel_list->nr_entries == 1 || !display_weighted)
printf("%20.2f ", syme->weight); printf("%20.2f ", syme->weight);
else else
printf("%9.1f %10ld ", syme->weight, syme->snap_count); printf("%9.1f %10ld ", syme->weight, syme->snap_count);
...@@ -744,7 +748,7 @@ static void print_mapped_keys(void) ...@@ -744,7 +748,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
if (nr_counters > 1) if (evsel_list->nr_entries > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
...@@ -753,7 +757,7 @@ static void print_mapped_keys(void) ...@@ -753,7 +757,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
fprintf(stdout, "\t[S] stop annotation.\n"); fprintf(stdout, "\t[S] stop annotation.\n");
if (nr_counters > 1) if (evsel_list->nr_entries > 1)
fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
fprintf(stdout, fprintf(stdout,
...@@ -783,7 +787,7 @@ static int key_mapped(int c) ...@@ -783,7 +787,7 @@ static int key_mapped(int c)
return 1; return 1;
case 'E': case 'E':
case 'w': case 'w':
return nr_counters > 1 ? 1 : 0; return evsel_list->nr_entries > 1 ? 1 : 0;
default: default:
break; break;
} }
...@@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c) ...@@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c)
signal(SIGWINCH, SIG_DFL); signal(SIGWINCH, SIG_DFL);
break; break;
case 'E': case 'E':
if (nr_counters > 1) { if (evsel_list->nr_entries > 1) {
fprintf(stderr, "\nAvailable events:"); fprintf(stderr, "\nAvailable events:");
list_for_each_entry(sym_evsel, &evsel_list, node) list_for_each_entry(sym_evsel, &evsel_list->entries, node)
fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel));
prompt_integer(&sym_counter, "Enter details event counter"); prompt_integer(&sym_counter, "Enter details event counter");
if (sym_counter >= nr_counters) { if (sym_counter >= evsel_list->nr_entries) {
sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node);
sym_counter = 0; sym_counter = 0;
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel));
sleep(1); sleep(1);
break; break;
} }
list_for_each_entry(sym_evsel, &evsel_list, node) list_for_each_entry(sym_evsel, &evsel_list->entries, node)
if (sym_evsel->idx == sym_counter) if (sym_evsel->idx == sym_counter)
break; break;
} else sym_counter = 0; } else sym_counter = 0;
...@@ -930,6 +934,7 @@ static void *display_thread(void *arg __used) ...@@ -930,6 +934,7 @@ static void *display_thread(void *arg __used)
/* Tag samples to be skipped. */ /* Tag samples to be skipped. */
static const char *skip_symbols[] = { static const char *skip_symbols[] = {
"default_idle", "default_idle",
"native_safe_halt",
"cpu_idle", "cpu_idle",
"enter_idle", "enter_idle",
"exit_idle", "exit_idle",
...@@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) ...@@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
static void event__process_sample(const event_t *self, static void event__process_sample(const event_t *self,
struct sample_data *sample, struct sample_data *sample,
struct perf_session *session, struct perf_session *session)
struct perf_evsel *evsel)
{ {
u64 ip = self->ip.ip; u64 ip = self->ip.ip;
struct sym_entry *syme; struct sym_entry *syme;
...@@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self, ...@@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self,
syme = symbol__priv(al.sym); syme = symbol__priv(al.sym);
if (!syme->skip) { if (!syme->skip) {
syme->count[evsel->idx]++; struct perf_evsel *evsel;
syme->origin = origin; syme->origin = origin;
evsel = perf_evlist__id2evsel(evsel_list, sample->id);
assert(evsel != NULL);
syme->count[evsel->idx]++;
record_precise_ip(syme, evsel->idx, ip); record_precise_ip(syme, evsel->idx, ip);
pthread_mutex_lock(&active_symbols_lock); pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next) if (list_empty(&syme->node) || !syme->node.next)
...@@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self, ...@@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self,
} }
} }
struct mmap_data { static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu)
void *base;
int mask;
unsigned int prev;
};
static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel,
int ncpus, int nthreads)
{
evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data));
return evsel->priv != NULL ? 0 : -ENOMEM;
}
static void perf_evsel__free_mmap(struct perf_evsel *evsel)
{
xyarray__delete(evsel->priv);
evsel->priv = NULL;
}
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 perf_evsel *evsel,
int cpu, int thread_idx)
{ {
struct xyarray *mmap_array = evsel->priv;
struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx);
unsigned int head = mmap_read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
struct sample_data sample; struct sample_data sample;
int diff; event_t *event;
/*
* 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 > md->mask / 2 || diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
/*
* head points to a known good entry, start there.
*/
old = head;
}
for (; old != head;) {
event_t *event = (event_t *)&data[old & md->mask];
event_t event_copy; while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) {
perf_session__parse_sample(self, event, &sample);
size_t size = event->header.size;
/*
* Event straddles the mmap boundary -- header should always
* be inside due to u64 alignment of output.
*/
if ((old & md->mask) + size != ((old + size) & md->mask)) {
unsigned int offset = old;
unsigned int len = min(sizeof(*event), size), cpy;
void *dst = &event_copy;
do {
cpy = min(md->mask + 1 - (offset & md->mask), len);
memcpy(dst, &data[offset & md->mask], cpy);
offset += cpy;
dst += cpy;
len -= cpy;
} while (len);
event = &event_copy;
}
event__parse_sample(event, self, &sample);
if (event->header.type == PERF_RECORD_SAMPLE) if (event->header.type == PERF_RECORD_SAMPLE)
event__process_sample(event, &sample, self, evsel); event__process_sample(event, &sample, self);
else else
event__process(event, &sample, self); event__process(event, &sample, self);
old += size;
} }
md->prev = old;
} }
static struct pollfd *event_array;
static void perf_session__mmap_read(struct perf_session *self) static void perf_session__mmap_read(struct perf_session *self)
{ {
struct perf_evsel *counter; int i;
int i, thread_index;
for (i = 0; i < cpus->nr; i++) {
list_for_each_entry(counter, &evsel_list, node) {
for (thread_index = 0;
thread_index < threads->nr;
thread_index++) {
perf_session__mmap_read_counter(self,
counter, i, thread_index);
}
}
}
}
int nr_poll; for (i = 0; i < cpus->nr; i++)
int group_fd; perf_session__mmap_read_cpu(self, i);
}
static void start_counter(int i, struct perf_evsel *evsel) static void start_counters(struct perf_evlist *evlist)
{ {
struct xyarray *mmap_array = evsel->priv; struct perf_evsel *counter;
struct mmap_data *mm;
struct perf_event_attr *attr;
int cpu = -1;
int thread_index;
if (target_tid == -1)
cpu = cpus->map[i];
attr = &evsel->attr; list_for_each_entry(counter, &evlist->entries, node) {
struct perf_event_attr *attr = &counter->attr;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (freq) { if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD; attr->sample_type |= PERF_SAMPLE_PERIOD;
attr->freq = 1; attr->freq = 1;
attr->sample_freq = freq; attr->sample_freq = freq;
} }
attr->inherit = (cpu < 0) && inherit; if (evlist->nr_entries > 1) {
attr->mmap = 1; attr->sample_type |= PERF_SAMPLE_ID;
attr->read_format |= PERF_FORMAT_ID;
}
for (thread_index = 0; thread_index < threads->nr; thread_index++) { attr->mmap = 1;
try_again: try_again:
FD(evsel, i, thread_index) = sys_perf_event_open(attr, if (perf_evsel__open(counter, cpus, threads, group, inherit) < 0) {
threads->map[thread_index], cpu, group_fd, 0);
if (FD(evsel, i, thread_index) < 0) {
int err = errno; int err = errno;
if (err == EPERM || err == EACCES) if (err == EPERM || err == EACCES)
...@@ -1253,8 +1157,8 @@ static void start_counter(int i, struct perf_evsel *evsel) ...@@ -1253,8 +1157,8 @@ static void start_counter(int i, struct perf_evsel *evsel)
* based cpu-clock-tick sw counter, which * based cpu-clock-tick sw counter, which
* is always available even if no PMU support: * is always available even if no PMU support:
*/ */
if (attr->type == PERF_TYPE_HARDWARE if (attr->type == PERF_TYPE_HARDWARE &&
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) { attr->config == PERF_COUNT_HW_CPU_CYCLES) {
if (verbose) if (verbose)
warning(" ... trying to fall back to cpu-clock-ticks\n"); warning(" ... trying to fall back to cpu-clock-ticks\n");
...@@ -1264,39 +1168,23 @@ static void start_counter(int i, struct perf_evsel *evsel) ...@@ -1264,39 +1168,23 @@ static void start_counter(int i, struct perf_evsel *evsel)
goto try_again; goto try_again;
} }
printf("\n"); printf("\n");
error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", error("sys_perf_event_open() syscall returned with %d "
FD(evsel, i, thread_index), strerror(err)); "(%s). /bin/dmesg may provide additional information.\n",
err, strerror(err));
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
exit(-1); exit(-1);
} }
assert(FD(evsel, i, thread_index) >= 0);
fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK);
/*
* First counter acts as the group leader:
*/
if (group && group_fd == -1)
group_fd = FD(evsel, i, thread_index);
event_array[nr_poll].fd = FD(evsel, i, thread_index);
event_array[nr_poll].events = POLLIN;
nr_poll++;
mm = xyarray__entry(mmap_array, i, thread_index);
mm->prev = 0;
mm->mask = mmap_pages*page_size - 1;
mm->base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0);
if (mm->base == MAP_FAILED)
die("failed to mmap with %d (%s)\n", errno, strerror(errno));
} }
if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, true) < 0)
die("failed to mmap with %d (%s)\n", errno, strerror(errno));
} }
static int __cmd_top(void) static int __cmd_top(void)
{ {
pthread_t thread; pthread_t thread;
struct perf_evsel *counter; struct perf_evsel *first;
int i, ret; int ret;
/* /*
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this * FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now. * mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
...@@ -1310,14 +1198,12 @@ static int __cmd_top(void) ...@@ -1310,14 +1198,12 @@ static int __cmd_top(void)
else else
event__synthesize_threads(event__process, session); event__synthesize_threads(event__process, session);
for (i = 0; i < cpus->nr; i++) { start_counters(evsel_list);
group_fd = -1; first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
list_for_each_entry(counter, &evsel_list, node) perf_session__set_sample_type(session, first->attr.sample_type);
start_counter(i, counter);
}
/* Wait for a minimal set of events before starting the snapshot */ /* Wait for a minimal set of events before starting the snapshot */
poll(&event_array[0], nr_poll, 100); poll(evsel_list->pollfd, evsel_list->nr_fds, 100);
perf_session__mmap_read(session); perf_session__mmap_read(session);
...@@ -1342,7 +1228,7 @@ static int __cmd_top(void) ...@@ -1342,7 +1228,7 @@ static int __cmd_top(void)
perf_session__mmap_read(session); perf_session__mmap_read(session);
if (hits == samples) if (hits == samples)
ret = poll(event_array, nr_poll, 100); ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100);
} }
return 0; return 0;
...@@ -1354,7 +1240,7 @@ static const char * const top_usage[] = { ...@@ -1354,7 +1240,7 @@ static const char * const top_usage[] = {
}; };
static const struct option options[] = { static const struct option options[] = {
OPT_CALLBACK('e', "event", NULL, "event", OPT_CALLBACK('e', "event", &evsel_list, "event",
"event selector. use 'perf list' to list available events", "event selector. use 'perf list' to list available events",
parse_events), parse_events),
OPT_INTEGER('c', "count", &default_interval, OPT_INTEGER('c', "count", &default_interval,
...@@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
struct perf_evsel *pos; struct perf_evsel *pos;
int status = -ENOMEM; int status = -ENOMEM;
evsel_list = perf_evlist__new();
if (evsel_list == NULL)
return -ENOMEM;
page_size = sysconf(_SC_PAGE_SIZE); page_size = sysconf(_SC_PAGE_SIZE);
argc = parse_options(argc, argv, options, top_usage, 0); argc = parse_options(argc, argv, options, top_usage, 0);
...@@ -1419,11 +1309,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1419,11 +1309,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
usage_with_options(top_usage, options); usage_with_options(top_usage, options);
} }
event_array = malloc((sizeof(struct pollfd) *
MAX_NR_CPUS * MAX_COUNTERS * threads->nr));
if (!event_array)
return -ENOMEM;
/* CPU and PID are mutually exclusive */ /* CPU and PID are mutually exclusive */
if (target_tid > 0 && cpu_list) { if (target_tid > 0 && cpu_list) {
printf("WARNING: PID switch overriding CPU\n"); printf("WARNING: PID switch overriding CPU\n");
...@@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
cpu_list = NULL; cpu_list = NULL;
} }
if (!nr_counters && perf_evsel_list__create_default() < 0) { if (!evsel_list->nr_entries &&
perf_evlist__add_default(evsel_list) < 0) {
pr_err("Not enough memory for event selector list\n"); pr_err("Not enough memory for event selector list\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
if (cpus == NULL) if (cpus == NULL)
usage_with_options(top_usage, options); usage_with_options(top_usage, options);
list_for_each_entry(pos, &evsel_list, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
goto out_free_fd; goto out_free_fd;
/* /*
* Fill in the ones not specifically initialized via -c: * Fill in the ones not specifically initialized via -c:
...@@ -1472,10 +1357,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1472,10 +1357,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
pos->attr.sample_period = default_interval; pos->attr.sample_period = default_interval;
} }
sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0 ||
perf_evlist__alloc_mmap(evsel_list, cpus->nr) < 0)
goto out_free_fd;
sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node);
symbol_conf.priv_size = (sizeof(struct sym_entry) + symbol_conf.priv_size = (sizeof(struct sym_entry) +
(nr_counters + 1) * sizeof(unsigned long)); (evsel_list->nr_entries + 1) * sizeof(unsigned long));
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (symbol__init() < 0) if (symbol__init() < 0)
...@@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
status = __cmd_top(); status = __cmd_top();
out_free_fd: out_free_fd:
list_for_each_entry(pos, &evsel_list, node) perf_evlist__delete(evsel_list);
perf_evsel__free_mmap(pos);
perf_evsel_list__delete();
return status; return status;
} }
...@@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws); ...@@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws);
#include "util/types.h" #include "util/types.h"
#include <stdbool.h> #include <stdbool.h>
struct perf_mmap {
void *base;
int mask;
unsigned int prev;
};
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
{
struct perf_event_mmap_page *pc = mm->base;
int head = pc->data_head;
rmb();
return head;
}
static inline void perf_mmap__write_tail(struct perf_mmap *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;
}
/* /*
* prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
* counters in the current task. * counters in the current task.
......
/* /*
* Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com>
* *
* Handle the callchains from the stream in an ad-hoc radix tree and then * Handle the callchains from the stream in an ad-hoc radix tree and then
* sort them in an rbtree. * sort them in an rbtree.
...@@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) ...@@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
} }
#define chain_for_each_child(child, parent) \ #define chain_for_each_child(child, parent) \
list_for_each_entry(child, &parent->children, brothers) list_for_each_entry(child, &parent->children, siblings)
#define chain_for_each_child_safe(child, next, parent) \ #define chain_for_each_child_safe(child, next, parent) \
list_for_each_entry_safe(child, next, &parent->children, brothers) list_for_each_entry_safe(child, next, &parent->children, siblings)
static void static void
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
...@@ -38,14 +38,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, ...@@ -38,14 +38,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
struct rb_node **p = &root->rb_node; struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct callchain_node *rnode; struct callchain_node *rnode;
u64 chain_cumul = cumul_hits(chain); u64 chain_cumul = callchain_cumul_hits(chain);
while (*p) { while (*p) {
u64 rnode_cumul; u64 rnode_cumul;
parent = *p; parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node); rnode = rb_entry(parent, struct callchain_node, rb_node);
rnode_cumul = cumul_hits(rnode); rnode_cumul = callchain_cumul_hits(rnode);
switch (mode) { switch (mode) {
case CHAIN_FLAT: case CHAIN_FLAT:
...@@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, ...@@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
chain_for_each_child(child, node) { chain_for_each_child(child, node) {
__sort_chain_graph_abs(child, min_hit); __sort_chain_graph_abs(child, min_hit);
if (cumul_hits(child) >= min_hit) if (callchain_cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child, rb_insert_callchain(&node->rb_root, child,
CHAIN_GRAPH_ABS); CHAIN_GRAPH_ABS);
} }
...@@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, ...@@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
chain_for_each_child(child, node) { chain_for_each_child(child, node) {
__sort_chain_graph_rel(child, min_percent); __sort_chain_graph_rel(child, min_percent);
if (cumul_hits(child) >= min_hit) if (callchain_cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child, rb_insert_callchain(&node->rb_root, child,
CHAIN_GRAPH_REL); CHAIN_GRAPH_REL);
} }
...@@ -143,7 +143,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, ...@@ -143,7 +143,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
rb_root->rb_node = chain_root->node.rb_root.rb_node; rb_root->rb_node = chain_root->node.rb_root.rb_node;
} }
int register_callchain_param(struct callchain_param *param) int callchain_register_param(struct callchain_param *param)
{ {
switch (param->mode) { switch (param->mode) {
case CHAIN_GRAPH_ABS: case CHAIN_GRAPH_ABS:
...@@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children) ...@@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children)
chain_for_each_child(next, new) chain_for_each_child(next, new)
next->parent = new; next->parent = new;
} }
list_add_tail(&new->brothers, &parent->children); list_add_tail(&new->siblings, &parent->children);
return new; return new;
} }
struct resolved_ip {
u64 ip;
struct map_symbol ms;
};
struct resolved_chain {
u64 nr;
struct resolved_ip ips[0];
};
/* /*
* Fill the node with callchain values * Fill the node with callchain values
*/ */
static void static void
fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
{ {
unsigned int i; struct callchain_cursor_node *cursor_node;
node->val_nr = cursor->nr - cursor->pos;
if (!node->val_nr)
pr_warning("Warning: empty node in callchain tree\n");
for (i = start; i < chain->nr; i++) { cursor_node = callchain_cursor_current(cursor);
while (cursor_node) {
struct callchain_list *call; struct callchain_list *call;
call = zalloc(sizeof(*call)); call = zalloc(sizeof(*call));
...@@ -222,23 +217,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) ...@@ -222,23 +217,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
perror("not enough memory for the code path tree"); perror("not enough memory for the code path tree");
return; return;
} }
call->ip = chain->ips[i].ip; call->ip = cursor_node->ip;
call->ms = chain->ips[i].ms; call->ms.sym = cursor_node->sym;
call->ms.map = cursor_node->map;
list_add_tail(&call->list, &node->val); list_add_tail(&call->list, &node->val);
callchain_cursor_advance(cursor);
cursor_node = callchain_cursor_current(cursor);
} }
node->val_nr = chain->nr - start;
if (!node->val_nr)
pr_warning("Warning: empty node in callchain tree\n");
} }
static void static void
add_child(struct callchain_node *parent, struct resolved_chain *chain, add_child(struct callchain_node *parent,
int start, u64 period) struct callchain_cursor *cursor,
u64 period)
{ {
struct callchain_node *new; struct callchain_node *new;
new = create_child(parent, false); new = create_child(parent, false);
fill_node(new, chain, start); fill_node(new, cursor);
new->children_hit = 0; new->children_hit = 0;
new->hit = period; new->hit = period;
...@@ -250,9 +247,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, ...@@ -250,9 +247,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain,
* Then create another child to host the given callchain of new branch * Then create another child to host the given callchain of new branch
*/ */
static void static void
split_add_child(struct callchain_node *parent, struct resolved_chain *chain, split_add_child(struct callchain_node *parent,
struct callchain_list *to_split, int idx_parents, int idx_local, struct callchain_cursor *cursor,
u64 period) struct callchain_list *to_split,
u64 idx_parents, u64 idx_local, u64 period)
{ {
struct callchain_node *new; struct callchain_node *new;
struct list_head *old_tail; struct list_head *old_tail;
...@@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, ...@@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
/* split the hits */ /* split the hits */
new->hit = parent->hit; new->hit = parent->hit;
new->children_hit = parent->children_hit; new->children_hit = parent->children_hit;
parent->children_hit = cumul_hits(new); parent->children_hit = callchain_cumul_hits(new);
new->val_nr = parent->val_nr - idx_local; new->val_nr = parent->val_nr - idx_local;
parent->val_nr = idx_local; parent->val_nr = idx_local;
/* create a new child for the new branch if any */ /* create a new child for the new branch if any */
if (idx_total < chain->nr) { if (idx_total < cursor->nr) {
parent->hit = 0; parent->hit = 0;
add_child(parent, chain, idx_total, period); add_child(parent, cursor, period);
parent->children_hit += period; parent->children_hit += period;
} else { } else {
parent->hit = period; parent->hit = period;
...@@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, ...@@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
} }
static int static int
append_chain(struct callchain_node *root, struct resolved_chain *chain, append_chain(struct callchain_node *root,
unsigned int start, u64 period); struct callchain_cursor *cursor,
u64 period);
static void static void
append_chain_children(struct callchain_node *root, struct resolved_chain *chain, append_chain_children(struct callchain_node *root,
unsigned int start, u64 period) struct callchain_cursor *cursor,
u64 period)
{ {
struct callchain_node *rnode; struct callchain_node *rnode;
/* lookup in childrens */ /* lookup in childrens */
chain_for_each_child(rnode, root) { chain_for_each_child(rnode, root) {
unsigned int ret = append_chain(rnode, chain, start, period); unsigned int ret = append_chain(rnode, cursor, period);
if (!ret) if (!ret)
goto inc_children_hit; goto inc_children_hit;
} }
/* nothing in children, add to the current node */ /* nothing in children, add to the current node */
add_child(root, chain, start, period); add_child(root, cursor, period);
inc_children_hit: inc_children_hit:
root->children_hit += period; root->children_hit += period;
} }
static int static int
append_chain(struct callchain_node *root, struct resolved_chain *chain, append_chain(struct callchain_node *root,
unsigned int start, u64 period) struct callchain_cursor *cursor,
u64 period)
{ {
struct callchain_cursor_node *curr_snap = cursor->curr;
struct callchain_list *cnode; struct callchain_list *cnode;
unsigned int i = start; u64 start = cursor->pos;
bool found = false; bool found = false;
u64 matches;
/* /*
* Lookup in the current node * Lookup in the current node
...@@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, ...@@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain,
* anywhere inside a function. * anywhere inside a function.
*/ */
list_for_each_entry(cnode, &root->val, list) { list_for_each_entry(cnode, &root->val, list) {
struct callchain_cursor_node *node;
struct symbol *sym; struct symbol *sym;
if (i == chain->nr) node = callchain_cursor_current(cursor);
if (!node)
break; break;
sym = chain->ips[i].ms.sym; sym = node->sym;
if (cnode->ms.sym && sym) { if (cnode->ms.sym && sym) {
if (cnode->ms.sym->start != sym->start) if (cnode->ms.sym->start != sym->start)
break; break;
} else if (cnode->ip != chain->ips[i].ip) } else if (cnode->ip != node->ip)
break; break;
if (!found) if (!found)
found = true; found = true;
i++;
callchain_cursor_advance(cursor);
} }
/* matches not, relay on the parent */ /* matches not, relay on the parent */
if (!found) if (!found) {
cursor->curr = curr_snap;
cursor->pos = start;
return -1; return -1;
}
matches = cursor->pos - start;
/* we match only a part of the node. Split it and add the new chain */ /* we match only a part of the node. Split it and add the new chain */
if (i - start < root->val_nr) { if (matches < root->val_nr) {
split_add_child(root, chain, cnode, start, i - start, period); split_add_child(root, cursor, cnode, start, matches, period);
return 0; return 0;
} }
/* we match 100% of the path, increment the hit */ /* we match 100% of the path, increment the hit */
if (i - start == root->val_nr && i == chain->nr) { if (matches == root->val_nr && cursor->pos == cursor->nr) {
root->hit += period; root->hit += period;
return 0; return 0;
} }
/* We match the node and still have a part remaining */ /* We match the node and still have a part remaining */
append_chain_children(root, chain, i, period); append_chain_children(root, cursor, period);
return 0; return 0;
} }
static void filter_context(struct ip_callchain *old, struct resolved_chain *new, int callchain_append(struct callchain_root *root,
struct map_symbol *syms) struct callchain_cursor *cursor,
{ u64 period)
int i, j = 0;
for (i = 0; i < (int)old->nr; i++) {
if (old->ips[i] >= PERF_CONTEXT_MAX)
continue;
new->ips[j].ip = old->ips[i];
new->ips[j].ms = syms[i];
j++;
}
new->nr = j;
}
int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
struct map_symbol *syms, u64 period)
{ {
struct resolved_chain *filtered; if (!cursor->nr)
if (!chain->nr)
return 0; return 0;
filtered = zalloc(sizeof(*filtered) + callchain_cursor_commit(cursor);
chain->nr * sizeof(struct resolved_ip));
if (!filtered)
return -ENOMEM;
filter_context(chain, filtered, syms);
if (!filtered->nr)
goto end;
append_chain_children(&root->node, filtered, 0, period); append_chain_children(&root->node, cursor, period);
if (filtered->nr > root->max_depth) if (cursor->nr > root->max_depth)
root->max_depth = filtered->nr; root->max_depth = cursor->nr;
end:
free(filtered);
return 0; return 0;
} }
static int static int
merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, merge_chain_branch(struct callchain_cursor *cursor,
struct resolved_chain *chain) struct callchain_node *dst, struct callchain_node *src)
{ {
struct callchain_cursor_node **old_last = cursor->last;
struct callchain_node *child, *next_child; struct callchain_node *child, *next_child;
struct callchain_list *list, *next_list; struct callchain_list *list, *next_list;
int old_pos = chain->nr; int old_pos = cursor->nr;
int err = 0; int err = 0;
list_for_each_entry_safe(list, next_list, &src->val, list) { list_for_each_entry_safe(list, next_list, &src->val, list) {
chain->ips[chain->nr].ip = list->ip; callchain_cursor_append(cursor, list->ip,
chain->ips[chain->nr].ms = list->ms; list->ms.map, list->ms.sym);
chain->nr++;
list_del(&list->list); list_del(&list->list);
free(list); free(list);
} }
if (src->hit) if (src->hit) {
append_chain_children(dst, chain, 0, src->hit); callchain_cursor_commit(cursor);
append_chain_children(dst, cursor, src->hit);
}
chain_for_each_child_safe(child, next_child, src) { chain_for_each_child_safe(child, next_child, src) {
err = merge_chain_branch(dst, child, chain); err = merge_chain_branch(cursor, dst, child);
if (err) if (err)
break; break;
list_del(&child->brothers); list_del(&child->siblings);
free(child); free(child);
} }
chain->nr = old_pos; cursor->nr = old_pos;
cursor->last = old_last;
return err; return err;
} }
int callchain_merge(struct callchain_root *dst, struct callchain_root *src) int callchain_merge(struct callchain_cursor *cursor,
struct callchain_root *dst, struct callchain_root *src)
{
return merge_chain_branch(cursor, &dst->node, &src->node);
}
int callchain_cursor_append(struct callchain_cursor *cursor,
u64 ip, struct map *map, struct symbol *sym)
{ {
struct resolved_chain *chain; struct callchain_cursor_node *node = *cursor->last;
int err;
chain = malloc(sizeof(*chain) + if (!node) {
src->max_depth * sizeof(struct resolved_ip)); node = calloc(sizeof(*node), 1);
if (!chain) if (!node)
return -ENOMEM; return -ENOMEM;
chain->nr = 0; *cursor->last = node;
}
err = merge_chain_branch(&dst->node, &src->node, chain); node->ip = ip;
node->map = map;
node->sym = sym;
free(chain); cursor->nr++;
return err; cursor->last = &node->next;
return 0;
} }
...@@ -16,7 +16,7 @@ enum chain_mode { ...@@ -16,7 +16,7 @@ enum chain_mode {
struct callchain_node { struct callchain_node {
struct callchain_node *parent; struct callchain_node *parent;
struct list_head brothers; struct list_head siblings;
struct list_head children; struct list_head children;
struct list_head val; struct list_head val;
struct rb_node rb_node; /* to sort nodes in an rbtree */ struct rb_node rb_node; /* to sort nodes in an rbtree */
...@@ -49,9 +49,30 @@ struct callchain_list { ...@@ -49,9 +49,30 @@ struct callchain_list {
struct list_head list; struct list_head list;
}; };
/*
* A callchain cursor is a single linked list that
* let one feed a callchain progressively.
* It keeps persitent allocated entries to minimize
* allocations.
*/
struct callchain_cursor_node {
u64 ip;
struct map *map;
struct symbol *sym;
struct callchain_cursor_node *next;
};
struct callchain_cursor {
u64 nr;
struct callchain_cursor_node *first;
struct callchain_cursor_node **last;
u64 pos;
struct callchain_cursor_node *curr;
};
static inline void callchain_init(struct callchain_root *root) static inline void callchain_init(struct callchain_root *root)
{ {
INIT_LIST_HEAD(&root->node.brothers); INIT_LIST_HEAD(&root->node.siblings);
INIT_LIST_HEAD(&root->node.children); INIT_LIST_HEAD(&root->node.children);
INIT_LIST_HEAD(&root->node.val); INIT_LIST_HEAD(&root->node.val);
...@@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) ...@@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root)
root->max_depth = 0; root->max_depth = 0;
} }
static inline u64 cumul_hits(struct callchain_node *node) static inline u64 callchain_cumul_hits(struct callchain_node *node)
{ {
return node->hit + node->children_hit; return node->hit + node->children_hit;
} }
int register_callchain_param(struct callchain_param *param); int callchain_register_param(struct callchain_param *param);
int callchain_append(struct callchain_root *root, struct ip_callchain *chain, int callchain_append(struct callchain_root *root,
struct map_symbol *syms, u64 period); struct callchain_cursor *cursor,
int callchain_merge(struct callchain_root *dst, struct callchain_root *src); u64 period);
int callchain_merge(struct callchain_cursor *cursor,
struct callchain_root *dst, struct callchain_root *src);
bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
/*
* Initialize a cursor before adding entries inside, but keep
* the previously allocated entries as a cache.
*/
static inline void callchain_cursor_reset(struct callchain_cursor *cursor)
{
cursor->nr = 0;
cursor->last = &cursor->first;
}
int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
struct map *map, struct symbol *sym);
/* Close a cursor writing session. Initialize for the reader */
static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
{
cursor->curr = cursor->first;
cursor->pos = 0;
}
/* Cursor reading iteration helpers */
static inline struct callchain_cursor_node *
callchain_cursor_current(struct callchain_cursor *cursor)
{
if (cursor->pos == cursor->nr)
return NULL;
return cursor->curr;
}
static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
{
cursor->curr = cursor->curr->next;
cursor->pos++;
}
#endif /* __PERF_CALLCHAIN_H */ #endif /* __PERF_CALLCHAIN_H */
...@@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) ...@@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void)
return cpus; return cpus;
} }
void cpu_map__delete(struct cpu_map *map)
{
free(map);
}
...@@ -8,6 +8,6 @@ struct cpu_map { ...@@ -8,6 +8,6 @@ struct cpu_map {
struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__new(const char *cpu_list);
struct cpu_map *cpu_map__dummy_new(void); struct cpu_map *cpu_map__dummy_new(void);
void *cpu_map__delete(struct cpu_map *map); void cpu_map__delete(struct cpu_map *map);
#endif /* __PERF_CPUMAP_H */ #endif /* __PERF_CPUMAP_H */
...@@ -826,128 +826,3 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, ...@@ -826,128 +826,3 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
al->filtered = true; al->filtered = true;
return 0; return 0;
} }
static int event__parse_id_sample(const event_t *event,
struct perf_session *session,
struct sample_data *sample)
{
const u64 *array;
u64 type;
sample->cpu = sample->pid = sample->tid = -1;
sample->stream_id = sample->id = sample->time = -1ULL;
if (!session->sample_id_all)
return 0;
array = event->sample.array;
array += ((event->header.size -
sizeof(event->header)) / sizeof(u64)) - 1;
type = session->sample_type;
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
sample->cpu = *p;
array--;
}
if (type & PERF_SAMPLE_STREAM_ID) {
sample->stream_id = *array;
array--;
}
if (type & PERF_SAMPLE_ID) {
sample->id = *array;
array--;
}
if (type & PERF_SAMPLE_TIME) {
sample->time = *array;
array--;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
sample->pid = p[0];
sample->tid = p[1];
}
return 0;
}
int event__parse_sample(const event_t *event, struct perf_session *session,
struct sample_data *data)
{
const u64 *array;
u64 type;
if (event->header.type != PERF_RECORD_SAMPLE)
return event__parse_id_sample(event, session, data);
array = event->sample.array;
type = session->sample_type;
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;
}
...@@ -169,9 +169,10 @@ struct addr_location; ...@@ -169,9 +169,10 @@ struct addr_location;
int event__preprocess_sample(const event_t *self, struct perf_session *session, int event__preprocess_sample(const event_t *self, struct perf_session *session,
struct addr_location *al, struct sample_data *data, struct addr_location *al, struct sample_data *data,
symbol_filter_t filter); symbol_filter_t filter);
int event__parse_sample(const event_t *event, struct perf_session *session,
struct sample_data *sample);
const char *event__get_event_name(unsigned int id); const char *event__get_event_name(unsigned int id);
int event__parse_sample(const event_t *event, u64 type, bool sample_id_all,
struct sample_data *sample);
#endif /* __PERF_RECORD_H */ #endif /* __PERF_RECORD_H */
#include <poll.h>
#include "evlist.h"
#include "evsel.h"
#include "util.h"
#include <linux/bitops.h>
#include <linux/hash.h>
void perf_evlist__init(struct perf_evlist *evlist)
{
int i;
for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
INIT_HLIST_HEAD(&evlist->heads[i]);
INIT_LIST_HEAD(&evlist->entries);
}
struct perf_evlist *perf_evlist__new(void)
{
struct perf_evlist *evlist = zalloc(sizeof(*evlist));
if (evlist != NULL)
perf_evlist__init(evlist);
return evlist;
}
static void perf_evlist__purge(struct perf_evlist *evlist)
{
struct perf_evsel *pos, *n;
list_for_each_entry_safe(pos, n, &evlist->entries, node) {
list_del_init(&pos->node);
perf_evsel__delete(pos);
}
evlist->nr_entries = 0;
}
void perf_evlist__exit(struct perf_evlist *evlist)
{
free(evlist->mmap);
free(evlist->pollfd);
evlist->mmap = NULL;
evlist->pollfd = NULL;
}
void perf_evlist__delete(struct perf_evlist *evlist)
{
perf_evlist__purge(evlist);
perf_evlist__exit(evlist);
free(evlist);
}
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
{
list_add_tail(&entry->node, &evlist->entries);
++evlist->nr_entries;
}
int perf_evlist__add_default(struct perf_evlist *evlist)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
};
struct perf_evsel *evsel = perf_evsel__new(&attr, 0);
if (evsel == NULL)
return -ENOMEM;
perf_evlist__add(evlist, evsel);
return 0;
}
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads)
{
int nfds = ncpus * nthreads * evlist->nr_entries;
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
return evlist->pollfd != NULL ? 0 : -ENOMEM;
}
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
{
fcntl(fd, F_SETFL, O_NONBLOCK);
evlist->pollfd[evlist->nr_fds].fd = fd;
evlist->pollfd[evlist->nr_fds].events = POLLIN;
evlist->nr_fds++;
}
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
{
struct hlist_head *head;
struct hlist_node *pos;
struct perf_sample_id *sid;
int hash;
if (evlist->nr_entries == 1)
return list_entry(evlist->entries.next, struct perf_evsel, node);
hash = hash_64(id, PERF_EVLIST__HLIST_BITS);
head = &evlist->heads[hash];
hlist_for_each_entry(sid, pos, head, node)
if (sid->id == id)
return sid->evsel;
return NULL;
}
event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu)
{
/* XXX Move this to perf.c, making it generally available */
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
struct perf_mmap *md = &evlist->mmap[cpu];
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
event_t *event = NULL;
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 > md->mask / 2 || 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) {
size_t size;
event = (event_t *)&data[old & md->mask];
size = event->header.size;
/*
* Event straddles the mmap boundary -- header should always
* be inside due to u64 alignment of output.
*/
if ((old & md->mask) + size != ((old + size) & md->mask)) {
unsigned int offset = old;
unsigned int len = min(sizeof(*event), size), cpy;
void *dst = &evlist->event_copy;
do {
cpy = min(md->mask + 1 - (offset & md->mask), len);
memcpy(dst, &data[offset & md->mask], cpy);
offset += cpy;
dst += cpy;
len -= cpy;
} while (len);
event = &evlist->event_copy;
}
old += size;
}
md->prev = old;
return event;
}
#ifndef __PERF_EVLIST_H
#define __PERF_EVLIST_H 1
#include <linux/list.h>
#include "../perf.h"
#include "event.h"
struct pollfd;
#define PERF_EVLIST__HLIST_BITS 8
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
struct perf_evlist {
struct list_head entries;
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
int nr_entries;
int nr_fds;
int mmap_len;
event_t event_copy;
struct perf_mmap *mmap;
struct pollfd *pollfd;
};
struct perf_evsel;
struct perf_evlist *perf_evlist__new(void);
void perf_evlist__init(struct perf_evlist *evlist);
void perf_evlist__exit(struct perf_evlist *evlist);
void perf_evlist__delete(struct perf_evlist *evlist);
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
int perf_evlist__add_default(struct perf_evlist *evlist);
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads);
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
event_t *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu);
#endif /* __PERF_EVLIST_H */
#include "evsel.h" #include "evsel.h"
#include "evlist.h"
#include "../perf.h" #include "../perf.h"
#include "util.h" #include "util.h"
#include "cpumap.h" #include "cpumap.h"
#include "thread.h" #include "thread_map.h"
#include <unistd.h>
#include <sys/mman.h>
#include <linux/bitops.h>
#include <linux/hash.h>
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define SID(e, x, y) xyarray__entry(e->id, x, y)
void perf_evsel__init(struct perf_evsel *evsel,
struct perf_event_attr *attr, int idx)
{
evsel->idx = idx;
evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node);
}
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
{ {
struct perf_evsel *evsel = zalloc(sizeof(*evsel)); struct perf_evsel *evsel = zalloc(sizeof(*evsel));
if (evsel != NULL) { if (evsel != NULL)
evsel->idx = idx; perf_evsel__init(evsel, attr, idx);
evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node);
}
return evsel; return evsel;
} }
...@@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) ...@@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
return evsel->fd != NULL ? 0 : -ENOMEM; return evsel->fd != NULL ? 0 : -ENOMEM;
} }
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
return evsel->id != NULL ? 0 : -ENOMEM;
}
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
{ {
evsel->counts = zalloc((sizeof(*evsel->counts) + evsel->counts = zalloc((sizeof(*evsel->counts) +
...@@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) ...@@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel)
evsel->fd = NULL; evsel->fd = NULL;
} }
void perf_evsel__free_id(struct perf_evsel *evsel)
{
xyarray__delete(evsel->id);
evsel->id = NULL;
}
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
{ {
int cpu, thread; int cpu, thread;
...@@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) ...@@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
} }
} }
void perf_evsel__delete(struct perf_evsel *evsel) void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus)
{
int cpu;
for (cpu = 0; cpu < ncpus; cpu++) {
if (evlist->mmap[cpu].base != NULL) {
munmap(evlist->mmap[cpu].base, evlist->mmap_len);
evlist->mmap[cpu].base = NULL;
}
}
}
int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus)
{
evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap));
return evlist->mmap != NULL ? 0 : -ENOMEM;
}
void perf_evsel__exit(struct perf_evsel *evsel)
{ {
assert(list_empty(&evsel->node)); assert(list_empty(&evsel->node));
xyarray__delete(evsel->fd); xyarray__delete(evsel->fd);
xyarray__delete(evsel->id);
}
void perf_evsel__delete(struct perf_evsel *evsel)
{
perf_evsel__exit(evsel);
free(evsel); free(evsel);
} }
...@@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, ...@@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel,
} }
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
struct thread_map *threads) struct thread_map *threads, bool group, bool inherit)
{ {
int cpu, thread; int cpu, thread;
...@@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
return -1; return -1;
for (cpu = 0; cpu < cpus->nr; cpu++) { for (cpu = 0; cpu < cpus->nr; cpu++) {
int group_fd = -1;
evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit;
for (thread = 0; thread < threads->nr; thread++) { for (thread = 0; thread < threads->nr; thread++) {
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
threads->map[thread], threads->map[thread],
cpus->map[cpu], -1, 0); cpus->map[cpu],
group_fd, 0);
if (FD(evsel, cpu, thread) < 0) if (FD(evsel, cpu, thread) < 0)
goto out_close; goto out_close;
if (group && group_fd == -1)
group_fd = FD(evsel, cpu, thread);
} }
} }
...@@ -175,10 +232,9 @@ static struct { ...@@ -175,10 +232,9 @@ static struct {
.threads = { -1, }, .threads = { -1, },
}; };
int perf_evsel__open(struct perf_evsel *evsel, int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
struct cpu_map *cpus, struct thread_map *threads) struct thread_map *threads, bool group, bool inherit)
{ {
if (cpus == NULL) { if (cpus == NULL) {
/* Work around old compiler warnings about strict aliasing */ /* Work around old compiler warnings about strict aliasing */
cpus = &empty_cpu_map.map; cpus = &empty_cpu_map.map;
...@@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel, ...@@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel,
if (threads == NULL) if (threads == NULL)
threads = &empty_thread_map.map; threads = &empty_thread_map.map;
return __perf_evsel__open(evsel, cpus, threads); return __perf_evsel__open(evsel, cpus, threads, group, inherit);
}
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
struct cpu_map *cpus, bool group, bool inherit)
{
return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit);
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
struct thread_map *threads, bool group, bool inherit)
{
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit);
}
static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot,
int mask, int fd)
{
evlist->mmap[cpu].prev = 0;
evlist->mmap[cpu].mask = mask;
evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot,
MAP_SHARED, fd, 0);
if (evlist->mmap[cpu].base == MAP_FAILED)
return -1;
perf_evlist__add_pollfd(evlist, fd);
return 0;
}
static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, int fd)
{
struct perf_sample_id *sid;
u64 read_data[4] = { 0, };
int hash, id_idx = 1; /* The first entry is the counter value */
if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
read(fd, &read_data, sizeof(read_data)) == -1)
return -1;
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
++id_idx;
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
++id_idx;
sid = SID(evsel, cpu, thread);
sid->id = read_data[id_idx];
sid->evsel = evsel;
hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS);
hlist_add_head(&sid->node, &evlist->heads[hash]);
return 0;
}
/** perf_evlist__mmap - Create per cpu maps to receive events
*
* @evlist - list of events
* @cpus - cpu map being monitored
* @threads - threads map being monitored
* @pages - map length in pages
* @overwrite - overwrite older events?
*
* If overwrite is false the user needs to signal event consuption using:
*
* struct perf_mmap *m = &evlist->mmap[cpu];
* unsigned int head = perf_mmap__read_head(m);
*
* perf_mmap__write_tail(m, head)
*/
int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads, int pages, bool overwrite)
{
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
int mask = pages * page_size - 1, cpu;
struct perf_evsel *first_evsel, *evsel;
int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE);
if (evlist->mmap == NULL &&
perf_evlist__alloc_mmap(evlist, cpus->nr) < 0)
return -ENOMEM;
if (evlist->pollfd == NULL &&
perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0)
return -ENOMEM;
evlist->mmap_len = (pages + 1) * page_size;
first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
list_for_each_entry(evsel, &evlist->entries, node) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->id == NULL &&
perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0)
return -ENOMEM;
for (cpu = 0; cpu < cpus->nr; cpu++) {
for (thread = 0; thread < threads->nr; thread++) {
int fd = FD(evsel, cpu, thread);
if (evsel->idx || thread) {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT,
FD(first_evsel, cpu, 0)) != 0)
goto out_unmap;
} else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0)
goto out_unmap;
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0)
goto out_unmap;
}
}
}
return 0;
out_unmap:
for (cpu = 0; cpu < cpus->nr; cpu++) {
if (evlist->mmap[cpu].base != NULL) {
munmap(evlist->mmap[cpu].base, evlist->mmap_len);
evlist->mmap[cpu].base = NULL;
}
}
return -1;
} }
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) static int event__parse_id_sample(const event_t *event, u64 type,
struct sample_data *sample)
{ {
return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); const u64 *array = event->sample.array;
array += ((event->header.size -
sizeof(event->header)) / sizeof(u64)) - 1;
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
sample->cpu = *p;
array--;
}
if (type & PERF_SAMPLE_STREAM_ID) {
sample->stream_id = *array;
array--;
}
if (type & PERF_SAMPLE_ID) {
sample->id = *array;
array--;
}
if (type & PERF_SAMPLE_TIME) {
sample->time = *array;
array--;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
sample->pid = p[0];
sample->tid = p[1];
}
return 0;
} }
int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) int event__parse_sample(const event_t *event, u64 type, bool sample_id_all,
struct sample_data *data)
{ {
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); const u64 *array;
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
if (event->header.type != PERF_RECORD_SAMPLE) {
if (!sample_id_all)
return 0;
return event__parse_id_sample(event, type, data);
}
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++;
}
if (type & PERF_SAMPLE_PERIOD) {
data->period = *array;
array++;
}
if (type & PERF_SAMPLE_READ) {
fprintf(stderr, "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;
} }
...@@ -24,11 +24,24 @@ struct perf_counts { ...@@ -24,11 +24,24 @@ struct perf_counts {
struct perf_counts_values cpu[]; struct perf_counts_values cpu[];
}; };
struct perf_evsel;
/*
* Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are
* more than one entry in the evlist.
*/
struct perf_sample_id {
struct hlist_node node;
u64 id;
struct perf_evsel *evsel;
};
struct perf_evsel { struct perf_evsel {
struct list_head node; struct list_head node;
struct perf_event_attr attr; struct perf_event_attr attr;
char *filter; char *filter;
struct xyarray *fd; struct xyarray *fd;
struct xyarray *id;
struct perf_counts *counts; struct perf_counts *counts;
int idx; int idx;
void *priv; void *priv;
...@@ -36,19 +49,31 @@ struct perf_evsel { ...@@ -36,19 +49,31 @@ struct perf_evsel {
struct cpu_map; struct cpu_map;
struct thread_map; struct thread_map;
struct perf_evlist;
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
void perf_evsel__init(struct perf_evsel *evsel,
struct perf_event_attr *attr, int idx);
void perf_evsel__exit(struct perf_evsel *evsel);
void perf_evsel__delete(struct perf_evsel *evsel); void perf_evsel__delete(struct perf_evsel *evsel);
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus);
void perf_evsel__free_fd(struct perf_evsel *evsel); void perf_evsel__free_fd(struct perf_evsel *evsel);
void perf_evsel__free_id(struct perf_evsel *evsel);
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); struct cpu_map *cpus, bool group, bool inherit);
int perf_evsel__open(struct perf_evsel *evsel, int perf_evsel__open_per_thread(struct perf_evsel *evsel,
struct cpu_map *cpus, struct thread_map *threads); struct thread_map *threads, bool group, bool inherit);
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
struct thread_map *threads, bool group, bool inherit);
int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads, int pages, bool overwrite);
void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus);
#define perf_evsel__match(evsel, t, c) \ #define perf_evsel__match(evsel, t, c) \
(evsel->attr.type == PERF_TYPE_##t && \ (evsel->attr.type == PERF_TYPE_##t && \
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include "evlist.h"
#include "util.h" #include "util.h"
#include "header.h" #include "header.h"
#include "../perf.h" #include "../perf.h"
...@@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi ...@@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi
return ret; return ret;
} }
static int perf_header__adds_write(struct perf_header *self, int fd) static int perf_header__adds_write(struct perf_header *self,
struct perf_evlist *evlist, int fd)
{ {
int nr_sections; int nr_sections;
struct perf_session *session; struct perf_session *session;
...@@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) ...@@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
/* Write trace info */ /* Write trace info */
trace_sec->offset = lseek(fd, 0, SEEK_CUR); trace_sec->offset = lseek(fd, 0, SEEK_CUR);
read_tracing_data(fd, &evsel_list); read_tracing_data(fd, &evlist->entries);
trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
} }
...@@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd) ...@@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd)
return 0; return 0;
} }
int perf_header__write(struct perf_header *self, int fd, bool at_exit) int perf_header__write(struct perf_header *self, struct perf_evlist *evlist,
int fd, bool at_exit)
{ {
struct perf_file_header f_header; struct perf_file_header f_header;
struct perf_file_attr f_attr; struct perf_file_attr f_attr;
...@@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) ...@@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit)
self->data_offset = lseek(fd, 0, SEEK_CUR); self->data_offset = lseek(fd, 0, SEEK_CUR);
if (at_exit) { if (at_exit) {
err = perf_header__adds_write(self, fd); err = perf_header__adds_write(self, evlist, fd);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self, ...@@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self,
return 0; return 0;
} }
int event__synthesize_tracing_data(int fd, struct list_head *pattrs, int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
event__handler_t process, event__handler_t process,
struct perf_session *session __unused) struct perf_session *session __unused)
{ {
...@@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, ...@@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
memset(&ev, 0, sizeof(ev)); memset(&ev, 0, sizeof(ev));
ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
size = read_tracing_data_size(fd, pattrs); size = read_tracing_data_size(fd, &evlist->entries);
if (size <= 0) if (size <= 0)
return size; return size;
aligned_size = ALIGN(size, sizeof(u64)); aligned_size = ALIGN(size, sizeof(u64));
...@@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, ...@@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
process(&ev, NULL, session); process(&ev, NULL, session);
err = read_tracing_data(fd, pattrs); err = read_tracing_data(fd, &evlist->entries);
write_padded(fd, NULL, 0, padding); write_padded(fd, NULL, 0, padding);
return aligned_size; return aligned_size;
......
...@@ -65,8 +65,11 @@ struct perf_header { ...@@ -65,8 +65,11 @@ struct perf_header {
int perf_header__init(struct perf_header *self); int perf_header__init(struct perf_header *self);
void perf_header__exit(struct perf_header *self); void perf_header__exit(struct perf_header *self);
struct perf_evlist;
int perf_header__read(struct perf_session *session, int fd); 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(struct perf_header *self, struct perf_evlist *evlist,
int fd, bool at_exit);
int perf_header__write_pipe(int fd); int perf_header__write_pipe(int fd);
int perf_header__add_attr(struct perf_header *self, int perf_header__add_attr(struct perf_header *self,
...@@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process, ...@@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process,
int event__process_event_type(event_t *self, int event__process_event_type(event_t *self,
struct perf_session *session); struct perf_session *session);
int event__synthesize_tracing_data(int fd, struct list_head *pattrs, int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
event__handler_t process, event__handler_t process,
struct perf_session *session); struct perf_session *session);
int event__process_tracing_data(event_t *self, int event__process_tracing_data(event_t *self,
......
...@@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he) ...@@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram * collapse the histogram
*/ */
static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) static bool hists__collapse_insert_entry(struct hists *self,
struct rb_root *root,
struct hist_entry *he)
{ {
struct rb_node **p = &root->rb_node; struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
...@@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) ...@@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
if (!cmp) { if (!cmp) {
iter->period += he->period; iter->period += he->period;
if (symbol_conf.use_callchain) if (symbol_conf.use_callchain) {
callchain_merge(iter->callchain, he->callchain); callchain_cursor_reset(&self->callchain_cursor);
callchain_merge(&self->callchain_cursor, iter->callchain,
he->callchain);
}
hist_entry__free(he); hist_entry__free(he);
return false; return false;
} }
...@@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self) ...@@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self)
next = rb_next(&n->rb_node); next = rb_next(&n->rb_node);
rb_erase(&n->rb_node, &self->entries); rb_erase(&n->rb_node, &self->entries);
if (collapse__insert_entry(&tmp, n)) if (hists__collapse_insert_entry(self, &tmp, n))
hists__inc_nr_entries(self, n); hists__inc_nr_entries(self, n);
} }
...@@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ...@@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
u64 cumul; u64 cumul;
child = rb_entry(node, struct callchain_node, rb_node); child = rb_entry(node, struct callchain_node, rb_node);
cumul = cumul_hits(child); cumul = callchain_cumul_hits(child);
remaining -= cumul; remaining -= cumul;
/* /*
......
...@@ -77,6 +77,8 @@ struct hists { ...@@ -77,6 +77,8 @@ struct hists {
u64 event_stream; u64 event_stream;
u32 type; u32 type;
u16 col_len[HISTC_NR_COLS]; u16 col_len[HISTC_NR_COLS];
/* Best would be to reuse the session callchain cursor */
struct callchain_cursor callchain_cursor;
}; };
struct hist_entry *__hists__add_entry(struct hists *self, struct hist_entry *__hists__add_entry(struct hists *self,
......
#include <linux/kernel.h>
#include "../../../../include/linux/list.h" #include "../../../../include/linux/list.h"
#ifndef PERF_LIST_H #ifndef PERF_LIST_H
......
#include "../../../include/linux/hw_breakpoint.h" #include "../../../include/linux/hw_breakpoint.h"
#include "util.h" #include "util.h"
#include "../perf.h" #include "../perf.h"
#include "evlist.h"
#include "evsel.h" #include "evsel.h"
#include "parse-options.h" #include "parse-options.h"
#include "parse-events.h" #include "parse-events.h"
...@@ -11,10 +12,6 @@ ...@@ -11,10 +12,6 @@
#include "header.h" #include "header.h"
#include "debugfs.h" #include "debugfs.h"
int nr_counters;
LIST_HEAD(evsel_list);
struct event_symbol { struct event_symbol {
u8 type; u8 type;
u64 config; u64 config;
...@@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name, ...@@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name,
/* sys + ':' + event + ':' + flags*/ /* sys + ':' + event + ':' + flags*/
#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
static enum event_result static enum event_result
parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, parse_multiple_tracepoint_event(const struct option *opt, char *sys_name,
char *flags) const char *evt_exp, char *flags)
{ {
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
struct dirent *evt_ent; struct dirent *evt_ent;
...@@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, ...@@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
if (len < 0) if (len < 0)
return EVT_FAILED; return EVT_FAILED;
if (parse_events(NULL, event_opt, 0)) if (parse_events(opt, event_opt, 0))
return EVT_FAILED; return EVT_FAILED;
} }
return EVT_HANDLED_ALL; return EVT_HANDLED_ALL;
} }
static enum event_result parse_tracepoint_event(const char **strp, static enum event_result
struct perf_event_attr *attr) parse_tracepoint_event(const struct option *opt, const char **strp,
struct perf_event_attr *attr)
{ {
const char *evt_name; const char *evt_name;
char *flags = NULL, *comma_loc; char *flags = NULL, *comma_loc;
...@@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, ...@@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp,
return EVT_FAILED; return EVT_FAILED;
if (strpbrk(evt_name, "*?")) { if (strpbrk(evt_name, "*?")) {
*strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
return parse_multiple_tracepoint_event(sys_name, evt_name, return parse_multiple_tracepoint_event(opt, sys_name, evt_name,
flags); flags);
} else { } else {
return parse_single_tracepoint_event(sys_name, evt_name, return parse_single_tracepoint_event(sys_name, evt_name,
...@@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) ...@@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
* Symbolic names are (almost) exactly matched. * Symbolic names are (almost) exactly matched.
*/ */
static enum event_result static enum event_result
parse_event_symbols(const char **str, struct perf_event_attr *attr) parse_event_symbols(const struct option *opt, const char **str,
struct perf_event_attr *attr)
{ {
enum event_result ret; enum event_result ret;
ret = parse_tracepoint_event(str, attr); ret = parse_tracepoint_event(opt, str, attr);
if (ret != EVT_FAILED) if (ret != EVT_FAILED)
goto modifier; goto modifier;
...@@ -778,14 +777,15 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) ...@@ -778,14 +777,15 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr)
return ret; return ret;
} }
int parse_events(const struct option *opt __used, const char *str, int unset __used) int parse_events(const struct option *opt, const char *str, int unset __used)
{ {
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
struct perf_event_attr attr; struct perf_event_attr attr;
enum event_result ret; enum event_result ret;
for (;;) { for (;;) {
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
ret = parse_event_symbols(&str, &attr); ret = parse_event_symbols(opt, &str, &attr);
if (ret == EVT_FAILED) if (ret == EVT_FAILED)
return -1; return -1;
...@@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u ...@@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u
if (ret != EVT_HANDLED_ALL) { if (ret != EVT_HANDLED_ALL) {
struct perf_evsel *evsel; struct perf_evsel *evsel;
evsel = perf_evsel__new(&attr, evsel = perf_evsel__new(&attr, evlist->nr_entries);
nr_counters);
if (evsel == NULL) if (evsel == NULL)
return -1; return -1;
list_add_tail(&evsel->node, &evsel_list); perf_evlist__add(evlist, evsel);
++nr_counters;
} }
if (*str == 0) if (*str == 0)
...@@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u ...@@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u
return 0; return 0;
} }
int parse_filter(const struct option *opt __used, const char *str, int parse_filter(const struct option *opt, const char *str,
int unset __used) int unset __used)
{ {
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
struct perf_evsel *last = NULL; struct perf_evsel *last = NULL;
if (!list_empty(&evsel_list)) if (evlist->nr_entries > 0)
last = list_entry(evsel_list.prev, struct perf_evsel, node); last = list_entry(evlist->entries.prev, struct perf_evsel, node);
if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
fprintf(stderr, fprintf(stderr,
...@@ -981,33 +980,3 @@ void print_events(void) ...@@ -981,33 +980,3 @@ void print_events(void)
exit(129); exit(129);
} }
int perf_evsel_list__create_default(void)
{
struct perf_evsel *evsel;
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CPU_CYCLES;
evsel = perf_evsel__new(&attr, 0);
if (evsel == NULL)
return -ENOMEM;
list_add(&evsel->node, &evsel_list);
++nr_counters;
return 0;
}
void perf_evsel_list__delete(void)
{
struct perf_evsel *pos, *n;
list_for_each_entry_safe(pos, n, &evsel_list, node) {
list_del_init(&pos->node);
perf_evsel__delete(pos);
}
nr_counters = 0;
}
...@@ -9,11 +9,6 @@ ...@@ -9,11 +9,6 @@
struct list_head; struct list_head;
struct perf_evsel; struct perf_evsel;
extern struct list_head evsel_list;
int perf_evsel_list__create_default(void);
void perf_evsel_list__delete(void);
struct option; struct option;
struct tracepoint_path { struct tracepoint_path {
...@@ -25,8 +20,6 @@ struct tracepoint_path { ...@@ -25,8 +20,6 @@ struct tracepoint_path {
extern struct tracepoint_path *tracepoint_id_to_path(u64 config); extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern bool have_tracepoints(struct list_head *evlist); extern bool have_tracepoints(struct list_head *evlist);
extern int nr_counters;
const char *event_name(struct perf_evsel *event); const char *event_name(struct perf_evsel *event);
extern const char *__event_name(int type, u64 config); extern const char *__event_name(int type, u64 config);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <limits.h> #include <limits.h>
#include <elf.h>
#undef _GNU_SOURCE #undef _GNU_SOURCE
#include "util.h" #include "util.h"
...@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, ...@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name,
NULL); NULL);
} }
const char *kernel_get_module_path(const char *module) static struct map *kernel_get_module_map(const char *module)
{
struct rb_node *nd;
struct map_groups *grp = &machine.kmaps;
if (!module)
module = "kernel";
for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) {
struct map *pos = rb_entry(nd, struct map, rb_node);
if (strncmp(pos->dso->short_name + 1, module,
pos->dso->short_name_len - 2) == 0) {
return pos;
}
}
return NULL;
}
static struct dso *kernel_get_module_dso(const char *module)
{ {
struct dso *dso; struct dso *dso;
struct map *map; struct map *map;
...@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) ...@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module)
} }
} }
found: found:
return dso->long_name; return dso;
}
const char *kernel_get_module_path(const char *module)
{
struct dso *dso = kernel_get_module_dso(module);
return (dso) ? dso->long_name : NULL;
} }
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
...@@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist) ...@@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist)
return ret; return ret;
} }
/*
* If a symbol corresponds to a function with global binding return 0.
* For all others return 1.
*/
static int filter_non_global_functions(struct map *map __unused,
struct symbol *sym)
{
if (sym->binding != STB_GLOBAL)
return 1;
return 0;
}
int show_available_funcs(const char *module)
{
struct map *map;
int ret;
setup_pager();
ret = init_vmlinux();
if (ret < 0)
return ret;
map = kernel_get_module_map(module);
if (!map) {
pr_err("Failed to find %s map.\n", (module) ? : "kernel");
return -EINVAL;
}
if (map__load(map, filter_non_global_functions)) {
pr_err("Failed to load map.\n");
return -EINVAL;
}
if (!dso__sorted_by_name(map->dso, map->type))
dso__sort_by_name(map->dso, map->type);
dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
return 0;
}
...@@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module); ...@@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs, extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
int max_probe_points, const char *module, int max_probe_points, const char *module,
bool externs); bool externs);
extern int show_available_funcs(const char *module);
/* Maximum index number of event-name postfix */ /* Maximum index number of event-name postfix */
......
...@@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) ...@@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
return name ? (strcmp(tname, name) == 0) : false; return name ? (strcmp(tname, name) == 0) : false;
} }
/* Get callsite line number of inline-function instance */
static int die_get_call_lineno(Dwarf_Die *in_die)
{
Dwarf_Attribute attr;
Dwarf_Word ret;
if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
return -ENOENT;
dwarf_formudata(&attr, &ret);
return (int)ret;
}
/* Get type die */ /* Get type die */
static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{ {
...@@ -458,6 +471,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, ...@@ -458,6 +471,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
} }
/* Walker on lines (Note: line number will not be sorted) */
typedef int (* line_walk_handler_t) (const char *fname, int lineno,
Dwarf_Addr addr, void *data);
struct __line_walk_param {
const char *fname;
line_walk_handler_t handler;
void *data;
int retval;
};
static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
struct __line_walk_param *lw = data;
Dwarf_Addr addr;
int lineno;
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
lineno = die_get_call_lineno(in_die);
if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
lw->retval = lw->handler(lw->fname, lineno, addr,
lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_FOUND;
}
}
return DIE_FIND_CB_SIBLING;
}
/* Walk on lines of blocks included in given DIE */
static int __die_walk_funclines(Dwarf_Die *sp_die,
line_walk_handler_t handler, void *data)
{
struct __line_walk_param lw = {
.handler = handler,
.data = data,
.retval = 0,
};
Dwarf_Die die_mem;
Dwarf_Addr addr;
int lineno;
/* Handle function declaration line */
lw.fname = dwarf_decl_file(sp_die);
if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
dwarf_entrypc(sp_die, &addr) == 0) {
lw.retval = handler(lw.fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
}
die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
done:
return lw.retval;
}
static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
struct __line_walk_param *lw = data;
lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
return DWARF_CB_OK;
}
/*
* Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
* the lines inside the subprogram, otherwise PDIE must be a CU DIE.
*/
static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
void *data)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
Dwarf_Addr addr;
const char *fname;
int lineno, ret = 0;
Dwarf_Die die_mem, *cu_die;
size_t nlines, i;
/* Get the CU die */
if (dwarf_tag(pdie) == DW_TAG_subprogram)
cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
else
cu_die = pdie;
if (!cu_die) {
pr_debug2("Failed to get CU from subprogram\n");
return -EINVAL;
}
/* Get lines list in the CU */
if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
pr_debug2("Failed to get source lines on this CU.\n");
return -ENOENT;
}
pr_debug2("Get %zd lines from this CU\n", nlines);
/* Walk on the lines on lines list */
for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
if (line == NULL ||
dwarf_lineno(line, &lineno) != 0 ||
dwarf_lineaddr(line, &addr) != 0) {
pr_debug2("Failed to get line info. "
"Possible error in debuginfo.\n");
continue;
}
/* Filter lines based on address */
if (pdie != cu_die)
/*
* Address filtering
* The line is included in given function, and
* no inline block includes it.
*/
if (!dwarf_haspc(pdie, addr) ||
die_find_inlinefunc(pdie, addr, &die_mem))
continue;
/* Get source line */
fname = dwarf_linesrc(line, NULL, NULL);
ret = handler(fname, lineno, addr, data);
if (ret != 0)
return ret;
}
/*
* Dwarf lines doesn't include function declarations and inlined
* subroutines. We have to check functions list or given function.
*/
if (pdie != cu_die)
ret = __die_walk_funclines(pdie, handler, data);
else {
struct __line_walk_param param = {
.handler = handler,
.data = data,
.retval = 0,
};
dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
ret = param.retval;
}
return ret;
}
struct __find_variable_param { struct __find_variable_param {
const char *name; const char *name;
Dwarf_Addr addr; Dwarf_Addr addr;
...@@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) ...@@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
return ret; return ret;
} }
/* Find probe point from its line number */ static int probe_point_line_walker(const char *fname, int lineno,
static int find_probe_point_by_line(struct probe_finder *pf) Dwarf_Addr addr, void *data)
{ {
Dwarf_Lines *lines; struct probe_finder *pf = data;
Dwarf_Line *line; int ret;
size_t nlines, i;
Dwarf_Addr addr;
int lineno;
int ret = 0;
if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
pr_warning("No source lines found.\n");
return -ENOENT;
}
for (i = 0; i < nlines && ret == 0; i++) { if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
line = dwarf_onesrcline(lines, i); return 0;
if (dwarf_lineno(line, &lineno) != 0 ||
lineno != pf->lno)
continue;
/* TODO: Get fileno from line, but how? */ pf->addr = addr;
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) ret = call_probe_finder(NULL, pf);
continue;
if (dwarf_lineaddr(line, &addr) != 0) { /* Continue if no error, because the line will be in inline function */
pr_warning("Failed to get the address of the line.\n"); return ret < 0 ?: 0;
return -ENOENT; }
}
pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
(int)i, lineno, (uintmax_t)addr);
pf->addr = addr;
ret = call_probe_finder(NULL, pf); /* Find probe point from its line number */
/* Continuing, because target line might be inlined. */ static int find_probe_point_by_line(struct probe_finder *pf)
} {
return ret; return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
} }
/* Find lines which match lazy pattern */ /* Find lines which match lazy pattern */
...@@ -1140,15 +1281,31 @@ static int find_lazy_match_lines(struct list_head *head, ...@@ -1140,15 +1281,31 @@ static int find_lazy_match_lines(struct list_head *head,
return nlines; return nlines;
} }
static int probe_point_lazy_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
struct probe_finder *pf = data;
int ret;
if (!line_list__has_line(&pf->lcache, lineno) ||
strtailcmp(fname, pf->fname) != 0)
return 0;
pr_debug("Probe line found: line:%d addr:0x%llx\n",
lineno, (unsigned long long)addr);
pf->addr = addr;
ret = call_probe_finder(NULL, pf);
/*
* Continue if no error, because the lazy pattern will match
* to other lines
*/
return ret < 0 ?: 0;
}
/* Find probe points from lazy pattern */ /* Find probe points from lazy pattern */
static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
{ {
Dwarf_Lines *lines;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
Dwarf_Die die_mem;
int lineno;
int ret = 0; int ret = 0;
if (list_empty(&pf->lcache)) { if (list_empty(&pf->lcache)) {
...@@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) ...@@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
return ret; return ret;
} }
if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
pr_warning("No source lines found.\n");
return -ENOENT;
}
for (i = 0; i < nlines && ret >= 0; i++) {
line = dwarf_onesrcline(lines, i);
if (dwarf_lineno(line, &lineno) != 0 ||
!line_list__has_line(&pf->lcache, lineno))
continue;
/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;
if (dwarf_lineaddr(line, &addr) != 0) {
pr_debug("Failed to get the address of line %d.\n",
lineno);
continue;
}
if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
if (!dwarf_haspc(sp_die, addr))
continue;
/* Address filtering 2: No child include addr? */
if (die_find_inlinefunc(sp_die, addr, &die_mem))
continue;
}
pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
(int)i, lineno, (unsigned long long)addr);
pf->addr = addr;
ret = call_probe_finder(sp_die, pf);
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
return ret;
} }
/* Callback parameter with return value */ /* Callback parameter with return value */
...@@ -1644,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, ...@@ -1644,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno,
return line_list__add_line(&lr->line_list, lineno); return line_list__add_line(&lr->line_list, lineno);
} }
/* Search function declaration lines */ static int line_range_walk_cb(const char *fname, int lineno,
static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) Dwarf_Addr addr __used,
void *data)
{ {
struct dwarf_callback_param *param = data; struct line_finder *lf = data;
struct line_finder *lf = param->data;
const char *src;
int lineno;
src = dwarf_decl_file(sp_die); if ((strtailcmp(fname, lf->fname) != 0) ||
if (src && strtailcmp(src, lf->fname) != 0)
return DWARF_CB_OK;
if (dwarf_decl_line(sp_die, &lineno) != 0 ||
(lf->lno_s > lineno || lf->lno_e < lineno)) (lf->lno_s > lineno || lf->lno_e < lineno))
return DWARF_CB_OK; return 0;
param->retval = line_range_add_line(src, lineno, lf->lr); if (line_range_add_line(fname, lineno, lf->lr) < 0)
if (param->retval < 0) return -EINVAL;
return DWARF_CB_ABORT;
return DWARF_CB_OK;
}
static int find_line_range_func_decl_lines(struct line_finder *lf) return 0;
{
struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
return param.retval;
} }
/* Find line range from its line number */ /* Find line range from its line number */
static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
{ {
Dwarf_Lines *lines; int ret;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
int lineno, ret = 0;
const char *src;
Dwarf_Die die_mem;
line_list__init(&lf->lr->line_list);
if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
pr_warning("No source lines found.\n");
return -ENOENT;
}
/* Search probable lines on lines list */
for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
if (dwarf_lineno(line, &lineno) != 0 ||
(lf->lno_s > lineno || lf->lno_e < lineno))
continue;
if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
if (dwarf_lineaddr(line, &addr) != 0 ||
!dwarf_haspc(sp_die, addr))
continue;
/* Address filtering 2: No child include addr? */
if (die_find_inlinefunc(sp_die, addr, &die_mem))
continue;
}
/* TODO: Get fileno from line, but how? */
src = dwarf_linesrc(line, NULL, NULL);
if (strtailcmp(src, lf->fname) != 0)
continue;
ret = line_range_add_line(src, lineno, lf->lr);
if (ret < 0)
return ret;
}
/* ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);
* Dwarf lines doesn't include function declarations. We have to
* check functions list or given function.
*/
if (sp_die) {
src = dwarf_decl_file(sp_die);
if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
(lf->lno_s <= lineno && lf->lno_e >= lineno))
ret = line_range_add_line(src, lineno, lf->lr);
} else
ret = find_line_range_func_decl_lines(lf);
/* Update status */ /* Update status */
if (ret >= 0) if (ret >= 0)
...@@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) ...@@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
struct line_finder *lf = param->data; struct line_finder *lf = param->data;
struct line_range *lr = lf->lr; struct line_range *lr = lf->lr;
pr_debug("find (%llx) %s\n",
(unsigned long long)dwarf_dieoffset(sp_die),
dwarf_diename(sp_die));
if (dwarf_tag(sp_die) == DW_TAG_subprogram && if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
die_compare_name(sp_die, lr->function)) { die_compare_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die); lf->fname = dwarf_decl_file(sp_die);
......
...@@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) ...@@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym)
return 0; return 0;
} }
struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, int perf_session__resolve_callchain(struct perf_session *self,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent) struct symbol **parent)
{ {
u8 cpumode = PERF_RECORD_MISC_USER; u8 cpumode = PERF_RECORD_MISC_USER;
unsigned int i; unsigned int i;
struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); int err;
if (!syms) callchain_cursor_reset(&self->callchain_cursor);
return NULL;
for (i = 0; i < chain->nr; i++) { for (i = 0; i < chain->nr; i++) {
u64 ip = chain->ips[i]; u64 ip = chain->ips[i];
...@@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, ...@@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
*parent = al.sym; *parent = al.sym;
if (!symbol_conf.use_callchain) if (!symbol_conf.use_callchain)
break; break;
syms[i].map = al.map;
syms[i].sym = al.sym;
} }
err = callchain_cursor_append(&self->callchain_cursor,
ip, al.map, al.sym);
if (err)
return err;
} }
return syms; return 0;
} }
static int process_event_synth_stub(event_t *event __used, static int process_event_synth_stub(event_t *event __used,
...@@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s, ...@@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s,
if (iter->timestamp > limit) if (iter->timestamp > limit)
break; break;
event__parse_sample(iter->event, s, &sample); perf_session__parse_sample(s, iter->event, &sample);
perf_session_deliver_event(s, iter->event, &sample, ops, perf_session_deliver_event(s, iter->event, &sample, ops,
iter->file_offset); iter->file_offset);
...@@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session, ...@@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session,
/* /*
* For all kernel events we get the sample data * For all kernel events we get the sample data
*/ */
event__parse_sample(event, session, &sample); perf_session__parse_sample(session, event, &sample);
/* Preprocess sample records - precheck callchains */ /* Preprocess sample records - precheck callchains */
if (perf_session__preprocess_sample(session, event, &sample)) if (perf_session__preprocess_sample(session, event, &sample))
......
...@@ -51,7 +51,8 @@ struct perf_session { ...@@ -51,7 +51,8 @@ struct perf_session {
int cwdlen; int cwdlen;
char *cwd; char *cwd;
struct ordered_samples ordered_samples; struct ordered_samples ordered_samples;
char filename[0]; struct callchain_cursor callchain_cursor;
char filename[0];
}; };
struct perf_event_ops; struct perf_event_ops;
...@@ -94,10 +95,10 @@ int __perf_session__process_events(struct perf_session *self, ...@@ -94,10 +95,10 @@ int __perf_session__process_events(struct perf_session *self,
int perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self,
struct perf_event_ops *event_ops); struct perf_event_ops *event_ops);
struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, int perf_session__resolve_callchain(struct perf_session *self,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent); struct symbol **parent);
bool perf_session__has_traces(struct perf_session *self, const char *msg); bool perf_session__has_traces(struct perf_session *self, const char *msg);
...@@ -154,4 +155,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) ...@@ -154,4 +155,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
{ {
return hists__fprintf_nr_events(&self->hists, fp); return hists__fprintf_nr_events(&self->hists, fp);
} }
static inline int perf_session__parse_sample(struct perf_session *session,
const event_t *event,
struct sample_data *sample)
{
return event__parse_sample(event, session->sample_type,
session->sample_id_all, sample);
}
#endif /* __PERF_SESSION_H */ #endif /* __PERF_SESSION_H */
...@@ -7,61 +7,6 @@ ...@@ -7,61 +7,6 @@
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
{
if (dir->d_name[0] == '.')
return 0;
else
return 1;
}
struct thread_map *thread_map__new_by_pid(pid_t pid)
{
struct thread_map *threads;
char name[256];
int items;
struct dirent **namelist = NULL;
int i;
sprintf(name, "/proc/%d/task", pid);
items = scandir(name, &namelist, filter, NULL);
if (items <= 0)
return NULL;
threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
if (threads != NULL) {
for (i = 0; i < items; i++)
threads->map[i] = atoi(namelist[i]->d_name);
threads->nr = items;
}
for (i=0; i<items; i++)
free(namelist[i]);
free(namelist);
return threads;
}
struct thread_map *thread_map__new_by_tid(pid_t tid)
{
struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
if (threads != NULL) {
threads->map[0] = tid;
threads->nr = 1;
}
return threads;
}
struct thread_map *thread_map__new(pid_t pid, pid_t tid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);
return thread_map__new_by_tid(tid);
}
static struct thread *thread__new(pid_t pid) static struct thread *thread__new(pid_t pid)
{ {
struct thread *self = zalloc(sizeof(*self)); struct thread *self = zalloc(sizeof(*self));
......
...@@ -18,24 +18,10 @@ struct thread { ...@@ -18,24 +18,10 @@ struct thread {
int comm_len; int comm_len;
}; };
struct thread_map {
int nr;
int map[];
};
struct perf_session; struct perf_session;
void thread__delete(struct thread *self); void thread__delete(struct thread *self);
struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid);
static inline void thread_map__delete(struct thread_map *threads)
{
free(threads);
}
int thread__set_comm(struct thread *self, const char *comm); int thread__set_comm(struct thread *self, const char *comm);
int thread__comm_len(struct thread *self); int thread__comm_len(struct thread *self);
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
......
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include "thread_map.h"
/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
{
if (dir->d_name[0] == '.')
return 0;
else
return 1;
}
struct thread_map *thread_map__new_by_pid(pid_t pid)
{
struct thread_map *threads;
char name[256];
int items;
struct dirent **namelist = NULL;
int i;
sprintf(name, "/proc/%d/task", pid);
items = scandir(name, &namelist, filter, NULL);
if (items <= 0)
return NULL;
threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
if (threads != NULL) {
for (i = 0; i < items; i++)
threads->map[i] = atoi(namelist[i]->d_name);
threads->nr = items;
}
for (i=0; i<items; i++)
free(namelist[i]);
free(namelist);
return threads;
}
struct thread_map *thread_map__new_by_tid(pid_t tid)
{
struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
if (threads != NULL) {
threads->map[0] = tid;
threads->nr = 1;
}
return threads;
}
struct thread_map *thread_map__new(pid_t pid, pid_t tid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);
return thread_map__new_by_tid(tid);
}
void thread_map__delete(struct thread_map *threads)
{
free(threads);
}
#ifndef __PERF_THREAD_MAP_H
#define __PERF_THREAD_MAP_H
#include <sys/types.h>
struct thread_map {
int nr;
int map[];
};
struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid);
void thread_map__delete(struct thread_map *threads);
#endif /* __PERF_THREAD_MAP_H */
...@@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, ...@@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
while (node) { while (node) {
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
struct rb_node *next = rb_next(node); struct rb_node *next = rb_next(node);
u64 cumul = cumul_hits(child); u64 cumul = callchain_cumul_hits(child);
struct callchain_list *chain; struct callchain_list *chain;
char folded_sign = ' '; char folded_sign = ' ';
int first = true; int first = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment