mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 07:00:48 +00:00
perf/core improvements and fixes:
User visible: - 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: - Ignore .config-detected in .gitignore (Wang Nan) - Move libtraceevent dynamic list to separated LDFLAGS variable (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVgeMMAAoJENZQFvNTUqpAN6oP/jnkFswb0ul0ZhznxDrHfLwy +mjItjuDpWBS2haRF7IPaBkMu+NwKbTIT45r2P+EcV8PvVbevxjaTmtzMgnbFxNE al80A+y3HtFPbRj7Am5yaYnd5Z/BOVLfvqk41vX+GNBr9esqgrNNFwTbVatnn1fM RF2fbr83VcQJO6CysT4EwxHS2dsFfG3R1R3VsO5aVrsGNjWoBhB+5ljRr4KHYUBN CmEUut+J7vELZvmoJfiJzpfsesRydZegAHJc0MLPADvgGFn/bZbyZ95YfvFJcPq9 Tf/GPp4J2J0iJccNdV2RhBZNCUlDvz4PPdxZsAhaSdA/4Z0uwzcTLPA7GNlQZ0I5 h+tqBJTSM91m+6N2FRzZAEJpEnAdHONljNJeAl3XRR6/W1dh9HxxzWf5ECvu1Lpf az4z3Wf5tnoNwRBP2wxV8vau/r0IgRDclw8TY8bRgdeMNSBxcG0i5G80uSFQRV1m KoHGUA1ykjX/9NVAr7giOJ/B8UR2yK2TPWEzQH781RVKZZlH4mnXgNt0OqmXonuU WKZSLKbGg8wCKRVbmf/Q/pf15jYLQnKKqm39YjTadkW+u4GeW9C89YyTaQeahy1o efIVm4ecS4MOtqaOsedYqMvgf0Gtv1Ud+2hT0qH01jz1IsUCHgtPCkvjAysnOBK8 51AoegTS5VWzX3xa9Hy+ =hhW4 -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
79928928c5
1
tools/perf/.gitignore
vendored
1
tools/perf/.gitignore
vendored
@ -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->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,12 +279,14 @@ static void perf_top__print_sym_table(struct perf_top *top)
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
hists__output_resort(hists, NULL);
|
||||
@ -545,12 +550,14 @@ static void perf_top__sort_new_samples(void *arg)
|
||||
|
||||
hists = evsel__hists(t->sym_evsel);
|
||||
|
||||
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);
|
||||
hists__output_resort(hists, NULL);
|
||||
@ -579,9 +586,22 @@ 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,
|
||||
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);
|
||||
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,12 +671,6 @@ 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);
|
||||
|
||||
if (err)
|
||||
|
@ -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,15 +622,13 @@ 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 (!tevs[i].point.address || tevs[i].point.retprobe)
|
||||
continue;
|
||||
/* 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);
|
||||
if (kprobe_warn_out_range(tevs[i].point.symbol,
|
||||
tevs[i].point.address)) {
|
||||
tmp = NULL;
|
||||
skipped++;
|
||||
} else {
|
||||
@ -600,7 +645,6 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
|
||||
tevs[i].point.offset = tevs[i].point.address -
|
||||
reloc_sym->unrelocated_addr;
|
||||
}
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
@ -920,6 +964,10 @@ out:
|
||||
|
||||
#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 @@ next:
|
||||
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 @@ out_free_threads:
|
||||
|
||||
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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user