Commit 79928928 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-2' of...

Merge tag 'perf-core-for-mingo-2' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - List perf probes to stdout. (Masami Hiramatsu)

  - Return error when none of the requested probes were
    installed. (Masami Hiramatsu)

  - Cut off the gcc optimization postfixes from
    function name in 'perf probe'. (Masami Hiramatsu)

  - Allow disabling/enabling events dynamicly in 'perf top':
    a 'perf top' session can instantly become a 'perf report'
    one, i.e. going from dynamic analysis to a static one,
    returning to a dynamic one is possible, to toogle the
    modes, just press CTRL+z. (Arnaldo Carvalho de Melo)

  - Greatly speed up 'perf probe --list' by caching debuginfo.
    (Masami Hiramatsu)

  - Fix 'perf trace' race condition at the end of started
    workloads. (Sukadev Bhattiprolu)

  - Fix a problem when opening old perf.data with different
    byte order. (Wang Nan)

Infrastructure changes:

  - Replace map->referenced & maps->removed_maps with
    map->refcnt. (Arnaldo Carvalho de Melo)

  - Introduce the xyarray__reset() function. (Jiri Olsa)

  - Add thread_map__(alloc|realloc)() helpers. (Jiri Olsa)

  - Move perf_evsel__(alloc|free|reset)_counts into stat object. (Jiri Olsa)

  - Introduce perf_counts__(new|delete|reset)() functions. (Jiri Olsa)

  - Ignore .config-detected in .gitignore. (Wang Nan)

  - Move libtraceevent dynamic list to separated LDFLAGS
    variable. (Wang Nan)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 61d67d56 5d484f99
......@@ -28,3 +28,4 @@ config.mak.autogen
*-flex.*
*.pyc
*.pyo
.config-detected
......@@ -174,7 +174,7 @@ LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
export LIBTRACEEVENT
LIBTRACEEVENT_DYNAMIC_LIST = $(TE_PATH)libtraceevent-dynamic-list
LDFLAGS += -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYNAMIC_LIST)
LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYNAMIC_LIST)
LIBAPI = $(LIB_PATH)libapi.a
export LIBAPI
......@@ -190,8 +190,9 @@ python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPI)
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
$(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(LIBTRACEEVENT_DYNAMIC_LIST)
$(QUIET_GEN)CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS)' \
$(PYTHON_WORD) util/setup.py \
--quiet build_ext; \
mkdir -p $(OUTPUT)python && \
cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/
......@@ -282,7 +283,8 @@ $(PERF_IN): $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h FORCE
$(Q)$(MAKE) $(build)=perf
$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(PERF_IN) $(LIBS) -o $@
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
$(PERF_IN) $(LIBS) -o $@
$(GTK_IN): FORCE
$(Q)$(MAKE) $(build)=gtk
......
......@@ -178,24 +178,19 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel)
{
void *addr;
size_t sz;
struct perf_counts *counts;
sz = sizeof(*evsel->counts) +
(perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values));
counts = perf_counts__new(perf_evsel__nr_cpus(evsel));
if (counts)
evsel->prev_raw_counts = counts;
addr = zalloc(sz);
if (!addr)
return -ENOMEM;
evsel->prev_raw_counts = addr;
return 0;
return counts ? 0 : -ENOMEM;
}
static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
{
zfree(&evsel->prev_raw_counts);
perf_counts__delete(evsel->prev_raw_counts);
evsel->prev_raw_counts = NULL;
}
static void perf_evlist__free_stats(struct perf_evlist *evlist)
......
......@@ -235,10 +235,13 @@ static void perf_top__show_details(struct perf_top *top)
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel,
0, top->sym_pcnt_filter, top->print_entries, 4);
if (top->zero)
symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
else
symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
if (top->evlist->enabled) {
if (top->zero)
symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
else
symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
}
if (more != 0)
printf("%d lines not displayed, maybe increase display entries [e]\n", more);
out_unlock:
......@@ -276,11 +279,13 @@ static void perf_top__print_sym_table(struct perf_top *top)
return;
}
if (top->zero) {
hists__delete_entries(hists);
} else {
hists__decay_entries(hists, top->hide_user_symbols,
top->hide_kernel_symbols);
if (top->evlist->enabled) {
if (top->zero) {
hists__delete_entries(hists);
} else {
hists__decay_entries(hists, top->hide_user_symbols,
top->hide_kernel_symbols);
}
}
hists__collapse_resort(hists, NULL);
......@@ -545,11 +550,13 @@ static void perf_top__sort_new_samples(void *arg)
hists = evsel__hists(t->sym_evsel);
if (t->zero) {
hists__delete_entries(hists);
} else {
hists__decay_entries(hists, t->hide_user_symbols,
t->hide_kernel_symbols);
if (t->evlist->enabled) {
if (t->zero) {
hists__delete_entries(hists);
} else {
hists__decay_entries(hists, t->hide_user_symbols,
t->hide_kernel_symbols);
}
}
hists__collapse_resort(hists, NULL);
......@@ -579,8 +586,21 @@ static void *display_thread_tui(void *arg)
hists->uid_filter_str = top->record_opts.target.uid_str;
}
perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent,
&top->session->header.env);
while (true) {
int key = perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
top->min_percent,
&top->session->header.env);
if (key != CTRL('z'))
break;
perf_evlist__toggle_enable(top->evlist);
/*
* No need to refresh, resort/decay histogram entries
* if we are not collecting samples:
*/
hbt.refresh = top->evlist->enabled ? top->delay_secs : 0;
}
done = 1;
return NULL;
......
......@@ -3,6 +3,7 @@
#include "thread_map.h"
#include "cpumap.h"
#include "debug.h"
#include "stat.h"
int test__openat_syscall_event_on_all_cpus(void)
{
......
......@@ -1736,6 +1736,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"t Zoom into current Thread\n"
"V Verbose (DSO names in callchains, etc)\n"
"z Toggle zeroing of samples\n"
"CTRL+z Enable/Disable events\n"
"/ Filter symbol by name";
if (browser == NULL)
......@@ -1900,6 +1901,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
/* Fall thru */
case 'q':
case CTRL('c'):
case CTRL('z'):
goto out_free_stack;
default:
continue;
......
......@@ -297,6 +297,8 @@ void perf_evlist__disable(struct perf_evlist *evlist)
PERF_EVENT_IOC_DISABLE, 0);
}
}
evlist->enabled = false;
}
void perf_evlist__enable(struct perf_evlist *evlist)
......@@ -316,6 +318,13 @@ void perf_evlist__enable(struct perf_evlist *evlist)
PERF_EVENT_IOC_ENABLE, 0);
}
}
evlist->enabled = true;
}
void perf_evlist__toggle_enable(struct perf_evlist *evlist)
{
(evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist);
}
int perf_evlist__disable_event(struct perf_evlist *evlist,
......@@ -634,11 +643,18 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
{
struct perf_mmap *md = &evlist->mmap[idx];
u64 head = perf_mmap__read_head(md);
u64 head;
u64 old = md->prev;
unsigned char *data = md->base + page_size;
union perf_event *event = NULL;
/*
* Check if event was unmapped due to a POLLHUP/POLLERR.
*/
if (!atomic_read(&md->refcnt))
return NULL;
head = perf_mmap__read_head(md);
if (evlist->overwrite) {
/*
* If we're further behind than half the buffer, there's a chance
......
......@@ -41,6 +41,7 @@ struct perf_evlist {
int nr_groups;
int nr_mmaps;
bool overwrite;
bool enabled;
size_t mmap_len;
int id_pos;
int is_pos;
......@@ -139,6 +140,7 @@ void perf_evlist__munmap(struct perf_evlist *evlist);
void perf_evlist__disable(struct perf_evlist *evlist);
void perf_evlist__enable(struct perf_evlist *evlist);
void perf_evlist__toggle_enable(struct perf_evlist *evlist);
int perf_evlist__disable_event(struct perf_evlist *evlist,
struct perf_evsel *evsel);
......
......@@ -26,6 +26,7 @@
#include "perf_regs.h"
#include "debug.h"
#include "trace-event.h"
#include "stat.h"
static struct {
bool sample_id_all;
......@@ -851,19 +852,6 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
return 0;
}
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
{
memset(evsel->counts, 0, (sizeof(*evsel->counts) +
(ncpus * sizeof(struct perf_counts_values))));
}
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
{
evsel->counts = zalloc((sizeof(*evsel->counts) +
(ncpus * sizeof(struct perf_counts_values))));
return evsel->counts != NULL ? 0 : -ENOMEM;
}
static void perf_evsel__free_fd(struct perf_evsel *evsel)
{
xyarray__delete(evsel->fd);
......@@ -891,11 +879,6 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
}
}
void perf_evsel__free_counts(struct perf_evsel *evsel)
{
zfree(&evsel->counts);
}
void perf_evsel__exit(struct perf_evsel *evsel)
{
assert(list_empty(&evsel->node));
......
......@@ -170,9 +170,6 @@ const char *perf_evsel__group_name(struct perf_evsel *evsel);
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
void perf_evsel__free_counts(struct perf_evsel *evsel);
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
......
......@@ -313,8 +313,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
memset(&he->stat, 0, sizeof(he->stat));
}
if (he->ms.map)
he->ms.map->referenced = true;
map__get(he->ms.map);
if (he->branch_info) {
/*
......@@ -324,6 +323,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
*/
he->branch_info = malloc(sizeof(*he->branch_info));
if (he->branch_info == NULL) {
map__zput(he->ms.map);
free(he->stat_acc);
free(he);
return NULL;
......@@ -332,17 +332,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
memcpy(he->branch_info, template->branch_info,
sizeof(*he->branch_info));
if (he->branch_info->from.map)
he->branch_info->from.map->referenced = true;
if (he->branch_info->to.map)
he->branch_info->to.map->referenced = true;
map__get(he->branch_info->from.map);
map__get(he->branch_info->to.map);
}
if (he->mem_info) {
if (he->mem_info->iaddr.map)
he->mem_info->iaddr.map->referenced = true;
if (he->mem_info->daddr.map)
he->mem_info->daddr.map->referenced = true;
map__get(he->mem_info->iaddr.map);
map__get(he->mem_info->daddr.map);
}
if (symbol_conf.use_callchain)
......@@ -407,9 +403,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
* the history counter to increment.
*/
if (he->ms.map != entry->ms.map) {
he->ms.map = entry->ms.map;
if (he->ms.map)
he->ms.map->referenced = true;
map__put(he->ms.map);
he->ms.map = map__get(entry->ms.map);
}
goto out;
}
......@@ -933,8 +928,20 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
void hist_entry__delete(struct hist_entry *he)
{
thread__zput(he->thread);
zfree(&he->branch_info);
zfree(&he->mem_info);
map__zput(he->ms.map);
if (he->branch_info) {
map__zput(he->branch_info->from.map);
map__zput(he->branch_info->to.map);
zfree(&he->branch_info);
}
if (he->mem_info) {
map__zput(he->mem_info->iaddr.map);
map__zput(he->mem_info->daddr.map);
zfree(&he->mem_info);
}
zfree(&he->stat_acc);
free_srcline(he->srcline);
free_callchain(he->callchain);
......
......@@ -137,7 +137,6 @@ void map__init(struct map *map, enum map_type type,
map->unmap_ip = map__unmap_ip;
RB_CLEAR_NODE(&map->rb_node);
map->groups = NULL;
map->referenced = false;
map->erange_warned = false;
atomic_set(&map->refcnt, 1);
}
......@@ -439,7 +438,6 @@ static void maps__init(struct maps *maps)
{
maps->entries = RB_ROOT;
pthread_rwlock_init(&maps->lock, NULL);
INIT_LIST_HEAD(&maps->removed_maps);
}
void map_groups__init(struct map_groups *mg, struct machine *machine)
......@@ -466,21 +464,10 @@ static void __maps__purge(struct maps *maps)
}
}
static void __maps__purge_removed_maps(struct maps *maps)
{
struct map *pos, *n;
list_for_each_entry_safe(pos, n, &maps->removed_maps, node) {
list_del_init(&pos->node);
map__put(pos);
}
}
static void maps__exit(struct maps *maps)
{
pthread_rwlock_wrlock(&maps->lock);
__maps__purge(maps);
__maps__purge_removed_maps(maps);
pthread_rwlock_unlock(&maps->lock);
}
......@@ -499,8 +486,6 @@ bool map_groups__empty(struct map_groups *mg)
for (i = 0; i < MAP__NR_TYPES; ++i) {
if (maps__first(&mg->maps[i]))
return false;
if (!list_empty(&mg->maps[i].removed_maps))
return false;
}
return true;
......@@ -621,7 +606,7 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type,
return printed += maps__fprintf(&mg->maps[type], fp);
}
static size_t map_groups__fprintf_maps(struct map_groups *mg, FILE *fp)
size_t map_groups__fprintf(struct map_groups *mg, FILE *fp)
{
size_t printed = 0, i;
for (i = 0; i < MAP__NR_TYPES; ++i)
......@@ -629,39 +614,6 @@ static size_t map_groups__fprintf_maps(struct map_groups *mg, FILE *fp)
return printed;
}
static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg,
enum map_type type, FILE *fp)
{
struct map *pos;
size_t printed = 0;
list_for_each_entry(pos, &mg->maps[type].removed_maps, node) {
printed += fprintf(fp, "Map:");
printed += map__fprintf(pos, fp);
if (verbose > 1) {
printed += dso__fprintf(pos->dso, type, fp);
printed += fprintf(fp, "--\n");
}
}
return printed;
}
static size_t map_groups__fprintf_removed_maps(struct map_groups *mg,
FILE *fp)
{
size_t printed = 0, i;
for (i = 0; i < MAP__NR_TYPES; ++i)
printed += __map_groups__fprintf_removed_maps(mg, i, fp);
return printed;
}
size_t map_groups__fprintf(struct map_groups *mg, FILE *fp)
{
size_t printed = map_groups__fprintf_maps(mg, fp);
printed += fprintf(fp, "Removed maps:\n");
return printed + map_groups__fprintf_removed_maps(mg, fp);
}
static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
{
struct rb_root *root;
......@@ -719,13 +671,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
map__fprintf(after, fp);
}
put_map:
/*
* If we have references, just move them to a separate list.
*/
if (pos->referenced)
list_add_tail(&pos->node, &maps->removed_maps);
else
map__put(pos);
map__put(pos);
if (err)
goto out;
......
......@@ -34,7 +34,6 @@ struct map {
u64 start;
u64 end;
u8 /* enum map_type */ type;
bool referenced;
bool erange_warned;
u32 priv;
u32 prot;
......@@ -63,7 +62,6 @@ struct kmap {
struct maps {
struct rb_root entries;
pthread_rwlock_t lock;
struct list_head removed_maps;
};
struct map_groups {
......@@ -161,6 +159,14 @@ static inline struct map *map__get(struct map *map)
void map__put(struct map *map);
static inline void __map__zput(struct map **map)
{
map__put(*map);
*map = NULL;
}
#define map__zput(map) __map__zput(&map)
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *map, FILE *fp);
size_t map__fprintf_dsoname(struct map *map, FILE *fp);
......
......@@ -246,6 +246,20 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
clear_probe_trace_event(tevs + i);
}
static bool kprobe_blacklist__listed(unsigned long address);
static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
{
/* Get the address of _etext for checking non-probable text symbol */
if (kernel_get_symbol_address_by_name("_etext", false) < address)
pr_warning("%s is out of .text, skip it.\n", symbol);
else if (kprobe_blacklist__listed(address))
pr_warning("%s is blacklisted function, skip it.\n", symbol);
else
return false;
return true;
}
#ifdef HAVE_DWARF_SUPPORT
static int kernel_get_module_dso(const char *module, struct dso **pdso)
......@@ -415,6 +429,41 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
return ret;
}
/* For caching the last debuginfo */
static struct debuginfo *debuginfo_cache;
static char *debuginfo_cache_path;
static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
{
if ((debuginfo_cache_path && !strcmp(debuginfo_cache_path, module)) ||
(!debuginfo_cache_path && !module && debuginfo_cache))
goto out;
/* Copy module path */
free(debuginfo_cache_path);
if (module) {
debuginfo_cache_path = strdup(module);
if (!debuginfo_cache_path) {
debuginfo__delete(debuginfo_cache);
debuginfo_cache = NULL;
goto out;
}
}
debuginfo_cache = open_debuginfo(module, silent);
if (!debuginfo_cache)
zfree(&debuginfo_cache_path);
out:
return debuginfo_cache;
}
static void debuginfo_cache__exit(void)
{
debuginfo__delete(debuginfo_cache);
debuginfo_cache = NULL;
zfree(&debuginfo_cache_path);
}
static int get_text_start_address(const char *exec, unsigned long *address)
{
......@@ -476,12 +525,11 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
pr_debug("try to find information at %" PRIx64 " in %s\n", addr,
tp->module ? : "kernel");
dinfo = open_debuginfo(tp->module, verbose == 0);
if (dinfo) {
dinfo = debuginfo_cache__open(tp->module, verbose == 0);
if (dinfo)
ret = debuginfo__find_probe_point(dinfo,
(unsigned long)addr, pp);
debuginfo__delete(dinfo);
} else
else
ret = -ENOENT;
if (ret > 0) {
......@@ -559,7 +607,6 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
bool uprobe)
{
struct ref_reloc_sym *reloc_sym;
u64 etext_addr;
char *tmp;
int i, skipped = 0;
......@@ -575,31 +622,28 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
pr_warning("Relocated base symbol is not found!\n");
return -EINVAL;
}
/* Get the address of _etext for checking non-probable text symbol */
etext_addr = kernel_get_symbol_address_by_name("_etext", false);
for (i = 0; i < ntevs; i++) {
if (tevs[i].point.address && !tevs[i].point.retprobe) {
/* If we found a wrong one, mark it by NULL symbol */
if (etext_addr < tevs[i].point.address) {
pr_warning("%s+%lu is out of .text, skip it.\n",
tevs[i].point.symbol, tevs[i].point.offset);
tmp = NULL;
skipped++;
} else {
tmp = strdup(reloc_sym->name);
if (!tmp)
return -ENOMEM;
}
/* If we have no realname, use symbol for it */
if (!tevs[i].point.realname)
tevs[i].point.realname = tevs[i].point.symbol;
else
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
reloc_sym->unrelocated_addr;
if (!tevs[i].point.address || tevs[i].point.retprobe)
continue;
/* If we found a wrong one, mark it by NULL symbol */
if (kprobe_warn_out_range(tevs[i].point.symbol,
tevs[i].point.address)) {
tmp = NULL;
skipped++;
} else {
tmp = strdup(reloc_sym->name);
if (!tmp)
return -ENOMEM;
}
/* If we have no realname, use symbol for it */
if (!tevs[i].point.realname)
tevs[i].point.realname = tevs[i].point.symbol;
else
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
reloc_sym->unrelocated_addr;
}
return skipped;
}
......@@ -920,6 +964,10 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
#else /* !HAVE_DWARF_SUPPORT */
static void debuginfo_cache__exit(void)
{
}
static int
find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
struct perf_probe_point *pp __maybe_unused,
......@@ -2126,9 +2174,31 @@ kprobe_blacklist__find_by_address(struct list_head *blacklist,
return NULL;
}
/* Show an event */
static int show_perf_probe_event(struct perf_probe_event *pev,
const char *module)
static LIST_HEAD(kprobe_blacklist);
static void kprobe_blacklist__init(void)
{
if (!list_empty(&kprobe_blacklist))
return;
if (kprobe_blacklist__load(&kprobe_blacklist) < 0)
pr_debug("No kprobe blacklist support, ignored\n");
}
static void kprobe_blacklist__release(void)
{
kprobe_blacklist__delete(&kprobe_blacklist);
}
static bool kprobe_blacklist__listed(unsigned long address)
{
return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address);
}
static int perf_probe_event__sprintf(const char *group, const char *event,
struct perf_probe_event *pev,
const char *module,
struct strbuf *result)
{
int i, ret;
char buf[128];
......@@ -2139,29 +2209,50 @@ static int show_perf_probe_event(struct perf_probe_event *pev,
if (!place)
return -EINVAL;
ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
ret = e_snprintf(buf, 128, "%s:%s", group, event);
if (ret < 0)
return ret;
goto out;
pr_info(" %-20s (on %s", buf, place);
strbuf_addf(result, " %-20s (on %s", buf, place);
if (module)
pr_info(" in %s", module);
strbuf_addf(result, " in %s", module);
if (pev->nargs > 0) {
pr_info(" with");
strbuf_addstr(result, " with");
for (i = 0; i < pev->nargs; i++) {
ret = synthesize_perf_probe_arg(&pev->args[i],
buf, 128);
if (ret < 0)
break;
pr_info(" %s", buf);
goto out;
strbuf_addf(result, " %s", buf);
}
}
pr_info(")\n");
strbuf_addch(result, ')');
out:
free(place);
return ret;
}
/* Show an event */
static int show_perf_probe_event(const char *group, const char *event,
struct perf_probe_event *pev,
const char *module, bool use_stdout)
{
struct strbuf buf = STRBUF_INIT;
int ret;
ret = perf_probe_event__sprintf(group, event, pev, module, &buf);
if (ret >= 0) {
if (use_stdout)
printf("%s\n", buf.buf);
else
pr_info("%s\n", buf.buf);
}
strbuf_release(&buf);
return ret;
}
static bool filter_probe_trace_event(struct probe_trace_event *tev,
struct strfilter *filter)
{
......@@ -2200,9 +2291,11 @@ static int __show_perf_probe_events(int fd, bool is_kprobe,
goto next;
ret = convert_to_perf_probe_event(&tev, &pev,
is_kprobe);
if (ret >= 0)
ret = show_perf_probe_event(&pev,
tev.point.module);
if (ret < 0)
goto next;
ret = show_perf_probe_event(pev.group, pev.event,
&pev, tev.point.module,
true);
}
next:
clear_perf_probe_event(&pev);
......@@ -2211,6 +2304,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe,
break;
}
strlist__delete(rawlist);
/* Cleanup cached debuginfo if needed */
debuginfo_cache__exit();
return ret;
}
......@@ -2316,6 +2411,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
struct strlist *namelist, bool allow_suffix)
{
int i, ret;
char *p;
if (*base == '.')
base++;
......@@ -2326,6 +2422,10 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
pr_debug("snprintf() failed: %d\n", ret);
return ret;
}
/* Cut off the postfixes (e.g. .const, .isra)*/
p = strchr(buf, '.');
if (p && p != buf)
*p = '\0';
if (!strlist__has_entry(namelist, buf))
return 0;
......@@ -2381,10 +2481,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
int i, fd, ret;
struct probe_trace_event *tev = NULL;
char buf[64];
const char *event, *group;
const char *event = NULL, *group = NULL;
struct strlist *namelist;
LIST_HEAD(blacklist);
struct kprobe_blacklist_node *node;
bool safename;
if (pev->uprobes)
......@@ -2404,28 +2502,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
ret = -ENOMEM;
goto close_out;
}
/* Get kprobe blacklist if exists */
if (!pev->uprobes) {
ret = kprobe_blacklist__load(&blacklist);
if (ret < 0)
pr_debug("No kprobe blacklist support, ignored\n");
}
safename = (pev->point.function && !strisglob(pev->point.function));
ret = 0;
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
/* Skip if the symbol is out of .text (marked previously) */
/* Skip if the symbol is out of .text or blacklisted */
if (!tev->point.symbol)
continue;
/* Ensure that the address is NOT blacklisted */
node = kprobe_blacklist__find_by_address(&blacklist,
tev->point.address);
if (node) {
pr_warning("Warning: Skipped probing on blacklisted function: %s\n", node->symbol);
continue;
}
if (pev->event)
event = pev->event;
......@@ -2458,15 +2543,12 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
/* Add added event name to namelist */
strlist__add(namelist, event);
/* Trick here - save current event/group */
event = pev->event;
group = pev->group;
pev->event = tev->event;
pev->group = tev->group;
show_perf_probe_event(pev, tev->point.module);
/* Trick here - restore current event/group */
pev->event = (char *)event;
pev->group = (char *)group;
/* We use tev's name for showing new events */
show_perf_probe_event(tev->group, tev->event, pev,
tev->point.module, false);
/* Save the last valid name */
event = tev->event;
group = tev->group;
/*
* Probes after the first probe which comes from same
......@@ -2480,14 +2562,12 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
warn_uprobe_event_compat(tev);
/* Note that it is possible to skip all events because of blacklist */
if (ret >= 0 && tev->event) {
if (ret >= 0 && event) {
/* Show how to use the event. */
pr_info("\nYou can now use it in all perf tools, such as:\n\n");
pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event);
pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
}
kprobe_blacklist__delete(&blacklist);
strlist__delete(namelist);
close_out:
close(fd);
......@@ -2537,7 +2617,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
struct perf_probe_point *pp = &pev->point;
struct probe_trace_point *tp;
int num_matched_functions;
int ret, i, j;
int ret, i, j, skipped = 0;
map = get_target_map(pev->target, pev->uprobes);
if (!map) {
......@@ -2605,7 +2685,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
/* Add one probe point */
tp->address = map->unmap_ip(map, sym->start) + pp->offset;
if (reloc_sym) {
/* If we found a wrong one, mark it by NULL symbol */
if (!pev->uprobes &&
kprobe_warn_out_range(sym->name, tp->address)) {
tp->symbol = NULL; /* Skip it */
skipped++;
} else if (reloc_sym) {
tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
tp->offset = tp->address - reloc_sym->addr;
} else {
......@@ -2641,6 +2726,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
arch__fix_tev_from_maps(pev, tev, map);
}
if (ret == skipped) {
ret = -ENOENT;
goto err_out;
}
out:
put_target_map(map, pev->uprobes);
......@@ -2711,6 +2800,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
/* Loop 1: convert all events */
for (i = 0; i < npevs; i++) {
pkgs[i].pev = &pevs[i];
/* Init kprobe blacklist if needed */
if (!pkgs[i].pev->uprobes)
kprobe_blacklist__init();
/* Convert with or without debuginfo */
ret = convert_to_probe_trace_events(pkgs[i].pev,
&pkgs[i].tevs);
......@@ -2718,6 +2810,8 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
goto end;
pkgs[i].ntevs = ret;
}
/* This just release blacklist only if allocated */
kprobe_blacklist__release();
/* Loop 2: add all events */
for (i = 0; i < npevs; i++) {
......
......@@ -16,6 +16,7 @@ util/util.c
util/xyarray.c
util/cgroup.c
util/rblist.c
util/stat.c
util/strlist.c
util/trace-event.c
../../lib/rbtree.c
......@@ -517,20 +517,42 @@ void perf_event__attr_swap(struct perf_event_attr *attr)
{
attr->type = bswap_32(attr->type);
attr->size = bswap_32(attr->size);
attr->config = bswap_64(attr->config);
attr->sample_period = bswap_64(attr->sample_period);
attr->sample_type = bswap_64(attr->sample_type);
attr->read_format = bswap_64(attr->read_format);
attr->wakeup_events = bswap_32(attr->wakeup_events);
attr->bp_type = bswap_32(attr->bp_type);
attr->bp_addr = bswap_64(attr->bp_addr);
attr->bp_len = bswap_64(attr->bp_len);
attr->branch_sample_type = bswap_64(attr->branch_sample_type);
attr->sample_regs_user = bswap_64(attr->sample_regs_user);
attr->sample_stack_user = bswap_32(attr->sample_stack_user);
attr->aux_watermark = bswap_32(attr->aux_watermark);
swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));
#define bswap_safe(f, n) \
(attr->size > (offsetof(struct perf_event_attr, f) + \
sizeof(attr->f) * (n)))
#define bswap_field(f, sz) \
do { \
if (bswap_safe(f, 0)) \
attr->f = bswap_##sz(attr->f); \
} while(0)
#define bswap_field_32(f) bswap_field(f, 32)
#define bswap_field_64(f) bswap_field(f, 64)
bswap_field_64(config);
bswap_field_64(sample_period);
bswap_field_64(sample_type);
bswap_field_64(read_format);
bswap_field_32(wakeup_events);
bswap_field_32(bp_type);
bswap_field_64(bp_addr);
bswap_field_64(bp_len);
bswap_field_64(branch_sample_type);
bswap_field_64(sample_regs_user);
bswap_field_32(sample_stack_user);
bswap_field_32(aux_watermark);
/*
* After read_format are bitfields. Check read_format because
* we are unable to use offsetof on bitfield.
*/
if (bswap_safe(read_format, 1))
swap_bitfield((u8 *) (&attr->read_format + 1),
sizeof(u64));
#undef bswap_field_64
#undef bswap_field_32
#undef bswap_field
#undef bswap_safe
}
static void perf_event__hdr_attr_swap(union perf_event *event,
......
......@@ -94,3 +94,39 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel)
}
}
}
struct perf_counts *perf_counts__new(int ncpus)
{
int size = sizeof(struct perf_counts) +
ncpus * sizeof(struct perf_counts_values);
return zalloc(size);
}
void perf_counts__delete(struct perf_counts *counts)
{
free(counts);
}
static void perf_counts__reset(struct perf_counts *counts, int ncpus)
{
memset(counts, 0, (sizeof(*counts) +
(ncpus * sizeof(struct perf_counts_values))));
}
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
{
perf_counts__reset(evsel->counts, ncpus);
}
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
{
evsel->counts = perf_counts__new(ncpus);
return evsel->counts != NULL ? 0 : -ENOMEM;
}
void perf_evsel__free_counts(struct perf_evsel *evsel)
{
perf_counts__delete(evsel->counts);
evsel->counts = NULL;
}
......@@ -62,4 +62,10 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count,
void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
double avg, int cpu, enum aggr_mode aggr);
struct perf_counts *perf_counts__new(int ncpus);
void perf_counts__delete(struct perf_counts *counts);
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
void perf_evsel__free_counts(struct perf_evsel *evsel);
#endif
......@@ -20,6 +20,15 @@ static int filter(const struct dirent *dir)
return 1;
}
static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
{
size_t size = sizeof(*map) + sizeof(pid_t) * nr;
return realloc(map, size);
}
#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
struct thread_map *thread_map__new_by_pid(pid_t pid)
{
struct thread_map *threads;
......@@ -33,7 +42,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
if (items <= 0)
return NULL;
threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
threads = thread_map__alloc(items);
if (threads != NULL) {
for (i = 0; i < items; i++)
threads->map[i] = atoi(namelist[i]->d_name);
......@@ -49,7 +58,7 @@ 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 *threads = malloc(sizeof(*threads) + sizeof(pid_t));
struct thread_map *threads = thread_map__alloc(1);
if (threads != NULL) {
threads->map[0] = tid;
......@@ -65,8 +74,8 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
int max_threads = 32, items, i;
char path[256];
struct dirent dirent, *next, **namelist = NULL;
struct thread_map *threads = malloc(sizeof(*threads) +
max_threads * sizeof(pid_t));
struct thread_map *threads = thread_map__alloc(max_threads);
if (threads == NULL)
goto out;
......@@ -185,8 +194,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
goto out_free_threads;
total_tasks += items;
nt = realloc(threads, (sizeof(*threads) +
sizeof(pid_t) * total_tasks));
nt = thread_map__realloc(threads, total_tasks);
if (nt == NULL)
goto out_free_namelist;
......@@ -216,7 +224,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
struct thread_map *thread_map__new_dummy(void)
{
struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
struct thread_map *threads = thread_map__alloc(1);
if (threads != NULL) {
threads->map[0] = -1;
......@@ -253,7 +261,7 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
continue;
ntasks++;
nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
nt = thread_map__realloc(threads, ntasks);
if (nt == NULL)
goto out_free_threads;
......
......@@ -360,7 +360,7 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
unw_word_t base = is_exec ? 0 : map->start;
if (fd >= 0)
dso__data_put_fd(dso);
dso__data_put_fd(map->dso);
memset(&di, 0, sizeof(di));
if (dwarf_find_debug_frame(0, &di, ip, base, map->dso->name,
......
......@@ -9,11 +9,19 @@ struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
if (xy != NULL) {
xy->entry_size = entry_size;
xy->row_size = row_size;
xy->entries = xlen * ylen;
}
return xy;
}
void xyarray__reset(struct xyarray *xy)
{
size_t n = xy->entries * xy->entry_size;
memset(xy->contents, 0, n);
}
void xyarray__delete(struct xyarray *xy)
{
free(xy);
......
......@@ -6,11 +6,13 @@
struct xyarray {
size_t row_size;
size_t entry_size;
size_t entries;
char contents[];
};
struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size);
void xyarray__delete(struct xyarray *xy);
void xyarray__reset(struct xyarray *xy);
static inline void *xyarray__entry(struct xyarray *xy, int x, int y)
{
......
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