Commit d380eaae authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf/core' of...

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
parents dda99116 ef1d1af2
......@@ -73,6 +73,10 @@ OPTIONS
(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", &params.max_probe_points,
"Set how many probe points can be found for a probe."),
OPT_BOOLEAN('F', "funcs", &params.show_funcs,
"Show potential probe-able functions."),
OPT_END()
};
......@@ -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;
}
retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
}
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
try_again:
FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0);
static void open_counters(struct perf_evlist *evlist)
{
struct perf_evsel *pos;
int cpu;
if (FD(evsel, nr_cpu, thread_index) < 0) {
list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
/*
* Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME.
*
* XXX this is kludgy but short term fix for problems introduced by
* eac23d1c that broke 'perf script' by having different sample_types
* when using multiple tracepoint events when we use a perf binary
* that tries to use sample_id_all on an older kernel.
*
* We need to move counter creation to perf_session, support
* different sample_types, etc.
*/
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
config_attr(pos, evlist);
retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
try_again:
if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) {
int err = errno;
if (err == EPERM || err == EACCES)
......@@ -364,7 +370,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
}
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 @@ static void create_counter(struct perf_evsel *evsel, int cpu)
#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;
}
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);
if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, false) < 0)
die("failed to mmap with %d (%s)\n", errno, strerror(errno));
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 @@ static int test__vmlinux_matches_kallsyms(void)
#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 @@ static int test__open_syscall_event_on_all_cpus(void)
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 @@ static void *display_thread(void *arg __used)
/* 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)
{
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)
static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu)
{
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;
/*
* If we're further behind than half the buffer, there's a chance
* the writer will bite our tail and mess up the samples under us.
*
* If we somehow ended up ahead of the head, we got messed up.
*
* In either case, truncate and restart at head.
*/
diff = head - old;
if (diff > md->mask / 2 || diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
/*
* head points to a known good entry, start there.
*/
old = head;
}
for (; old != head;) {
event_t *event = (event_t *)&data[old & md->mask];
event_t *event;
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;
}
while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) {
perf_session__parse_sample(self, event, &sample);
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;
for (i = 0; i < cpus->nr; i++) {
list_for_each_entry(counter, &evsel_list, node) {
for (thread_index = 0;
thread_index < threads->nr;
thread_index++) {
perf_session__mmap_read_counter(self,
counter, i, thread_index);
}
}
}
}
int i;
int nr_poll;
int group_fd;
for (i = 0; i < cpus->nr; i++)
perf_session__mmap_read_cpu(self, i);
}
static void start_counter(int i, struct perf_evsel *evsel)
static void start_counters(struct perf_evlist *evlist)
{
struct xyarray *mmap_array = evsel->priv;
struct mmap_data *mm;
struct perf_event_attr *attr;
int cpu = -1;
int thread_index;
if (target_tid == -1)
cpu = cpus->map[i];
struct perf_evsel *counter;
attr = &evsel->attr;
list_for_each_entry(counter, &evlist->entries, node) {
struct perf_event_attr *attr = &counter->attr;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (freq) {
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;
}
attr->inherit = (cpu < 0) && inherit;
attr->mmap = 1;
if (evlist->nr_entries > 1) {
attr->sample_type |= PERF_SAMPLE_ID;
attr->read_format |= PERF_FORMAT_ID;
}
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
attr->mmap = 1;
try_again:
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 @@ static void start_counter(int i, struct perf_evsel *evsel)
* 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 @@ static void start_counter(int i, struct perf_evsel *evsel)
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.
......
/*
* 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;
node->val_nr = cursor->nr - cursor->pos;
if (!node->val_nr)
pr_warning("Warning: empty node in callchain tree\n");
for (i = start; i < chain->nr; i++) {
cursor_node = callchain_cursor_current(cursor);
while (cursor_node) {
struct callchain_list *call;
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 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)
int callchain_append(struct callchain_root *root,
struct callchain_cursor *cursor,
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;
filter_context(chain, filtered, syms);
if (!filtered->nr)
goto end;
callchain_cursor_commit(cursor);
append_chain_children(&root->node, filtered, 0, period);
append_chain_children(&root->node, cursor, 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)
{
return merge_chain_branch(cursor, &dst->node, &src->node);
}
int callchain_cursor_append(struct callchain_cursor *cursor,
u64 ip, struct map *map, struct symbol *sym)
{
struct resolved_chain *chain;
int err;
struct callchain_cursor_node *node = *cursor->last;
chain = malloc(sizeof(*chain) +
src->max_depth * sizeof(struct resolved_ip));
if (!chain)
return -ENOMEM;
if (!node) {
node = calloc(sizeof(*node), 1);
if (!node)
return -ENOMEM;
chain->nr = 0;
*cursor->last = node;
}
err = merge_chain_branch(&dst->node, &src->node, chain);
node->ip = ip;
node->map = map;
node->sym = sym;
free(chain);
cursor->nr++;
return err;
cursor->last = &node->next;
return 0;
}
......@@ -16,7 +16,7 @@ enum chain_mode {
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 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
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 */
#include <poll.h>
#include "evlist.h"
#include "evsel.h"
#include "util.h"
#include <linux/bitops.h>
#include <linux/hash.h>
void perf_evlist__init(struct perf_evlist *evlist)
{
int i;
for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
INIT_HLIST_HEAD(&evlist->heads[i]);
INIT_LIST_HEAD(&evlist->entries);
}
struct perf_evlist *perf_evlist__new(void)
{
struct perf_evlist *evlist = zalloc(sizeof(*evlist));
if (evlist != NULL)
perf_evlist__init(evlist);
return evlist;
}
static void perf_evlist__purge(struct perf_evlist *evlist)
{
struct perf_evsel *pos, *n;
list_for_each_entry_safe(pos, n, &evlist->entries, node) {
list_del_init(&pos->node);
perf_evsel__delete(pos);
}
evlist->nr_entries = 0;
}
void perf_evlist__exit(struct perf_evlist *evlist)
{
free(evlist->mmap);
free(evlist->pollfd);
evlist->mmap = NULL;
evlist->pollfd = NULL;
}
void perf_evlist__delete(struct perf_evlist *evlist)
{
perf_evlist__purge(evlist);
perf_evlist__exit(evlist);
free(evlist);
}
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
{
list_add_tail(&entry->node, &evlist->entries);
++evlist->nr_entries;
}
int perf_evlist__add_default(struct perf_evlist *evlist)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
};
struct perf_evsel *evsel = perf_evsel__new(&attr, 0);
if (evsel == NULL)
return -ENOMEM;
perf_evlist__add(evlist, evsel);
return 0;
}
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads)
{
int nfds = ncpus * nthreads * evlist->nr_entries;
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
return evlist->pollfd != NULL ? 0 : -ENOMEM;
}
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
{
fcntl(fd, F_SETFL, O_NONBLOCK);
evlist->pollfd[evlist->nr_fds].fd = fd;
evlist->pollfd[evlist->nr_fds].events = POLLIN;
evlist->nr_fds++;
}
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
{
struct hlist_head *head;
struct hlist_node *pos;
struct perf_sample_id *sid;
int hash;
if (evlist->nr_entries == 1)
return list_entry(evlist->entries.next, struct perf_evsel, node);
hash = hash_64(id, PERF_EVLIST__HLIST_BITS);
head = &evlist->heads[hash];
hlist_for_each_entry(sid, pos, head, node)
if (sid->id == id)
return sid->evsel;
return NULL;
}
event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu)
{
/* XXX Move this to perf.c, making it generally available */
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
struct perf_mmap *md = &evlist->mmap[cpu];
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
event_t *event = NULL;
int diff;
/*
* If we're further behind than half the buffer, there's a chance
* the writer will bite our tail and mess up the samples under us.
*
* If we somehow ended up ahead of the head, we got messed up.
*
* In either case, truncate and restart at head.
*/
diff = head - old;
if (diff > md->mask / 2 || diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
/*
* head points to a known good entry, start there.
*/
old = head;
}
if (old != head) {
size_t size;
event = (event_t *)&data[old & md->mask];
size = event->header.size;
/*
* Event straddles the mmap boundary -- header should always
* be inside due to u64 alignment of output.
*/
if ((old & md->mask) + size != ((old + size) & md->mask)) {
unsigned int offset = old;
unsigned int len = min(sizeof(*event), size), cpy;
void *dst = &evlist->event_copy;
do {
cpy = min(md->mask + 1 - (offset & md->mask), len);
memcpy(dst, &data[offset & md->mask], cpy);
offset += cpy;
dst += cpy;
len -= cpy;
} while (len);
event = &evlist->event_copy;
}
old += size;
}
md->prev = old;
return event;
}
#ifndef __PERF_EVLIST_H
#define __PERF_EVLIST_H 1
#include <linux/list.h>
#include "../perf.h"
#include "event.h"
struct pollfd;
#define PERF_EVLIST__HLIST_BITS 8
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
struct perf_evlist {
struct list_head entries;
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
int nr_entries;
int nr_fds;
int mmap_len;
event_t event_copy;
struct perf_mmap *mmap;
struct pollfd *pollfd;
};
struct perf_evsel;
struct perf_evlist *perf_evlist__new(void);
void perf_evlist__init(struct perf_evlist *evlist);
void perf_evlist__exit(struct perf_evlist *evlist);
void perf_evlist__delete(struct perf_evlist *evlist);
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
int perf_evlist__add_default(struct perf_evlist *evlist);
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads);
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
event_t *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu);
#endif /* __PERF_EVLIST_H */
#include "evsel.h"
#include "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, bool group, bool inherit)
{
return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit);
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
struct thread_map *threads, bool group, bool inherit)
{
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit);
}
static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot,
int mask, int fd)
{
evlist->mmap[cpu].prev = 0;
evlist->mmap[cpu].mask = mask;
evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot,
MAP_SHARED, fd, 0);
if (evlist->mmap[cpu].base == MAP_FAILED)
return -1;
perf_evlist__add_pollfd(evlist, fd);
return 0;
}
static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, int fd)
{
struct perf_sample_id *sid;
u64 read_data[4] = { 0, };
int hash, id_idx = 1; /* The first entry is the counter value */
if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
read(fd, &read_data, sizeof(read_data)) == -1)
return -1;
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
++id_idx;
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
++id_idx;
sid = SID(evsel, cpu, thread);
sid->id = read_data[id_idx];
sid->evsel = evsel;
hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS);
hlist_add_head(&sid->node, &evlist->heads[hash]);
return 0;
}
/** perf_evlist__mmap - Create per cpu maps to receive events
*
* @evlist - list of events
* @cpus - cpu map being monitored
* @threads - threads map being monitored
* @pages - map length in pages
* @overwrite - overwrite older events?
*
* If overwrite is false the user needs to signal event consuption using:
*
* struct perf_mmap *m = &evlist->mmap[cpu];
* unsigned int head = perf_mmap__read_head(m);
*
* perf_mmap__write_tail(m, head)
*/
int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads, int pages, bool overwrite)
{
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
int mask = pages * page_size - 1, cpu;
struct perf_evsel *first_evsel, *evsel;
int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE);
if (evlist->mmap == NULL &&
perf_evlist__alloc_mmap(evlist, cpus->nr) < 0)
return -ENOMEM;
if (evlist->pollfd == NULL &&
perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0)
return -ENOMEM;
evlist->mmap_len = (pages + 1) * page_size;
first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
list_for_each_entry(evsel, &evlist->entries, node) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->id == NULL &&
perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0)
return -ENOMEM;
for (cpu = 0; cpu < cpus->nr; cpu++) {
for (thread = 0; thread < threads->nr; thread++) {
int fd = FD(evsel, cpu, thread);
if (evsel->idx || thread) {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT,
FD(first_evsel, cpu, 0)) != 0)
goto out_unmap;
} else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0)
goto out_unmap;
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0)
goto out_unmap;
}
}
}
return 0;
out_unmap:
for (cpu = 0; cpu < cpus->nr; cpu++) {
if (evlist->mmap[cpu].base != NULL) {
munmap(evlist->mmap[cpu].base, evlist->mmap_len);
evlist->mmap[cpu].base = NULL;
}
}
return -1;
}
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus)
static int event__parse_id_sample(const event_t *event, u64 type,
struct sample_data *sample)
{
return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
const u64 *array = event->sample.array;
array += ((event->header.size -
sizeof(event->header)) / sizeof(u64)) - 1;
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
sample->cpu = *p;
array--;
}
if (type & PERF_SAMPLE_STREAM_ID) {
sample->stream_id = *array;
array--;
}
if (type & PERF_SAMPLE_ID) {
sample->id = *array;
array--;
}
if (type & PERF_SAMPLE_TIME) {
sample->time = *array;
array--;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
sample->pid = p[0];
sample->tid = p[1];
}
return 0;
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads)
int event__parse_sample(const event_t *event, u64 type, bool sample_id_all,
struct sample_data *data)
{
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
const u64 *array;
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
if (event->header.type != PERF_RECORD_SAMPLE) {
if (!sample_id_all)
return 0;
return event__parse_id_sample(event, type, data);
}
array = event->sample.array;
if (type & PERF_SAMPLE_IP) {
data->ip = event->ip.ip;
array++;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
data->pid = p[0];
data->tid = p[1];
array++;
}
if (type & PERF_SAMPLE_TIME) {
data->time = *array;
array++;
}
if (type & PERF_SAMPLE_ADDR) {
data->addr = *array;
array++;
}
data->id = -1ULL;
if (type & PERF_SAMPLE_ID) {
data->id = *array;
array++;
}
if (type & PERF_SAMPLE_STREAM_ID) {
data->stream_id = *array;
array++;
}
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
data->cpu = *p;
array++;
}
if (type & PERF_SAMPLE_PERIOD) {
data->period = *array;
array++;
}
if (type & PERF_SAMPLE_READ) {
fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n");
return -1;
}
if (type & PERF_SAMPLE_CALLCHAIN) {
data->callchain = (struct ip_callchain *)array;
array += 1 + data->callchain->nr;
}
if (type & PERF_SAMPLE_RAW) {
u32 *p = (u32 *)array;
data->raw_size = *p;
p++;
data->raw_data = p;
}
return 0;
}
......@@ -24,11 +24,24 @@ struct perf_counts {
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,
......
#include <linux/kernel.h>
#include "../../../../include/linux/list.h"
#ifndef PERF_LIST_H
......
#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 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr)
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, &param, 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;
}
/* Find probe point from its line number */
static int find_probe_point_by_line(struct probe_finder *pf)
static int probe_point_line_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
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;
}
struct probe_finder *pf = data;
int ret;
for (i = 0; i < nlines && ret == 0; i++) {
line = dwarf_onesrcline(lines, i);
if (dwarf_lineno(line, &lineno) != 0 ||
lineno != pf->lno)
continue;
if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
return 0;
/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;
pf->addr = addr;
ret = call_probe_finder(NULL, pf);
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;
/* Continue if no error, because the line will be in inline function */
return ret < 0 ?: 0;
}
ret = call_probe_finder(NULL, pf);
/* Continuing, because target line might be inlined. */
}
return ret;
/* Find probe point from its line number */
static int find_probe_point_by_line(struct probe_finder *pf)
{
return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
}
/* Find lines which match lazy pattern */
......@@ -1140,15 +1281,31 @@ static int find_lazy_match_lines(struct list_head *head,
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, &param, 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;
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;
}
int 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);
......
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include "thread_map.h"
/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
{
if (dir->d_name[0] == '.')
return 0;
else
return 1;
}
struct thread_map *thread_map__new_by_pid(pid_t pid)
{
struct thread_map *threads;
char name[256];
int items;
struct dirent **namelist = NULL;
int i;
sprintf(name, "/proc/%d/task", pid);
items = scandir(name, &namelist, filter, NULL);
if (items <= 0)
return NULL;
threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
if (threads != NULL) {
for (i = 0; i < items; i++)
threads->map[i] = atoi(namelist[i]->d_name);
threads->nr = items;
}
for (i=0; i<items; i++)
free(namelist[i]);
free(namelist);
return threads;
}
struct thread_map *thread_map__new_by_tid(pid_t tid)
{
struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
if (threads != NULL) {
threads->map[0] = tid;
threads->nr = 1;
}
return threads;
}
struct thread_map *thread_map__new(pid_t pid, pid_t tid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);
return thread_map__new_by_tid(tid);
}
void thread_map__delete(struct thread_map *threads)
{
free(threads);
}
#ifndef __PERF_THREAD_MAP_H
#define __PERF_THREAD_MAP_H
#include <sys/types.h>
struct thread_map {
int nr;
int map[];
};
struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid);
void thread_map__delete(struct thread_map *threads);
#endif /* __PERF_THREAD_MAP_H */
......@@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment