Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
This commit is contained in:
commit
d380eaaea7
@ -73,6 +73,10 @@ OPTIONS
|
||||
(Only for --vars) Show external defined variables in addition to local
|
||||
variables.
|
||||
|
||||
-F::
|
||||
--funcs::
|
||||
Show available functions in given module or kernel.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Forcibly add events with existing name.
|
||||
|
@ -402,6 +402,7 @@ LIB_H += util/debug.h
|
||||
LIB_H += util/debugfs.h
|
||||
LIB_H += util/event.h
|
||||
LIB_H += util/evsel.h
|
||||
LIB_H += util/evlist.h
|
||||
LIB_H += util/exec_cmd.h
|
||||
LIB_H += util/types.h
|
||||
LIB_H += util/levenshtein.h
|
||||
@ -425,6 +426,7 @@ LIB_H += util/values.h
|
||||
LIB_H += util/sort.h
|
||||
LIB_H += util/hist.h
|
||||
LIB_H += util/thread.h
|
||||
LIB_H += util/thread_map.h
|
||||
LIB_H += util/trace-event.h
|
||||
LIB_H += util/probe-finder.h
|
||||
LIB_H += util/probe-event.h
|
||||
@ -440,6 +442,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
LIB_OBJS += $(OUTPUT)util/event.o
|
||||
LIB_OBJS += $(OUTPUT)util/evlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/evsel.o
|
||||
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
|
||||
LIB_OBJS += $(OUTPUT)util/help.o
|
||||
@ -469,6 +472,7 @@ LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread_map.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
|
||||
|
@ -52,6 +52,7 @@ static struct {
|
||||
bool show_lines;
|
||||
bool show_vars;
|
||||
bool show_ext_vars;
|
||||
bool show_funcs;
|
||||
bool mod_events;
|
||||
int nevents;
|
||||
struct perf_probe_event events[MAX_PROBES];
|
||||
@ -221,6 +222,8 @@ static const struct option options[] = {
|
||||
OPT__DRY_RUN(&probe_event_dry_run),
|
||||
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
|
||||
"Set how many probe points can be found for a probe."),
|
||||
OPT_BOOLEAN('F', "funcs", ¶ms.show_funcs,
|
||||
"Show potential probe-able functions."),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||
params.max_probe_points = MAX_PROBES;
|
||||
|
||||
if ((!params.nevents && !params.dellist && !params.list_events &&
|
||||
!params.show_lines))
|
||||
!params.show_lines && !params.show_funcs))
|
||||
usage_with_options(probe_usage, options);
|
||||
|
||||
/*
|
||||
@ -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");
|
||||
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();
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show event list. (%d)\n",
|
||||
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
|
||||
if (params.show_lines) {
|
||||
|
@ -18,17 +18,20 @@
|
||||
|
||||
#include "util/header.h"
|
||||
#include "util/event.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/cpumap.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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 {
|
||||
WRITE_FORCE,
|
||||
@ -46,7 +49,7 @@ static unsigned int user_freq = UINT_MAX;
|
||||
static int freq = 1000;
|
||||
static int output;
|
||||
static int pipe_output = 0;
|
||||
static const char *output_name = "perf.data";
|
||||
static const char *output_name = NULL;
|
||||
static int group = 0;
|
||||
static int realtime_prio = 0;
|
||||
static bool nodelay = false;
|
||||
@ -66,51 +69,17 @@ static bool sample_address = false;
|
||||
static bool sample_time = false;
|
||||
static bool no_buildid = false;
|
||||
static bool no_buildid_cache = false;
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
static long samples = 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 off_t post_processing_offset;
|
||||
|
||||
static struct perf_session *session;
|
||||
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)
|
||||
{
|
||||
bytes_written += size;
|
||||
@ -139,9 +108,9 @@ static int process_synthesized_event(event_t *event,
|
||||
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 char *data = md->base + page_size;
|
||||
unsigned long size;
|
||||
@ -185,7 +154,7 @@ static void mmap_read(struct mmap_data *md)
|
||||
write_output(buf, size);
|
||||
|
||||
md->prev = old;
|
||||
mmap_write_tail(md, old);
|
||||
perf_mmap__write_tail(md, old);
|
||||
}
|
||||
|
||||
static volatile int done = 0;
|
||||
@ -209,8 +178,6 @@ static void sig_atexit(void)
|
||||
kill(getpid(), signr);
|
||||
}
|
||||
|
||||
static int group_fd;
|
||||
|
||||
static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
|
||||
{
|
||||
struct perf_header_attr *h_attr;
|
||||
@ -234,28 +201,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
|
||||
char *filter = evsel->filter;
|
||||
struct perf_event_attr *attr = &evsel->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 ret;
|
||||
struct {
|
||||
u64 count;
|
||||
u64 time_enabled;
|
||||
u64 time_running;
|
||||
u64 id;
|
||||
} read_data;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
sid = SID(evsel, cpu, thread_index);
|
||||
if (perf_header_attr__add_id(h_attr, sid->id) < 0) {
|
||||
pr_warning("Not enough memory to add id\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
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 |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING |
|
||||
@ -263,7 +249,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
|
||||
|
||||
attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
|
||||
if (nr_counters > 1)
|
||||
if (evlist->nr_entries > 1)
|
||||
attr->sample_type |= PERF_SAMPLE_ID;
|
||||
|
||||
/*
|
||||
@ -315,19 +301,39 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
|
||||
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
attr->inherit = !no_inherit;
|
||||
|
||||
if (target_pid == -1 && target_tid == -1 && !system_wide) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void open_counters(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
int cpu;
|
||||
|
||||
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;
|
||||
|
||||
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
|
||||
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
|
||||
try_again:
|
||||
FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0);
|
||||
|
||||
if (FD(evsel, nr_cpu, thread_index) < 0) {
|
||||
if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) {
|
||||
int err = errno;
|
||||
|
||||
if (err == EPERM || err == EACCES)
|
||||
@ -364,7 +370,7 @@ try_again:
|
||||
}
|
||||
printf("\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 (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
|
||||
@ -375,90 +381,16 @@ try_again:
|
||||
#endif
|
||||
|
||||
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)
|
||||
sample_type = attr->sample_type;
|
||||
}
|
||||
if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, false) < 0)
|
||||
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)
|
||||
@ -481,9 +413,9 @@ static void atexit_header(void)
|
||||
|
||||
if (!no_buildid)
|
||||
process_buildids();
|
||||
perf_header__write(&session->header, output, true);
|
||||
perf_header__write(&session->header, evsel_list, output, true);
|
||||
perf_session__delete(session);
|
||||
perf_evsel_list__delete();
|
||||
perf_evlist__delete(evsel_list);
|
||||
symbol__exit();
|
||||
}
|
||||
}
|
||||
@ -533,9 +465,9 @@ static void mmap_read_all(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_cpu; i++) {
|
||||
if (mmap_array[i].base)
|
||||
mmap_read(&mmap_array[i]);
|
||||
for (i = 0; i < cpus->nr; i++) {
|
||||
if (evsel_list->mmap[i].base)
|
||||
mmap_read(&evsel_list->mmap[i]);
|
||||
}
|
||||
|
||||
if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
|
||||
@ -566,18 +498,26 @@ static int __cmd_record(int argc, const char **argv)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!strcmp(output_name, "-"))
|
||||
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);
|
||||
if (!output_name) {
|
||||
if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||
pipe_output = 1;
|
||||
else
|
||||
output_name = "perf.data";
|
||||
}
|
||||
if (output_name) {
|
||||
if (!strcmp(output_name, "-"))
|
||||
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;
|
||||
@ -611,7 +551,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
goto out_delete_session;
|
||||
}
|
||||
|
||||
if (have_tracepoints(&evsel_list))
|
||||
if (have_tracepoints(&evsel_list->entries))
|
||||
perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
|
||||
|
||||
/*
|
||||
@ -673,12 +613,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
close(child_ready_pipe[0]);
|
||||
}
|
||||
|
||||
if (!system_wide && no_inherit && !cpu_list) {
|
||||
open_counters(-1);
|
||||
} else {
|
||||
for (i = 0; i < cpus->nr; i++)
|
||||
open_counters(cpus->map[i]);
|
||||
}
|
||||
open_counters(evsel_list);
|
||||
|
||||
perf_session__set_sample_type(session, sample_type);
|
||||
|
||||
@ -687,7 +622,8 @@ static int __cmd_record(int argc, const char **argv)
|
||||
if (err < 0)
|
||||
return err;
|
||||
} 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)
|
||||
return err;
|
||||
}
|
||||
@ -712,7 +648,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (have_tracepoints(&evsel_list)) {
|
||||
if (have_tracepoints(&evsel_list->entries)) {
|
||||
/*
|
||||
* FIXME err <= 0 here actually means that
|
||||
* there were no tracepoints so its not really
|
||||
@ -721,7 +657,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
* return this more properly and also
|
||||
* 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,
|
||||
session);
|
||||
if (err <= 0) {
|
||||
@ -789,15 +725,15 @@ static int __cmd_record(int argc, const char **argv)
|
||||
if (hits == samples) {
|
||||
if (done)
|
||||
break;
|
||||
err = poll(event_array, nr_poll, -1);
|
||||
err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1);
|
||||
waking++;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
for (i = 0; i < nr_cpu; i++) {
|
||||
for (i = 0; i < cpus->nr; i++) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
list_for_each_entry(pos, &evsel_list, node) {
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
for (thread = 0;
|
||||
thread < threads->nr;
|
||||
thread++)
|
||||
@ -838,10 +774,10 @@ static const char * const record_usage[] = {
|
||||
static bool force, append_file;
|
||||
|
||||
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",
|
||||
parse_events),
|
||||
OPT_CALLBACK(0, "filter", NULL, "filter",
|
||||
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
|
||||
"event filter", parse_filter),
|
||||
OPT_INTEGER('p', "pid", &target_pid,
|
||||
"record events on existing process id"),
|
||||
@ -892,6 +828,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
int err = -ENOMEM;
|
||||
struct perf_evsel *pos;
|
||||
|
||||
evsel_list = perf_evlist__new();
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
argc = parse_options(argc, argv, record_options, record_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
if (!argc && target_pid == -1 && target_tid == -1 &&
|
||||
@ -913,7 +853,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
if (no_buildid_cache || no_buildid)
|
||||
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");
|
||||
goto out_symbol_exit;
|
||||
}
|
||||
@ -927,21 +868,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
usage_with_options(record_usage, record_options);
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(cpu_list);
|
||||
if (cpus == NULL) {
|
||||
perror("failed to parse CPUs map");
|
||||
return -1;
|
||||
}
|
||||
if (target_tid != -1)
|
||||
cpus = cpu_map__dummy_new();
|
||||
else
|
||||
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)
|
||||
goto out_free_fd;
|
||||
if (perf_header__push_event(pos->attr.config, event_name(pos)))
|
||||
goto out_free_fd;
|
||||
}
|
||||
event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS *
|
||||
MAX_COUNTERS * threads->nr));
|
||||
if (!event_array)
|
||||
|
||||
if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0)
|
||||
goto out_free_fd;
|
||||
|
||||
if (user_interval != ULLONG_MAX)
|
||||
@ -959,13 +901,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
} else {
|
||||
fprintf(stderr, "frequency and count are zero, aborting\n");
|
||||
err = -EINVAL;
|
||||
goto out_free_event_array;
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
err = __cmd_record(argc, argv);
|
||||
|
||||
out_free_event_array:
|
||||
free(event_array);
|
||||
out_free_fd:
|
||||
thread_map__delete(threads);
|
||||
threads = NULL;
|
||||
|
@ -81,18 +81,17 @@ static int perf_session__add_hist_entry(struct perf_session *self,
|
||||
struct addr_location *al,
|
||||
struct sample_data *data)
|
||||
{
|
||||
struct map_symbol *syms = NULL;
|
||||
struct symbol *parent = NULL;
|
||||
int err = -ENOMEM;
|
||||
int err = 0;
|
||||
struct hist_entry *he;
|
||||
struct hists *hists;
|
||||
struct perf_event_attr *attr;
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
|
||||
syms = perf_session__resolve_callchain(self, al->thread,
|
||||
data->callchain, &parent);
|
||||
if (syms == NULL)
|
||||
return -ENOMEM;
|
||||
err = perf_session__resolve_callchain(self, al->thread,
|
||||
data->callchain, &parent);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
attr = perf_header__find_attr(data->id, &self->header);
|
||||
@ -101,16 +100,17 @@ static int perf_session__add_hist_entry(struct perf_session *self,
|
||||
else
|
||||
hists = perf_session__hists_findnew(self, data->id, 0, 0);
|
||||
if (hists == NULL)
|
||||
goto out_free_syms;
|
||||
return -ENOMEM;
|
||||
|
||||
he = __hists__add_entry(hists, al, parent, data->period);
|
||||
if (he == NULL)
|
||||
goto out_free_syms;
|
||||
err = 0;
|
||||
return -ENOMEM;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
err = callchain_append(he->callchain, data->callchain, syms,
|
||||
err = callchain_append(he->callchain, &self->callchain_cursor,
|
||||
data->period);
|
||||
if (err)
|
||||
goto out_free_syms;
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* 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,
|
||||
*/
|
||||
if (use_browser > 0)
|
||||
err = hist_entry__inc_addr_samples(he, al->addr);
|
||||
out_free_syms:
|
||||
free(syms);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -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 &&
|
||||
!symbol_conf.use_callchain) {
|
||||
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"
|
||||
" params\n");
|
||||
return -EINVAL;
|
||||
@ -424,7 +423,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
|
||||
if (tok2)
|
||||
callchain_param.print_limit = strtod(tok2, &endptr);
|
||||
setup:
|
||||
if (register_callchain_param(&callchain_param) < 0) {
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
fprintf(stderr, "Can't register callchain params\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -43,11 +43,13 @@
|
||||
#include "util/parse-options.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/event.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/header.h"
|
||||
#include "util/cpumap.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <math.h>
|
||||
@ -71,6 +73,8 @@ static struct perf_event_attr default_attrs[] = {
|
||||
|
||||
};
|
||||
|
||||
struct perf_evlist *evsel_list;
|
||||
|
||||
static bool system_wide = false;
|
||||
static struct cpu_map *cpus;
|
||||
static int run_idx = 0;
|
||||
@ -166,7 +170,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING;
|
||||
|
||||
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;
|
||||
if (target_pid == -1 && target_tid == -1) {
|
||||
@ -174,7 +178,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
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)
|
||||
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 (errno == -EPERM || errno == -EACCES) {
|
||||
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)
|
||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||
|
||||
if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list, node) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
read_counter(counter);
|
||||
perf_evsel__close_fd(counter, cpus->nr, 1);
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list, node) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
read_counter_aggr(counter);
|
||||
perf_evsel__close_fd(counter, cpus->nr, threads->nr);
|
||||
}
|
||||
@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list, node)
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter);
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list, node)
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter_aggr(counter);
|
||||
}
|
||||
|
||||
@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used,
|
||||
}
|
||||
|
||||
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",
|
||||
parse_events),
|
||||
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
||||
@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
evsel_list = perf_evlist__new();
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
argc = parse_options(argc, argv, options, stat_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
@ -679,17 +687,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
||||
usage_with_options(stat_usage, options);
|
||||
|
||||
/* 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;
|
||||
|
||||
nr_counters = ARRAY_SIZE(default_attrs);
|
||||
|
||||
for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) {
|
||||
pos = perf_evsel__new(&default_attrs[c],
|
||||
nr_counters);
|
||||
pos = perf_evsel__new(&default_attrs[c], c);
|
||||
if (pos == NULL)
|
||||
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)
|
||||
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 ||
|
||||
perf_evsel__alloc_counts(pos, cpus->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)
|
||||
if (status != -1)
|
||||
print_stat(argc, argv);
|
||||
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_list__delete();
|
||||
perf_evlist__delete(evsel_list);
|
||||
out:
|
||||
thread_map__delete(threads);
|
||||
threads = NULL;
|
||||
|
@ -7,10 +7,11 @@
|
||||
|
||||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
static long page_size;
|
||||
|
||||
@ -238,14 +239,14 @@ out:
|
||||
#include "util/evsel.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
static int trace_event__id(const char *event_name)
|
||||
static int trace_event__id(const char *evname)
|
||||
{
|
||||
char *filename;
|
||||
int err = -1, fd;
|
||||
|
||||
if (asprintf(&filename,
|
||||
"/sys/kernel/debug/tracing/events/syscalls/%s/id",
|
||||
event_name) < 0)
|
||||
evname) < 0)
|
||||
return -1;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
@ -289,7 +290,7 @@ static int test__open_syscall_event(void)
|
||||
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, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void)
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
if (cpus == NULL) {
|
||||
pr_debug("cpu_map__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
|
||||
@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void)
|
||||
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, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void)
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; ++cpu) {
|
||||
unsigned int expected;
|
||||
|
||||
@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void)
|
||||
|
||||
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
|
||||
pr_debug("perf_evsel__open_read_on_cpu\n");
|
||||
goto out_close_fd;
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
expected = nr_open_calls + cpu;
|
||||
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",
|
||||
expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
|
||||
goto out_close_fd;
|
||||
err = -1;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
@ -437,6 +440,159 @@ out_thread_map_delete:
|
||||
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 {
|
||||
const char *desc;
|
||||
int (*func)(void);
|
||||
@ -453,6 +609,10 @@ static struct test {
|
||||
.desc = "detect 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,
|
||||
},
|
||||
|
@ -21,10 +21,12 @@
|
||||
#include "perf.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "util/util.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/parse-options.h"
|
||||
@ -60,6 +62,8 @@
|
||||
|
||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||
|
||||
struct perf_evlist *evsel_list;
|
||||
|
||||
static bool system_wide = false;
|
||||
|
||||
static int default_interval = 0;
|
||||
@ -75,7 +79,7 @@ static struct cpu_map *cpus;
|
||||
static int realtime_prio = 0;
|
||||
static bool group = false;
|
||||
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 delay_secs = 2;
|
||||
@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme)
|
||||
|
||||
line = syme->src->lines;
|
||||
while (line) {
|
||||
for (i = 0; i < nr_counters; i++)
|
||||
for (i = 0; i < evsel_list->nr_entries; i++)
|
||||
line->count[i] = 0;
|
||||
line = line->next;
|
||||
}
|
||||
@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym)
|
||||
if (!display_weighted)
|
||||
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] + 1);
|
||||
@ -501,7 +505,7 @@ static void print_sym_table(void)
|
||||
rb_insert_active_sym(&tmp, syme);
|
||||
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;
|
||||
} else
|
||||
list_remove_active_sym(syme);
|
||||
@ -535,9 +539,9 @@ static void print_sym_table(void)
|
||||
esamples_percent);
|
||||
}
|
||||
|
||||
if (nr_counters == 1 || !display_weighted) {
|
||||
if (evsel_list->nr_entries == 1 || !display_weighted) {
|
||||
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);
|
||||
if (freq)
|
||||
printf("Hz ");
|
||||
@ -547,7 +551,7 @@ static void print_sym_table(void)
|
||||
|
||||
if (!display_weighted)
|
||||
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)
|
||||
printf("/");
|
||||
|
||||
@ -606,7 +610,7 @@ static void print_sym_table(void)
|
||||
sym_width = winsize.ws_col - dso_width - 29;
|
||||
}
|
||||
putchar('\n');
|
||||
if (nr_counters == 1)
|
||||
if (evsel_list->nr_entries == 1)
|
||||
printf(" samples pcnt");
|
||||
else
|
||||
printf(" weight samples pcnt");
|
||||
@ -615,7 +619,7 @@ static void print_sym_table(void)
|
||||
printf(" RIP ");
|
||||
printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
|
||||
printf(" %s _______ _____",
|
||||
nr_counters == 1 ? " " : "______");
|
||||
evsel_list->nr_entries == 1 ? " " : "______");
|
||||
if (verbose)
|
||||
printf(" ________________");
|
||||
printf(" %-*.*s", sym_width, sym_width, graph_line);
|
||||
@ -634,7 +638,7 @@ static void print_sym_table(void)
|
||||
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
|
||||
sum_ksamples));
|
||||
|
||||
if (nr_counters == 1 || !display_weighted)
|
||||
if (evsel_list->nr_entries == 1 || !display_weighted)
|
||||
printf("%20.2f ", syme->weight);
|
||||
else
|
||||
printf("%9.1f %10ld ", syme->weight, syme->snap_count);
|
||||
@ -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[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[f] profile display filter (count). \t(%d)\n", count_filter);
|
||||
@ -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] 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,
|
||||
@ -783,7 +787,7 @@ static int key_mapped(int c)
|
||||
return 1;
|
||||
case 'E':
|
||||
case 'w':
|
||||
return nr_counters > 1 ? 1 : 0;
|
||||
return evsel_list->nr_entries > 1 ? 1 : 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c)
|
||||
signal(SIGWINCH, SIG_DFL);
|
||||
break;
|
||||
case 'E':
|
||||
if (nr_counters > 1) {
|
||||
if (evsel_list->nr_entries > 1) {
|
||||
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));
|
||||
|
||||
prompt_integer(&sym_counter, "Enter details event counter");
|
||||
|
||||
if (sym_counter >= nr_counters) {
|
||||
sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
|
||||
if (sym_counter >= evsel_list->nr_entries) {
|
||||
sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node);
|
||||
sym_counter = 0;
|
||||
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel));
|
||||
sleep(1);
|
||||
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)
|
||||
break;
|
||||
} else sym_counter = 0;
|
||||
@ -930,6 +934,7 @@ repeat:
|
||||
/* Tag samples to be skipped. */
|
||||
static const char *skip_symbols[] = {
|
||||
"default_idle",
|
||||
"native_safe_halt",
|
||||
"cpu_idle",
|
||||
"enter_idle",
|
||||
"exit_idle",
|
||||
@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
|
||||
|
||||
static void event__process_sample(const event_t *self,
|
||||
struct sample_data *sample,
|
||||
struct perf_session *session,
|
||||
struct perf_evsel *evsel)
|
||||
struct perf_session *session)
|
||||
{
|
||||
u64 ip = self->ip.ip;
|
||||
struct sym_entry *syme;
|
||||
@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self,
|
||||
|
||||
syme = symbol__priv(al.sym);
|
||||
if (!syme->skip) {
|
||||
syme->count[evsel->idx]++;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
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);
|
||||
pthread_mutex_lock(&active_symbols_lock);
|
||||
if (list_empty(&syme->node) || !syme->node.next)
|
||||
@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self,
|
||||
}
|
||||
}
|
||||
|
||||
struct mmap_data {
|
||||
void *base;
|
||||
int mask;
|
||||
unsigned int prev;
|
||||
};
|
||||
|
||||
static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel,
|
||||
int ncpus, int nthreads)
|
||||
static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu)
|
||||
{
|
||||
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;
|
||||
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");
|
||||
while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) {
|
||||
perf_session__parse_sample(self, event, &sample);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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)
|
||||
event__process_sample(event, &sample, self, evsel);
|
||||
event__process_sample(event, &sample, self);
|
||||
else
|
||||
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)
|
||||
{
|
||||
struct perf_evsel *counter;
|
||||
int i, thread_index;
|
||||
int i;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < cpus->nr; i++)
|
||||
perf_session__mmap_read_cpu(self, i);
|
||||
}
|
||||
|
||||
int nr_poll;
|
||||
int group_fd;
|
||||
|
||||
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 mmap_data *mm;
|
||||
struct perf_event_attr *attr;
|
||||
int cpu = -1;
|
||||
int thread_index;
|
||||
struct perf_evsel *counter;
|
||||
|
||||
if (target_tid == -1)
|
||||
cpu = cpus->map[i];
|
||||
list_for_each_entry(counter, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &counter->attr;
|
||||
|
||||
attr = &evsel->attr;
|
||||
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
|
||||
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
if (freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = freq;
|
||||
}
|
||||
|
||||
if (freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = freq;
|
||||
}
|
||||
if (evlist->nr_entries > 1) {
|
||||
attr->sample_type |= PERF_SAMPLE_ID;
|
||||
attr->read_format |= PERF_FORMAT_ID;
|
||||
}
|
||||
|
||||
attr->inherit = (cpu < 0) && inherit;
|
||||
attr->mmap = 1;
|
||||
|
||||
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
|
||||
attr->mmap = 1;
|
||||
try_again:
|
||||
FD(evsel, i, thread_index) = sys_perf_event_open(attr,
|
||||
threads->map[thread_index], cpu, group_fd, 0);
|
||||
|
||||
if (FD(evsel, i, thread_index) < 0) {
|
||||
if (perf_evsel__open(counter, cpus, threads, group, inherit) < 0) {
|
||||
int err = errno;
|
||||
|
||||
if (err == EPERM || err == EACCES)
|
||||
@ -1253,8 +1157,8 @@ try_again:
|
||||
* based cpu-clock-tick sw counter, which
|
||||
* is always available even if no PMU support:
|
||||
*/
|
||||
if (attr->type == PERF_TYPE_HARDWARE
|
||||
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) {
|
||||
if (attr->type == PERF_TYPE_HARDWARE &&
|
||||
attr->config == PERF_COUNT_HW_CPU_CYCLES) {
|
||||
|
||||
if (verbose)
|
||||
warning(" ... trying to fall back to cpu-clock-ticks\n");
|
||||
@ -1264,39 +1168,23 @@ try_again:
|
||||
goto try_again;
|
||||
}
|
||||
printf("\n");
|
||||
error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
|
||||
FD(evsel, i, thread_index), strerror(err));
|
||||
error("sys_perf_event_open() syscall returned with %d "
|
||||
"(%s). /bin/dmesg may provide additional information.\n",
|
||||
err, strerror(err));
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
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)
|
||||
{
|
||||
pthread_t thread;
|
||||
struct perf_evsel *counter;
|
||||
int i, ret;
|
||||
struct perf_evsel *first;
|
||||
int ret;
|
||||
/*
|
||||
* 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.
|
||||
@ -1310,14 +1198,12 @@ static int __cmd_top(void)
|
||||
else
|
||||
event__synthesize_threads(event__process, session);
|
||||
|
||||
for (i = 0; i < cpus->nr; i++) {
|
||||
group_fd = -1;
|
||||
list_for_each_entry(counter, &evsel_list, node)
|
||||
start_counter(i, counter);
|
||||
}
|
||||
start_counters(evsel_list);
|
||||
first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
|
||||
perf_session__set_sample_type(session, first->attr.sample_type);
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -1342,7 +1228,7 @@ static int __cmd_top(void)
|
||||
perf_session__mmap_read(session);
|
||||
|
||||
if (hits == samples)
|
||||
ret = poll(event_array, nr_poll, 100);
|
||||
ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1354,7 +1240,7 @@ static const char * const top_usage[] = {
|
||||
};
|
||||
|
||||
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",
|
||||
parse_events),
|
||||
OPT_INTEGER('c', "count", &default_interval,
|
||||
@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
||||
struct perf_evsel *pos;
|
||||
int status = -ENOMEM;
|
||||
|
||||
evsel_list = perf_evlist__new();
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
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)
|
||||
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 */
|
||||
if (target_tid > 0 && cpu_list) {
|
||||
printf("WARNING: PID switch overriding CPU\n");
|
||||
@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
||||
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");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
||||
if (cpus == NULL)
|
||||
usage_with_options(top_usage, options);
|
||||
|
||||
list_for_each_entry(pos, &evsel_list, node) {
|
||||
if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 ||
|
||||
perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
|
||||
goto out_free_fd;
|
||||
/*
|
||||
* 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)
|
||||
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) +
|
||||
(nr_counters + 1) * sizeof(unsigned long));
|
||||
(evsel_list->nr_entries + 1) * sizeof(unsigned long));
|
||||
|
||||
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
|
||||
if (symbol__init() < 0)
|
||||
@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
status = __cmd_top();
|
||||
out_free_fd:
|
||||
list_for_each_entry(pos, &evsel_list, node)
|
||||
perf_evsel__free_mmap(pos);
|
||||
perf_evsel_list__delete();
|
||||
perf_evlist__delete(evsel_list);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws);
|
||||
#include "util/types.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
|
||||
* counters in the current task.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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
|
||||
* sort them in an rbtree.
|
||||
@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
|
||||
}
|
||||
|
||||
#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) \
|
||||
list_for_each_entry_safe(child, next, &parent->children, brothers)
|
||||
list_for_each_entry_safe(child, next, &parent->children, siblings)
|
||||
|
||||
static void
|
||||
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 *parent = NULL;
|
||||
struct callchain_node *rnode;
|
||||
u64 chain_cumul = cumul_hits(chain);
|
||||
u64 chain_cumul = callchain_cumul_hits(chain);
|
||||
|
||||
while (*p) {
|
||||
u64 rnode_cumul;
|
||||
|
||||
parent = *p;
|
||||
rnode = rb_entry(parent, struct callchain_node, rb_node);
|
||||
rnode_cumul = cumul_hits(rnode);
|
||||
rnode_cumul = callchain_cumul_hits(rnode);
|
||||
|
||||
switch (mode) {
|
||||
case CHAIN_FLAT:
|
||||
@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
|
||||
|
||||
chain_for_each_child(child, node) {
|
||||
__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,
|
||||
CHAIN_GRAPH_ABS);
|
||||
}
|
||||
@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
|
||||
|
||||
chain_for_each_child(child, node) {
|
||||
__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,
|
||||
CHAIN_GRAPH_REL);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
int register_callchain_param(struct callchain_param *param)
|
||||
int callchain_register_param(struct callchain_param *param)
|
||||
{
|
||||
switch (param->mode) {
|
||||
case CHAIN_GRAPH_ABS:
|
||||
@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children)
|
||||
chain_for_each_child(next, new)
|
||||
next->parent = new;
|
||||
}
|
||||
list_add_tail(&new->brothers, &parent->children);
|
||||
list_add_tail(&new->siblings, &parent->children);
|
||||
|
||||
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
|
||||
*/
|
||||
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;
|
||||
|
||||
for (i = start; i < chain->nr; i++) {
|
||||
node->val_nr = cursor->nr - cursor->pos;
|
||||
if (!node->val_nr)
|
||||
pr_warning("Warning: empty node in callchain tree\n");
|
||||
|
||||
cursor_node = callchain_cursor_current(cursor);
|
||||
|
||||
while (cursor_node) {
|
||||
struct callchain_list *call;
|
||||
|
||||
call = zalloc(sizeof(*call));
|
||||
@ -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");
|
||||
return;
|
||||
}
|
||||
call->ip = chain->ips[i].ip;
|
||||
call->ms = chain->ips[i].ms;
|
||||
call->ip = cursor_node->ip;
|
||||
call->ms.sym = cursor_node->sym;
|
||||
call->ms.map = cursor_node->map;
|
||||
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
|
||||
add_child(struct callchain_node *parent, struct resolved_chain *chain,
|
||||
int start, u64 period)
|
||||
add_child(struct callchain_node *parent,
|
||||
struct callchain_cursor *cursor,
|
||||
u64 period)
|
||||
{
|
||||
struct callchain_node *new;
|
||||
|
||||
new = create_child(parent, false);
|
||||
fill_node(new, chain, start);
|
||||
fill_node(new, cursor);
|
||||
|
||||
new->children_hit = 0;
|
||||
new->hit = period;
|
||||
@ -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
|
||||
*/
|
||||
static void
|
||||
split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
|
||||
struct callchain_list *to_split, int idx_parents, int idx_local,
|
||||
u64 period)
|
||||
split_add_child(struct callchain_node *parent,
|
||||
struct callchain_cursor *cursor,
|
||||
struct callchain_list *to_split,
|
||||
u64 idx_parents, u64 idx_local, u64 period)
|
||||
{
|
||||
struct callchain_node *new;
|
||||
struct list_head *old_tail;
|
||||
@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
|
||||
/* split the hits */
|
||||
new->hit = parent->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;
|
||||
parent->val_nr = idx_local;
|
||||
|
||||
/* create a new child for the new branch if any */
|
||||
if (idx_total < chain->nr) {
|
||||
if (idx_total < cursor->nr) {
|
||||
parent->hit = 0;
|
||||
add_child(parent, chain, idx_total, period);
|
||||
add_child(parent, cursor, period);
|
||||
parent->children_hit += period;
|
||||
} else {
|
||||
parent->hit = period;
|
||||
@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
|
||||
}
|
||||
|
||||
static int
|
||||
append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period);
|
||||
append_chain(struct callchain_node *root,
|
||||
struct callchain_cursor *cursor,
|
||||
u64 period);
|
||||
|
||||
static void
|
||||
append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
append_chain_children(struct callchain_node *root,
|
||||
struct callchain_cursor *cursor,
|
||||
u64 period)
|
||||
{
|
||||
struct callchain_node *rnode;
|
||||
|
||||
/* lookup in childrens */
|
||||
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)
|
||||
goto inc_children_hit;
|
||||
}
|
||||
/* nothing in children, add to the current node */
|
||||
add_child(root, chain, start, period);
|
||||
add_child(root, cursor, period);
|
||||
|
||||
inc_children_hit:
|
||||
root->children_hit += period;
|
||||
}
|
||||
|
||||
static int
|
||||
append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
unsigned int start, u64 period)
|
||||
append_chain(struct callchain_node *root,
|
||||
struct callchain_cursor *cursor,
|
||||
u64 period)
|
||||
{
|
||||
struct callchain_cursor_node *curr_snap = cursor->curr;
|
||||
struct callchain_list *cnode;
|
||||
unsigned int i = start;
|
||||
u64 start = cursor->pos;
|
||||
bool found = false;
|
||||
u64 matches;
|
||||
|
||||
/*
|
||||
* Lookup in the current node
|
||||
@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain,
|
||||
* anywhere inside a function.
|
||||
*/
|
||||
list_for_each_entry(cnode, &root->val, list) {
|
||||
struct callchain_cursor_node *node;
|
||||
struct symbol *sym;
|
||||
|
||||
if (i == chain->nr)
|
||||
node = callchain_cursor_current(cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
sym = chain->ips[i].ms.sym;
|
||||
sym = node->sym;
|
||||
|
||||
if (cnode->ms.sym && sym) {
|
||||
if (cnode->ms.sym->start != sym->start)
|
||||
break;
|
||||
} else if (cnode->ip != chain->ips[i].ip)
|
||||
} else if (cnode->ip != node->ip)
|
||||
break;
|
||||
|
||||
if (!found)
|
||||
found = true;
|
||||
i++;
|
||||
|
||||
callchain_cursor_advance(cursor);
|
||||
}
|
||||
|
||||
/* matches not, relay on the parent */
|
||||
if (!found)
|
||||
if (!found) {
|
||||
cursor->curr = curr_snap;
|
||||
cursor->pos = start;
|
||||
return -1;
|
||||
}
|
||||
|
||||
matches = cursor->pos - start;
|
||||
|
||||
/* we match only a part of the node. Split it and add the new chain */
|
||||
if (i - start < root->val_nr) {
|
||||
split_add_child(root, chain, cnode, start, i - start, period);
|
||||
if (matches < root->val_nr) {
|
||||
split_add_child(root, cursor, cnode, start, matches, period);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
|
||||
struct map_symbol *syms)
|
||||
int callchain_append(struct callchain_root *root,
|
||||
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 (!chain->nr)
|
||||
if (!cursor->nr)
|
||||
return 0;
|
||||
|
||||
filtered = zalloc(sizeof(*filtered) +
|
||||
chain->nr * sizeof(struct resolved_ip));
|
||||
if (!filtered)
|
||||
return -ENOMEM;
|
||||
callchain_cursor_commit(cursor);
|
||||
|
||||
filter_context(chain, filtered, syms);
|
||||
append_chain_children(&root->node, cursor, period);
|
||||
|
||||
if (!filtered->nr)
|
||||
goto end;
|
||||
|
||||
append_chain_children(&root->node, filtered, 0, period);
|
||||
|
||||
if (filtered->nr > root->max_depth)
|
||||
root->max_depth = filtered->nr;
|
||||
end:
|
||||
free(filtered);
|
||||
if (cursor->nr > root->max_depth)
|
||||
root->max_depth = cursor->nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_chain_branch(struct callchain_node *dst, struct callchain_node *src,
|
||||
struct resolved_chain *chain)
|
||||
merge_chain_branch(struct callchain_cursor *cursor,
|
||||
struct callchain_node *dst, struct callchain_node *src)
|
||||
{
|
||||
struct callchain_cursor_node **old_last = cursor->last;
|
||||
struct callchain_node *child, *next_child;
|
||||
struct callchain_list *list, *next_list;
|
||||
int old_pos = chain->nr;
|
||||
int old_pos = cursor->nr;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry_safe(list, next_list, &src->val, list) {
|
||||
chain->ips[chain->nr].ip = list->ip;
|
||||
chain->ips[chain->nr].ms = list->ms;
|
||||
chain->nr++;
|
||||
callchain_cursor_append(cursor, list->ip,
|
||||
list->ms.map, list->ms.sym);
|
||||
list_del(&list->list);
|
||||
free(list);
|
||||
}
|
||||
|
||||
if (src->hit)
|
||||
append_chain_children(dst, chain, 0, src->hit);
|
||||
if (src->hit) {
|
||||
callchain_cursor_commit(cursor);
|
||||
append_chain_children(dst, cursor, src->hit);
|
||||
}
|
||||
|
||||
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)
|
||||
break;
|
||||
|
||||
list_del(&child->brothers);
|
||||
list_del(&child->siblings);
|
||||
free(child);
|
||||
}
|
||||
|
||||
chain->nr = old_pos;
|
||||
cursor->nr = old_pos;
|
||||
cursor->last = old_last;
|
||||
|
||||
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)
|
||||
{
|
||||
struct resolved_chain *chain;
|
||||
int err;
|
||||
|
||||
chain = malloc(sizeof(*chain) +
|
||||
src->max_depth * sizeof(struct resolved_ip));
|
||||
if (!chain)
|
||||
return -ENOMEM;
|
||||
|
||||
chain->nr = 0;
|
||||
|
||||
err = merge_chain_branch(&dst->node, &src->node, chain);
|
||||
|
||||
free(chain);
|
||||
|
||||
return err;
|
||||
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 callchain_cursor_node *node = *cursor->last;
|
||||
|
||||
if (!node) {
|
||||
node = calloc(sizeof(*node), 1);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
*cursor->last = node;
|
||||
}
|
||||
|
||||
node->ip = ip;
|
||||
node->map = map;
|
||||
node->sym = sym;
|
||||
|
||||
cursor->nr++;
|
||||
|
||||
cursor->last = &node->next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ enum chain_mode {
|
||||
|
||||
struct callchain_node {
|
||||
struct callchain_node *parent;
|
||||
struct list_head brothers;
|
||||
struct list_head siblings;
|
||||
struct list_head children;
|
||||
struct list_head val;
|
||||
struct rb_node rb_node; /* to sort nodes in an rbtree */
|
||||
@ -49,9 +49,30 @@ struct callchain_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)
|
||||
{
|
||||
INIT_LIST_HEAD(&root->node.brothers);
|
||||
INIT_LIST_HEAD(&root->node.siblings);
|
||||
INIT_LIST_HEAD(&root->node.children);
|
||||
INIT_LIST_HEAD(&root->node.val);
|
||||
|
||||
@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root)
|
||||
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;
|
||||
}
|
||||
|
||||
int register_callchain_param(struct callchain_param *param);
|
||||
int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
|
||||
struct map_symbol *syms, u64 period);
|
||||
int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
|
||||
int callchain_register_param(struct callchain_param *param);
|
||||
int callchain_append(struct callchain_root *root,
|
||||
struct callchain_cursor *cursor,
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void)
|
||||
|
||||
return cpus;
|
||||
}
|
||||
|
||||
void cpu_map__delete(struct cpu_map *map)
|
||||
{
|
||||
free(map);
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ struct cpu_map {
|
||||
|
||||
struct cpu_map *cpu_map__new(const char *cpu_list);
|
||||
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 */
|
||||
|
@ -826,128 +826,3 @@ out_filtered:
|
||||
al->filtered = true;
|
||||
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;
|
||||
int event__preprocess_sample(const event_t *self, struct perf_session *session,
|
||||
struct addr_location *al, struct sample_data *data,
|
||||
symbol_filter_t filter);
|
||||
int event__parse_sample(const event_t *event, struct perf_session *session,
|
||||
struct sample_data *sample);
|
||||
|
||||
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 */
|
||||
|
170
tools/perf/util/evlist.c
Normal file
170
tools/perf/util/evlist.c
Normal file
@ -0,0 +1,170 @@
|
||||
#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;
|
||||
}
|
41
tools/perf/util/evlist.h
Normal file
41
tools/perf/util/evlist.h
Normal file
@ -0,0 +1,41 @@
|
||||
#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 */
|
@ -1,20 +1,33 @@
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "../perf.h"
|
||||
#include "util.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 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 *evsel = zalloc(sizeof(*evsel));
|
||||
|
||||
if (evsel != NULL) {
|
||||
evsel->idx = idx;
|
||||
evsel->attr = *attr;
|
||||
INIT_LIST_HEAD(&evsel->node);
|
||||
}
|
||||
if (evsel != NULL)
|
||||
perf_evsel__init(evsel, attr, idx);
|
||||
|
||||
return evsel;
|
||||
}
|
||||
@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
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)
|
||||
{
|
||||
evsel->counts = zalloc((sizeof(*evsel->counts) +
|
||||
@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel)
|
||||
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)
|
||||
{
|
||||
int cpu, thread;
|
||||
@ -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));
|
||||
xyarray__delete(evsel->fd);
|
||||
xyarray__delete(evsel->id);
|
||||
}
|
||||
|
||||
void perf_evsel__delete(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_evsel__exit(evsel);
|
||||
free(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,
|
||||
struct thread_map *threads)
|
||||
struct thread_map *threads, bool group, bool inherit)
|
||||
{
|
||||
int cpu, thread;
|
||||
|
||||
@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
return -1;
|
||||
|
||||
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++) {
|
||||
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
|
||||
threads->map[thread],
|
||||
cpus->map[cpu], -1, 0);
|
||||
cpus->map[cpu],
|
||||
group_fd, 0);
|
||||
if (FD(evsel, cpu, thread) < 0)
|
||||
goto out_close;
|
||||
|
||||
if (group && group_fd == -1)
|
||||
group_fd = FD(evsel, cpu, thread);
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,10 +232,9 @@ static struct {
|
||||
.threads = { -1, },
|
||||
};
|
||||
|
||||
int perf_evsel__open(struct perf_evsel *evsel,
|
||||
struct cpu_map *cpus, struct thread_map *threads)
|
||||
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
struct thread_map *threads, bool group, bool inherit)
|
||||
{
|
||||
|
||||
if (cpus == NULL) {
|
||||
/* Work around old compiler warnings about strict aliasing */
|
||||
cpus = &empty_cpu_map.map;
|
||||
@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel,
|
||||
if (threads == NULL)
|
||||
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)
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static int event__parse_id_sample(const event_t *event, u64 type,
|
||||
struct sample_data *sample)
|
||||
{
|
||||
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 event__parse_sample(const event_t *event, u64 type, bool sample_id_all,
|
||||
struct sample_data *data)
|
||||
{
|
||||
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 {
|
||||
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 list_head node;
|
||||
struct perf_event_attr attr;
|
||||
char *filter;
|
||||
struct xyarray *fd;
|
||||
struct xyarray *id;
|
||||
struct perf_counts *counts;
|
||||
int idx;
|
||||
void *priv;
|
||||
@ -36,19 +49,31 @@ struct perf_evsel {
|
||||
|
||||
struct cpu_map;
|
||||
struct thread_map;
|
||||
struct perf_evlist;
|
||||
|
||||
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);
|
||||
|
||||
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_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus);
|
||||
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);
|
||||
|
||||
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus);
|
||||
int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads);
|
||||
int perf_evsel__open(struct perf_evsel *evsel,
|
||||
struct cpu_map *cpus, struct thread_map *threads);
|
||||
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
|
||||
struct cpu_map *cpus, bool group, bool inherit);
|
||||
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
|
||||
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) \
|
||||
(evsel->attr.type == PERF_TYPE_##t && \
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "evlist.h"
|
||||
#include "util.h"
|
||||
#include "header.h"
|
||||
#include "../perf.h"
|
||||
@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi
|
||||
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;
|
||||
struct perf_session *session;
|
||||
@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
|
||||
|
||||
/* Write trace info */
|
||||
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;
|
||||
}
|
||||
|
||||
@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd)
|
||||
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_attr f_attr;
|
||||
@ -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);
|
||||
|
||||
if (at_exit) {
|
||||
err = perf_header__adds_write(self, fd);
|
||||
err = perf_header__adds_write(self, evlist, fd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self,
|
||||
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,
|
||||
struct perf_session *session __unused)
|
||||
{
|
||||
@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
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)
|
||||
return size;
|
||||
aligned_size = ALIGN(size, sizeof(u64));
|
||||
@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
|
||||
|
||||
process(&ev, NULL, session);
|
||||
|
||||
err = read_tracing_data(fd, pattrs);
|
||||
err = read_tracing_data(fd, &evlist->entries);
|
||||
write_padded(fd, NULL, 0, padding);
|
||||
|
||||
return aligned_size;
|
||||
|
@ -65,8 +65,11 @@ struct perf_header {
|
||||
int perf_header__init(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__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__add_attr(struct perf_header *self,
|
||||
@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process,
|
||||
int event__process_event_type(event_t *self,
|
||||
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,
|
||||
struct perf_session *session);
|
||||
int event__process_tracing_data(event_t *self,
|
||||
|
@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he)
|
||||
* 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 *parent = NULL;
|
||||
@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
|
||||
|
||||
if (!cmp) {
|
||||
iter->period += he->period;
|
||||
if (symbol_conf.use_callchain)
|
||||
callchain_merge(iter->callchain, he->callchain);
|
||||
if (symbol_conf.use_callchain) {
|
||||
callchain_cursor_reset(&self->callchain_cursor);
|
||||
callchain_merge(&self->callchain_cursor, iter->callchain,
|
||||
he->callchain);
|
||||
}
|
||||
hist_entry__free(he);
|
||||
return false;
|
||||
}
|
||||
@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self)
|
||||
next = rb_next(&n->rb_node);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
|
||||
u64 cumul;
|
||||
|
||||
child = rb_entry(node, struct callchain_node, rb_node);
|
||||
cumul = cumul_hits(child);
|
||||
cumul = callchain_cumul_hits(child);
|
||||
remaining -= cumul;
|
||||
|
||||
/*
|
||||
|
@ -77,6 +77,8 @@ struct hists {
|
||||
u64 event_stream;
|
||||
u32 type;
|
||||
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,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <linux/kernel.h>
|
||||
#include "../../../../include/linux/list.h"
|
||||
|
||||
#ifndef PERF_LIST_H
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "../../../include/linux/hw_breakpoint.h"
|
||||
#include "util.h"
|
||||
#include "../perf.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "parse-options.h"
|
||||
#include "parse-events.h"
|
||||
@ -11,10 +12,6 @@
|
||||
#include "header.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
int nr_counters;
|
||||
|
||||
LIST_HEAD(evsel_list);
|
||||
|
||||
struct event_symbol {
|
||||
u8 type;
|
||||
u64 config;
|
||||
@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name,
|
||||
/* sys + ':' + event + ':' + flags*/
|
||||
#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
|
||||
static enum event_result
|
||||
parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
|
||||
char *flags)
|
||||
parse_multiple_tracepoint_event(const struct option *opt, char *sys_name,
|
||||
const char *evt_exp, char *flags)
|
||||
{
|
||||
char evt_path[MAXPATHLEN];
|
||||
struct dirent *evt_ent;
|
||||
@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
|
||||
if (len < 0)
|
||||
return EVT_FAILED;
|
||||
|
||||
if (parse_events(NULL, event_opt, 0))
|
||||
if (parse_events(opt, event_opt, 0))
|
||||
return EVT_FAILED;
|
||||
}
|
||||
|
||||
return EVT_HANDLED_ALL;
|
||||
}
|
||||
|
||||
static enum event_result parse_tracepoint_event(const char **strp,
|
||||
struct perf_event_attr *attr)
|
||||
static enum event_result
|
||||
parse_tracepoint_event(const struct option *opt, const char **strp,
|
||||
struct perf_event_attr *attr)
|
||||
{
|
||||
const char *evt_name;
|
||||
char *flags = NULL, *comma_loc;
|
||||
@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp,
|
||||
return EVT_FAILED;
|
||||
if (strpbrk(evt_name, "*?")) {
|
||||
*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);
|
||||
} else {
|
||||
return parse_single_tracepoint_event(sys_name, evt_name,
|
||||
@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
|
||||
* Symbolic names are (almost) exactly matched.
|
||||
*/
|
||||
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;
|
||||
|
||||
ret = parse_tracepoint_event(str, attr);
|
||||
ret = parse_tracepoint_event(opt, str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
|
||||
@ -778,14 +777,15 @@ modifier:
|
||||
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;
|
||||
enum event_result ret;
|
||||
|
||||
for (;;) {
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
ret = parse_event_symbols(&str, &attr);
|
||||
ret = parse_event_symbols(opt, &str, &attr);
|
||||
if (ret == EVT_FAILED)
|
||||
return -1;
|
||||
|
||||
@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u
|
||||
|
||||
if (ret != EVT_HANDLED_ALL) {
|
||||
struct perf_evsel *evsel;
|
||||
evsel = perf_evsel__new(&attr,
|
||||
nr_counters);
|
||||
evsel = perf_evsel__new(&attr, evlist->nr_entries);
|
||||
if (evsel == NULL)
|
||||
return -1;
|
||||
list_add_tail(&evsel->node, &evsel_list);
|
||||
++nr_counters;
|
||||
perf_evlist__add(evlist, evsel);
|
||||
}
|
||||
|
||||
if (*str == 0)
|
||||
@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u
|
||||
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)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
struct perf_evsel *last = NULL;
|
||||
|
||||
if (!list_empty(&evsel_list))
|
||||
last = list_entry(evsel_list.prev, struct perf_evsel, node);
|
||||
if (evlist->nr_entries > 0)
|
||||
last = list_entry(evlist->entries.prev, struct perf_evsel, node);
|
||||
|
||||
if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
fprintf(stderr,
|
||||
@ -981,33 +980,3 @@ void print_events(void)
|
||||
|
||||
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 @@
|
||||
struct list_head;
|
||||
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 tracepoint_path {
|
||||
@ -25,8 +20,6 @@ struct tracepoint_path {
|
||||
extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
|
||||
extern bool have_tracepoints(struct list_head *evlist);
|
||||
|
||||
extern int nr_counters;
|
||||
|
||||
const char *event_name(struct perf_evsel *event);
|
||||
extern const char *__event_name(int type, u64 config);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <elf.h>
|
||||
|
||||
#undef _GNU_SOURCE
|
||||
#include "util.h"
|
||||
@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name,
|
||||
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 map *map;
|
||||
@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module)
|
||||
}
|
||||
}
|
||||
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
|
||||
@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist)
|
||||
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);
|
||||
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
int max_probe_points, const char *module,
|
||||
bool externs);
|
||||
extern int show_available_funcs(const char *module);
|
||||
|
||||
|
||||
/* Maximum index number of event-name postfix */
|
||||
|
@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
|
||||
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 */
|
||||
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,
|
||||
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, ¶m, 0);
|
||||
ret = param.retval;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct __find_variable_param {
|
||||
const char *name;
|
||||
Dwarf_Addr addr;
|
||||
@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int probe_point_line_walker(const char *fname, int lineno,
|
||||
Dwarf_Addr addr, void *data)
|
||||
{
|
||||
struct probe_finder *pf = data;
|
||||
int ret;
|
||||
|
||||
if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
|
||||
return 0;
|
||||
|
||||
pf->addr = addr;
|
||||
ret = call_probe_finder(NULL, pf);
|
||||
|
||||
/* Continue if no error, because the line will be in inline function */
|
||||
return ret < 0 ?: 0;
|
||||
}
|
||||
|
||||
/* Find probe point from its line number */
|
||||
static int find_probe_point_by_line(struct probe_finder *pf)
|
||||
{
|
||||
Dwarf_Lines *lines;
|
||||
Dwarf_Line *line;
|
||||
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++) {
|
||||
line = dwarf_onesrcline(lines, i);
|
||||
if (dwarf_lineno(line, &lineno) != 0 ||
|
||||
lineno != pf->lno)
|
||||
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_warning("Failed to get the address of the line.\n");
|
||||
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);
|
||||
/* Continuing, because target line might be inlined. */
|
||||
}
|
||||
return ret;
|
||||
return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
|
||||
}
|
||||
|
||||
/* Find lines which match lazy pattern */
|
||||
@ -1140,15 +1281,31 @@ out_close:
|
||||
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 */
|
||||
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;
|
||||
|
||||
if (list_empty(&pf->lcache)) {
|
||||
@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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++) {
|
||||
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;
|
||||
return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
|
||||
}
|
||||
|
||||
/* Callback parameter with return value */
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/* Search function declaration lines */
|
||||
static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
|
||||
static int line_range_walk_cb(const char *fname, int lineno,
|
||||
Dwarf_Addr addr __used,
|
||||
void *data)
|
||||
{
|
||||
struct dwarf_callback_param *param = data;
|
||||
struct line_finder *lf = param->data;
|
||||
const char *src;
|
||||
int lineno;
|
||||
struct line_finder *lf = data;
|
||||
|
||||
src = dwarf_decl_file(sp_die);
|
||||
if (src && strtailcmp(src, lf->fname) != 0)
|
||||
return DWARF_CB_OK;
|
||||
|
||||
if (dwarf_decl_line(sp_die, &lineno) != 0 ||
|
||||
if ((strtailcmp(fname, lf->fname) != 0) ||
|
||||
(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 (param->retval < 0)
|
||||
return DWARF_CB_ABORT;
|
||||
return DWARF_CB_OK;
|
||||
}
|
||||
if (line_range_add_line(fname, lineno, lf->lr) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
static int find_line_range_func_decl_lines(struct line_finder *lf)
|
||||
{
|
||||
struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
|
||||
dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0);
|
||||
return param.retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find line range from its line number */
|
||||
static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
|
||||
{
|
||||
Dwarf_Lines *lines;
|
||||
Dwarf_Line *line;
|
||||
size_t nlines, i;
|
||||
Dwarf_Addr addr;
|
||||
int lineno, ret = 0;
|
||||
const char *src;
|
||||
Dwarf_Die die_mem;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);
|
||||
|
||||
/* Update status */
|
||||
if (ret >= 0)
|
||||
@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
||||
struct line_finder *lf = param->data;
|
||||
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 &&
|
||||
die_compare_name(sp_die, lr->function)) {
|
||||
lf->fname = dwarf_decl_file(sp_die);
|
||||
|
@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
int perf_session__resolve_callchain(struct perf_session *self,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
{
|
||||
u8 cpumode = PERF_RECORD_MISC_USER;
|
||||
unsigned int i;
|
||||
struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
|
||||
int err;
|
||||
|
||||
if (!syms)
|
||||
return NULL;
|
||||
callchain_cursor_reset(&self->callchain_cursor);
|
||||
|
||||
for (i = 0; i < chain->nr; i++) {
|
||||
u64 ip = chain->ips[i];
|
||||
@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
|
||||
*parent = al.sym;
|
||||
if (!symbol_conf.use_callchain)
|
||||
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,
|
||||
@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s,
|
||||
if (iter->timestamp > limit)
|
||||
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,
|
||||
iter->file_offset);
|
||||
|
||||
@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session,
|
||||
/*
|
||||
* 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 */
|
||||
if (perf_session__preprocess_sample(session, event, &sample))
|
||||
|
@ -51,7 +51,8 @@ struct perf_session {
|
||||
int cwdlen;
|
||||
char *cwd;
|
||||
struct ordered_samples ordered_samples;
|
||||
char filename[0];
|
||||
struct callchain_cursor callchain_cursor;
|
||||
char filename[0];
|
||||
};
|
||||
|
||||
struct perf_event_ops;
|
||||
@ -94,10 +95,10 @@ 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 map_symbol *perf_session__resolve_callchain(struct perf_session *self,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
int perf_session__resolve_callchain(struct perf_session *self,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
|
||||
bool perf_session__has_traces(struct perf_session *self, const char *msg);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -7,61 +7,6 @@
|
||||
#include "util.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)
|
||||
{
|
||||
struct thread *self = zalloc(sizeof(*self));
|
||||
|
@ -18,24 +18,10 @@ struct thread {
|
||||
int comm_len;
|
||||
};
|
||||
|
||||
struct thread_map {
|
||||
int nr;
|
||||
int map[];
|
||||
};
|
||||
|
||||
struct perf_session;
|
||||
|
||||
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__comm_len(struct thread *self);
|
||||
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
|
||||
|
64
tools/perf/util/thread_map.c
Normal file
64
tools/perf/util/thread_map.c
Normal file
@ -0,0 +1,64 @@
|
||||
#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);
|
||||
}
|
15
tools/perf/util/thread_map.h
Normal file
15
tools/perf/util/thread_map.h
Normal file
@ -0,0 +1,15 @@
|
||||
#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,
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
u64 cumul = cumul_hits(child);
|
||||
u64 cumul = callchain_cumul_hits(child);
|
||||
struct callchain_list *chain;
|
||||
char folded_sign = ' ';
|
||||
int first = true;
|
||||
|
Loading…
Reference in New Issue
Block a user