Merge branch 'perf-tools-for-linus' of git://github.com/acmel/linux

* 'perf-tools-for-linus' of git://github.com/acmel/linux:
  perf tools: Add support for disabling -Werror via WERROR=0
  perf top: Fix userspace sample addr map offset
  perf symbols: Fix issue with binaries using 16-bytes buildids (v2)
  perf tool: Fix endianness handling of u32 data in samples
  perf sort: Fix symbol sort output by separating unresolved samples by type
  perf symbols: Synthesize anonymous mmap events
  perf record: Create events initially disabled and enable after init
  perf symbols: Add some heuristics for choosing the best duplicate symbol
  perf symbols: Preserve symbol scope when parsing /proc/kallsyms
  perf symbols: /proc/kallsyms does not sort module symbols
  perf symbols: Fix ppc64 SEGV in dso__load_sym with debuginfo files
  perf probe: Fix regression of variable finder
This commit is contained in:
Linus Torvalds 2011-09-23 13:59:37 -07:00
commit eab8bcb67a
13 changed files with 209 additions and 57 deletions

View file

@ -30,6 +30,8 @@ endif
# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
#
# Define NO_DWARF if you do not want debug-info analysis feature at all.
#
# Define WERROR=0 to disable treating any warnings as errors.
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@ -63,6 +65,11 @@ ifeq ($(ARCH),x86_64)
endif
endif
# Treat warnings as errors unless directed not to
ifneq ($(WERROR),0)
CFLAGS_WERROR := -Werror
endif
#
# Include saner warnings here, which can catch bugs:
#
@ -95,7 +102,7 @@ ifndef PERF_DEBUG
CFLAGS_OPTIMIZE = -O6
endif
CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
EXTLIBS = -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ALL_LDFLAGS = $(LDFLAGS)

View file

@ -161,6 +161,7 @@ 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->disabled = 1;
attr->inherit = !no_inherit;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
@ -671,6 +672,8 @@ static int __cmd_record(int argc, const char **argv)
}
}
perf_evlist__enable(evsel_list);
/*
* Let the child rip
*/

View file

@ -561,7 +561,7 @@ static int test__basic_mmap(void)
}
err = perf_event__parse_sample(event, attr.sample_type, sample_size,
false, &sample);
false, &sample, false);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;

View file

@ -191,7 +191,8 @@ static void __zero_source_counters(struct sym_entry *syme)
symbol__annotate_zero_histograms(sym);
}
static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
static void record_precise_ip(struct sym_entry *syme, struct map *map,
int counter, u64 ip)
{
struct annotation *notes;
struct symbol *sym;
@ -205,8 +206,8 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
if (pthread_mutex_trylock(&notes->lock))
return;
ip = syme->map->map_ip(syme->map, ip);
symbol__inc_addr_samples(sym, syme->map, counter, ip);
ip = map->map_ip(map, ip);
symbol__inc_addr_samples(sym, map, counter, ip);
pthread_mutex_unlock(&notes->lock);
}
@ -810,7 +811,7 @@ static void perf_event__process_sample(const union perf_event *event,
evsel = perf_evlist__id2evsel(top.evlist, sample->id);
assert(evsel != NULL);
syme->count[evsel->idx]++;
record_precise_ip(syme, evsel->idx, ip);
record_precise_ip(syme, al.map, evsel->idx, ip);
pthread_mutex_lock(&top.active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next) {
static bool first = true;

View file

@ -169,12 +169,17 @@ static int perf_event__synthesize_mmap_events(union perf_event *event,
continue;
pbf += n + 3;
if (*pbf == 'x') { /* vm_exec */
char anonstr[] = "//anon\n";
char *execname = strchr(bf, '/');
/* Catch VDSO */
if (execname == NULL)
execname = strstr(bf, "[vdso]");
/* Catch anonymous mmaps */
if ((execname == NULL) && !strstr(bf, "["))
execname = anonstr;
if (execname == NULL)
continue;

View file

@ -186,6 +186,6 @@ const char *perf_event__name(unsigned int id);
int perf_event__parse_sample(const union perf_event *event, u64 type,
int sample_size, bool sample_id_all,
struct perf_sample *sample);
struct perf_sample *sample, bool swapped);
#endif /* __PERF_RECORD_H */

View file

@ -113,6 +113,19 @@ void perf_evlist__disable(struct perf_evlist *evlist)
}
}
void perf_evlist__enable(struct perf_evlist *evlist)
{
int cpu, thread;
struct perf_evsel *pos;
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
list_for_each_entry(pos, &evlist->entries, node) {
for (thread = 0; thread < evlist->threads->nr; thread++)
ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE);
}
}
}
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
{
int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries;

View file

@ -54,6 +54,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
void perf_evlist__munmap(struct perf_evlist *evlist);
void perf_evlist__disable(struct perf_evlist *evlist);
void perf_evlist__enable(struct perf_evlist *evlist);
static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
struct cpu_map *cpus,

View file

@ -7,6 +7,8 @@
* Released under the GPL v2. (and only v2, not any later version)
*/
#include <byteswap.h>
#include "asm/bug.h"
#include "evsel.h"
#include "evlist.h"
#include "util.h"
@ -342,10 +344,20 @@ static bool sample_overlap(const union perf_event *event,
int perf_event__parse_sample(const union perf_event *event, u64 type,
int sample_size, bool sample_id_all,
struct perf_sample *data)
struct perf_sample *data, bool swapped)
{
const u64 *array;
/*
* used for cross-endian analysis. See git commit 65014ab3
* for why this goofiness is needed.
*/
union {
u64 val64;
u32 val32[2];
} u;
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
@ -366,9 +378,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
data->pid = p[0];
data->tid = p[1];
u.val64 = *array;
if (swapped) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
}
data->pid = u.val32[0];
data->tid = u.val32[1];
array++;
}
@ -395,8 +414,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
data->cpu = *p;
u.val64 = *array;
if (swapped) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
}
data->cpu = u.val32[0];
array++;
}
@ -423,18 +449,24 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}
if (type & PERF_SAMPLE_RAW) {
u32 *p = (u32 *)array;
u.val64 = *array;
if (WARN_ONCE(swapped,
"Endianness of raw data not corrected!\n")) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
}
if (sample_overlap(event, array, sizeof(u32)))
return -EFAULT;
data->raw_size = *p;
p++;
data->raw_size = u.val32[0];
if (sample_overlap(event, p, data->raw_size))
if (sample_overlap(event, &u.val32[1], data->raw_size))
return -EFAULT;
data->raw_data = p;
data->raw_data = &u.val32[1];
}
return 0;

View file

@ -659,7 +659,7 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die))
ret = -ENOENT;
}
if (ret == 0)
if (ret >= 0)
ret = convert_variable(&vr_die, pf);
if (ret < 0)

View file

@ -162,7 +162,8 @@ static inline int perf_session__parse_sample(struct perf_session *session,
{
return perf_event__parse_sample(event, session->sample_type,
session->sample_size,
session->sample_id_all, sample);
session->sample_id_all, sample,
session->header.needs_swap);
}
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,

View file

@ -151,11 +151,17 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
u64 ip_l, ip_r;
if (!left->ms.sym && !right->ms.sym)
return right->level - left->level;
if (!left->ms.sym || !right->ms.sym)
return cmp_null(left->ms.sym, right->ms.sym);
if (left->ms.sym == right->ms.sym)
return 0;
ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
ip_l = left->ms.sym->start;
ip_r = right->ms.sym->start;
return (int64_t)(ip_r - ip_l);
}

View file

@ -74,16 +74,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type)
bool symbol_type__is_a(char symbol_type, enum map_type map_type)
{
symbol_type = toupper(symbol_type);
switch (map_type) {
case MAP__FUNCTION:
return symbol_type == 'T' || symbol_type == 'W';
case MAP__VARIABLE:
return symbol_type == 'D' || symbol_type == 'd';
return symbol_type == 'D';
default:
return false;
}
}
static int prefix_underscores_count(const char *str)
{
const char *tail = str;
while (*tail == '_')
tail++;
return tail - str;
}
#define SYMBOL_A 0
#define SYMBOL_B 1
static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
{
s64 a;
s64 b;
/* Prefer a symbol with non zero length */
a = syma->end - syma->start;
b = symb->end - symb->start;
if ((b == 0) && (a > 0))
return SYMBOL_A;
else if ((a == 0) && (b > 0))
return SYMBOL_B;
/* Prefer a non weak symbol over a weak one */
a = syma->binding == STB_WEAK;
b = symb->binding == STB_WEAK;
if (b && !a)
return SYMBOL_A;
if (a && !b)
return SYMBOL_B;
/* Prefer a global symbol over a non global one */
a = syma->binding == STB_GLOBAL;
b = symb->binding == STB_GLOBAL;
if (a && !b)
return SYMBOL_A;
if (b && !a)
return SYMBOL_B;
/* Prefer a symbol with less underscores */
a = prefix_underscores_count(syma->name);
b = prefix_underscores_count(symb->name);
if (b > a)
return SYMBOL_A;
else if (a > b)
return SYMBOL_B;
/* If all else fails, choose the symbol with the longest name */
if (strlen(syma->name) >= strlen(symb->name))
return SYMBOL_A;
else
return SYMBOL_B;
}
static void symbols__fixup_duplicate(struct rb_root *symbols)
{
struct rb_node *nd;
struct symbol *curr, *next;
nd = rb_first(symbols);
while (nd) {
curr = rb_entry(nd, struct symbol, rb_node);
again:
nd = rb_next(&curr->rb_node);
next = rb_entry(nd, struct symbol, rb_node);
if (!nd)
break;
if (curr->start != next->start)
continue;
if (choose_best_symbol(curr, next) == SYMBOL_A) {
rb_erase(&next->rb_node, symbols);
goto again;
} else {
nd = rb_next(&curr->rb_node);
rb_erase(&curr->rb_node, symbols);
}
}
}
static void symbols__fixup_end(struct rb_root *symbols)
{
struct rb_node *nd, *prevnd = rb_first(symbols);
@ -438,18 +526,11 @@ int kallsyms__parse(const char *filename, void *arg,
char *line = NULL;
size_t n;
int err = -1;
u64 prev_start = 0;
char prev_symbol_type = 0;
char *prev_symbol_name;
FILE *file = fopen(filename, "r");
if (file == NULL)
goto out_failure;
prev_symbol_name = malloc(KSYM_NAME_LEN);
if (prev_symbol_name == NULL)
goto out_close;
err = 0;
while (!feof(file)) {
@ -470,7 +551,7 @@ int kallsyms__parse(const char *filename, void *arg,
if (len + 2 >= line_len)
continue;
symbol_type = toupper(line[len]);
symbol_type = line[len];
len += 2;
symbol_name = line + len;
len = line_len - len;
@ -480,24 +561,18 @@ int kallsyms__parse(const char *filename, void *arg,
break;
}
if (prev_symbol_type) {
u64 end = start;
if (end != prev_start)
--end;
err = process_symbol(arg, prev_symbol_name,
prev_symbol_type, prev_start, end);
if (err)
break;
}
memcpy(prev_symbol_name, symbol_name, len + 1);
prev_symbol_type = symbol_type;
prev_start = start;
/*
* module symbols are not sorted so we add all
* symbols with zero length and rely on
* symbols__fixup_end() to fix it up.
*/
err = process_symbol(arg, symbol_name,
symbol_type, start, start);
if (err)
break;
}
free(prev_symbol_name);
free(line);
out_close:
fclose(file);
return err;
@ -703,6 +778,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
if (dso__load_all_kallsyms(dso, filename, map) < 0)
return -1;
symbols__fixup_duplicate(&dso->symbols[map->type]);
symbols__fixup_end(&dso->symbols[map->type]);
if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
dso->symtab_type = SYMTAB__GUEST_KALLSYMS;
else
@ -1092,8 +1170,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
if (dso->has_build_id) {
u8 build_id[BUILD_ID_SIZE];
if (elf_read_build_id(elf, build_id,
BUILD_ID_SIZE) != BUILD_ID_SIZE)
if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
goto out_elf_end;
if (!dso__build_id_equal(dso, build_id))
@ -1111,6 +1188,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
}
opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
if (opdshdr.sh_type != SHT_PROGBITS)
opdsec = NULL;
if (opdsec)
opddata = elf_rawdata(opdsec, NULL);
@ -1276,6 +1355,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
* For misannotated, zeroed, ASM function sizes.
*/
if (nr > 0) {
symbols__fixup_duplicate(&dso->symbols[map->type]);
symbols__fixup_end(&dso->symbols[map->type]);
if (kmap) {
/*
@ -1362,8 +1442,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)
ptr = data->d_buf;
while (ptr < (data->d_buf + data->d_size)) {
GElf_Nhdr *nhdr = ptr;
int namesz = NOTE_ALIGN(nhdr->n_namesz),
descsz = NOTE_ALIGN(nhdr->n_descsz);
size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
descsz = NOTE_ALIGN(nhdr->n_descsz);
const char *name;
ptr += sizeof(*nhdr);
@ -1372,8 +1452,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)
if (nhdr->n_type == NT_GNU_BUILD_ID &&
nhdr->n_namesz == sizeof("GNU")) {
if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
memcpy(bf, ptr, BUILD_ID_SIZE);
err = BUILD_ID_SIZE;
size_t sz = min(size, descsz);
memcpy(bf, ptr, sz);
memset(bf + sz, 0, size - sz);
err = descsz;
break;
}
}
@ -1425,7 +1507,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
while (1) {
char bf[BUFSIZ];
GElf_Nhdr nhdr;
int namesz, descsz;
size_t namesz, descsz;
if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
break;
@ -1434,15 +1516,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
descsz = NOTE_ALIGN(nhdr.n_descsz);
if (nhdr.n_type == NT_GNU_BUILD_ID &&
nhdr.n_namesz == sizeof("GNU")) {
if (read(fd, bf, namesz) != namesz)
if (read(fd, bf, namesz) != (ssize_t)namesz)
break;
if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
if (read(fd, build_id,
BUILD_ID_SIZE) == BUILD_ID_SIZE) {
size_t sz = min(descsz, size);
if (read(fd, build_id, sz) == (ssize_t)sz) {
memset(build_id + sz, 0, size - sz);
err = 0;
break;
}
} else if (read(fd, bf, descsz) != descsz)
} else if (read(fd, bf, descsz) != (ssize_t)descsz)
break;
} else {
int n = namesz + descsz;