perf/core improvements and fixes:

User visible:
 
 - Fix 'perf script' pipe mode segfault, by always initializing ordered_events in
   perf_session__new. (Arnaldo Carvalho de Melo)
 
 - Fix ppid for synthesized fork events (David Ahern)
 
 - Fix kernel symbol resolution of callchains in S/390 by remembering the
   cpumode. (David Hildenbrand)
 
 Infrastructure:
 
 - Disable libbabeltrace check by default in the build system (Jiri Olsa)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJVGwphAAoJEBpxZoYYoA71Go8IAI75DrD3ekKtvcD9PnGOsCyX
 DL004O2i9l7pE9ko6RNX1nD0W2jWTFpPfNcw2F/Xmd3LSBVSAs4l08Y7S7aEyW+3
 IHjfBnuWTpPDXhByvioHqorzfDumvbHSS/fqrCILT3dTpwEMeitu7rn+Dz7gUllV
 wOtEEvlt3u5vGxQa4C3tno1z197HSu0IqN0rLTqkAXM1l0MS9eFQehMGiMuIk2s1
 DUrR/zkHSMPMNP6aAdwMfcg4jf3wXlqLSYZJlEojMLXulnlJexyOSomB6RNnsuNj
 Qsvn/LXsy+8yiEvsOCHBFpVt1feztNb+OxlY/HIEI3EfLhgYQYG5q8/ymDoufEg=
 =D57w
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo' 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:

 - Fix 'perf script' pipe mode segfault, by always initializing ordered_events in
   perf_session__new(). (Arnaldo Carvalho de Melo)

 - Fix ppid for synthesized fork events. (David Ahern)

 - Fix kernel symbol resolution of callchains in S/390 by remembering the
   cpumode. (David Hildenbrand)

Infrastructure changes:

 - Disable libbabeltrace check by default in the build system. (Jiri Olsa)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2015-04-01 09:55:17 +02:00
commit aaa9fa3875
9 changed files with 143 additions and 122 deletions

View File

@ -69,7 +69,7 @@ include config/utilities.mak
#
# Define NO_ZLIB if you do not want to support compressed kernel modules
#
# Define NO_LIBBABELTRACE if you do not want libbabeltrace support
# Define LIBBABELTRACE if you DO want libbabeltrace support
# for CTF data format.
#
# Define NO_LZMA if you do not want to support compressed (xz) kernel modules

View File

@ -95,7 +95,7 @@ ifndef NO_LIBELF
FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw
endif
ifndef NO_LIBBABELTRACE
ifdef LIBBABELTRACE
# for linking with debug library, run like:
# make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/
ifdef LIBBABELTRACE_DIR
@ -598,7 +598,7 @@ else
NO_PERF_READ_VDSOX32 := 1
endif
ifndef NO_LIBBABELTRACE
ifdef LIBBABELTRACE
$(call feature_check,libbabeltrace)
ifeq ($(feature-libbabeltrace), 1)
CFLAGS += -DHAVE_LIBBABELTRACE_SUPPORT $(LIBBABELTRACE_CFLAGS)
@ -607,7 +607,6 @@ ifndef NO_LIBBABELTRACE
$(call detected,CONFIG_LIBBABELTRACE)
else
msg := $(warning No libbabeltrace found, disables 'perf data' CTF format support, please install libbabeltrace-dev[el]/libbabeltrace-ctf-dev);
NO_LIBBABELTRACE := 1
endif
endif

View File

@ -49,70 +49,103 @@ static struct perf_sample synth_sample = {
.period = 1,
};
static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len)
/*
* Assumes that the first 4095 bytes of /proc/pid/stat contains
* the comm, tgid and ppid.
*/
static int perf_event__get_comm_ids(pid_t pid, char *comm, size_t len,
pid_t *tgid, pid_t *ppid)
{
char filename[PATH_MAX];
char bf[BUFSIZ];
FILE *fp;
size_t size = 0;
pid_t tgid = -1;
char bf[4096];
int fd;
size_t size = 0, n;
char *nl, *name, *tgids, *ppids;
*tgid = -1;
*ppid = -1;
snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
fp = fopen(filename, "r");
if (fp == NULL) {
fd = open(filename, O_RDONLY);
if (fd < 0) {
pr_debug("couldn't open %s\n", filename);
return 0;
return -1;
}
while (!comm[0] || (tgid < 0)) {
if (fgets(bf, sizeof(bf), fp) == NULL) {
pr_warning("couldn't get COMM and pgid, malformed %s\n",
filename);
break;
}
n = read(fd, bf, sizeof(bf) - 1);
close(fd);
if (n <= 0) {
pr_warning("Couldn't get COMM, tigd and ppid for pid %d\n",
pid);
return -1;
}
bf[n] = '\0';
if (memcmp(bf, "Name:", 5) == 0) {
char *name = bf + 5;
while (*name && isspace(*name))
++name;
size = strlen(name) - 1;
if (size >= len)
size = len - 1;
memcpy(comm, name, size);
comm[size] = '\0';
name = strstr(bf, "Name:");
tgids = strstr(bf, "Tgid:");
ppids = strstr(bf, "PPid:");
} else if (memcmp(bf, "Tgid:", 5) == 0) {
char *tgids = bf + 5;
while (*tgids && isspace(*tgids))
++tgids;
tgid = atoi(tgids);
}
if (name) {
name += 5; /* strlen("Name:") */
while (*name && isspace(*name))
++name;
nl = strchr(name, '\n');
if (nl)
*nl = '\0';
size = strlen(name);
if (size >= len)
size = len - 1;
memcpy(comm, name, size);
comm[size] = '\0';
} else {
pr_debug("Name: string not found for pid %d\n", pid);
}
fclose(fp);
if (tgids) {
tgids += 5; /* strlen("Tgid:") */
*tgid = atoi(tgids);
} else {
pr_debug("Tgid: string not found for pid %d\n", pid);
}
return tgid;
if (ppids) {
ppids += 5; /* strlen("PPid:") */
*ppid = atoi(ppids);
} else {
pr_debug("PPid: string not found for pid %d\n", pid);
}
return 0;
}
static pid_t perf_event__prepare_comm(union perf_event *event, pid_t pid,
struct machine *machine)
static int perf_event__prepare_comm(union perf_event *event, pid_t pid,
struct machine *machine,
pid_t *tgid, pid_t *ppid)
{
size_t size;
pid_t tgid;
*ppid = -1;
memset(&event->comm, 0, sizeof(event->comm));
if (machine__is_host(machine))
tgid = perf_event__get_comm_tgid(pid, event->comm.comm,
sizeof(event->comm.comm));
else
tgid = machine->pid;
if (machine__is_host(machine)) {
if (perf_event__get_comm_ids(pid, event->comm.comm,
sizeof(event->comm.comm),
tgid, ppid) != 0) {
return -1;
}
} else {
*tgid = machine->pid;
}
if (tgid < 0)
goto out;
if (*tgid < 0)
return -1;
event->comm.pid = tgid;
event->comm.pid = *tgid;
event->comm.header.type = PERF_RECORD_COMM;
size = strlen(event->comm.comm) + 1;
@ -122,8 +155,8 @@ static pid_t perf_event__prepare_comm(union perf_event *event, pid_t pid,
(sizeof(event->comm.comm) - size) +
machine->id_hdr_size);
event->comm.tid = pid;
out:
return tgid;
return 0;
}
static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
@ -131,27 +164,27 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
pid_t tgid = perf_event__prepare_comm(event, pid, machine);
pid_t tgid, ppid;
if (tgid == -1)
goto out;
if (perf_event__prepare_comm(event, pid, machine, &tgid, &ppid) != 0)
return -1;
if (process(tool, event, &synth_sample, machine) != 0)
return -1;
out:
return tgid;
}
static int perf_event__synthesize_fork(struct perf_tool *tool,
union perf_event *event, pid_t pid,
pid_t tgid, perf_event__handler_t process,
union perf_event *event,
pid_t pid, pid_t tgid, pid_t ppid,
perf_event__handler_t process,
struct machine *machine)
{
memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size);
event->fork.ppid = tgid;
event->fork.ptid = tgid;
event->fork.ppid = ppid;
event->fork.ptid = ppid;
event->fork.pid = tgid;
event->fork.tid = pid;
event->fork.header.type = PERF_RECORD_FORK;
@ -343,7 +376,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,
char filename[PATH_MAX];
DIR *tasks;
struct dirent dirent, *next;
pid_t tgid;
pid_t tgid, ppid;
/* special case: only send one comm event using passed in pid */
if (!full) {
@ -378,12 +411,12 @@ static int __event__synthesize_thread(union perf_event *comm_event,
if (*end)
continue;
tgid = perf_event__prepare_comm(comm_event, _pid, machine);
if (tgid == -1)
if (perf_event__prepare_comm(comm_event, _pid, machine,
&tgid, &ppid) != 0)
return -1;
if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid,
process, machine) < 0)
ppid, process, machine) < 0)
return -1;
/*
* Send the prepared comm event

View File

@ -242,7 +242,6 @@ struct events_stats {
u32 nr_invalid_chains;
u32 nr_unknown_id;
u32 nr_unprocessable_samples;
u32 nr_unordered_events;
};
struct attr_event {

View File

@ -1408,29 +1408,27 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
static int add_callchain_ip(struct thread *thread,
struct symbol **parent,
struct addr_location *root_al,
bool branch_history,
u8 *cpumode,
u64 ip)
{
struct addr_location al;
al.filtered = 0;
al.sym = NULL;
if (branch_history)
if (!cpumode) {
thread__find_cpumode_addr_location(thread, MAP__FUNCTION,
ip, &al);
else {
u8 cpumode = PERF_RECORD_MISC_USER;
} else {
if (ip >= PERF_CONTEXT_MAX) {
switch (ip) {
case PERF_CONTEXT_HV:
cpumode = PERF_RECORD_MISC_HYPERVISOR;
*cpumode = PERF_RECORD_MISC_HYPERVISOR;
break;
case PERF_CONTEXT_KERNEL:
cpumode = PERF_RECORD_MISC_KERNEL;
*cpumode = PERF_RECORD_MISC_KERNEL;
break;
case PERF_CONTEXT_USER:
cpumode = PERF_RECORD_MISC_USER;
*cpumode = PERF_RECORD_MISC_USER;
break;
default:
pr_debug("invalid callchain context: "
@ -1444,8 +1442,8 @@ static int add_callchain_ip(struct thread *thread,
}
return 0;
}
thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
ip, &al);
thread__find_addr_location(thread, *cpumode, MAP__FUNCTION,
ip, &al);
}
if (al.sym != NULL) {
@ -1538,6 +1536,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
{
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr);
u8 cpumode = PERF_RECORD_MISC_USER;
int i, j, err;
u64 ip;
@ -1584,7 +1583,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
ip = lbr_stack->entries[0].to;
}
err = add_callchain_ip(thread, parent, root_al, false, ip);
err = add_callchain_ip(thread, parent, root_al, &cpumode, ip);
if (err)
return (err < 0) ? err : 0;
}
@ -1604,6 +1603,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
struct branch_stack *branch = sample->branch_stack;
struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr);
u8 cpumode = PERF_RECORD_MISC_USER;
int i, j, err;
int skip_idx = -1;
int first_call = 0;
@ -1669,10 +1669,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
for (i = 0; i < nr; i++) {
err = add_callchain_ip(thread, parent, root_al,
true, be[i].to);
NULL, be[i].to);
if (!err)
err = add_callchain_ip(thread, parent, root_al,
true, be[i].from);
NULL, be[i].from);
if (err == -EINVAL)
break;
if (err)
@ -1701,7 +1701,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
#endif
ip = chain->ips[j];
err = add_callchain_ip(thread, parent, root_al, false, ip);
err = add_callchain_ip(thread, parent, root_al, &cpumode, ip);
if (err)
return (err < 0) ? err : 0;

View File

@ -2,7 +2,6 @@
#include <linux/compiler.h>
#include <linux/string.h>
#include "ordered-events.h"
#include "evlist.h"
#include "session.h"
#include "asm/bug.h"
#include "debug.h"
@ -167,7 +166,7 @@ int ordered_events__queue(struct ordered_events *oe, union perf_event *event,
pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n",
oe->last_flush_type);
oe->evlist->stats.nr_unordered_events++;
oe->nr_unordered_events++;
}
oevent = ordered_events__new_event(oe, timestamp, event);
@ -187,7 +186,6 @@ static int __ordered_events__flush(struct ordered_events *oe)
{
struct list_head *head = &oe->events;
struct ordered_event *tmp, *iter;
struct perf_sample sample;
u64 limit = oe->next_flush;
u64 last_ts = oe->last ? oe->last->timestamp : 0ULL;
bool show_progress = limit == ULLONG_MAX;
@ -206,15 +204,9 @@ static int __ordered_events__flush(struct ordered_events *oe)
if (iter->timestamp > limit)
break;
ret = perf_evlist__parse_sample(oe->evlist, iter->event, &sample);
ret = oe->deliver(oe, iter);
if (ret)
pr_err("Can't parse sample, err = %d\n", ret);
else {
ret = oe->deliver(oe, iter, &sample);
if (ret)
return ret;
}
return ret;
ordered_events__delete(oe, iter);
oe->last_flush = iter->timestamp;
@ -292,18 +284,13 @@ int ordered_events__flush(struct ordered_events *oe, enum oe_flush how)
return err;
}
void ordered_events__init(struct ordered_events *oe, struct machines *machines,
struct perf_evlist *evlist, struct perf_tool *tool,
ordered_events__deliver_t deliver)
void ordered_events__init(struct ordered_events *oe, ordered_events__deliver_t deliver)
{
INIT_LIST_HEAD(&oe->events);
INIT_LIST_HEAD(&oe->cache);
INIT_LIST_HEAD(&oe->to_free);
oe->max_alloc_size = (u64) -1;
oe->cur_alloc_size = 0;
oe->evlist = evlist;
oe->machines = machines;
oe->tool = tool;
oe->deliver = deliver;
}

View File

@ -3,10 +3,7 @@
#include <linux/types.h>
struct perf_tool;
struct perf_evlist;
struct perf_sample;
struct machines;
struct ordered_event {
u64 timestamp;
@ -25,8 +22,7 @@ enum oe_flush {
struct ordered_events;
typedef int (*ordered_events__deliver_t)(struct ordered_events *oe,
struct ordered_event *event,
struct perf_sample *sample);
struct ordered_event *event);
struct ordered_events {
u64 last_flush;
@ -39,13 +35,11 @@ struct ordered_events {
struct list_head to_free;
struct ordered_event *buffer;
struct ordered_event *last;
struct machines *machines;
struct perf_evlist *evlist;
struct perf_tool *tool;
ordered_events__deliver_t deliver;
int buffer_idx;
unsigned int nr_events;
enum oe_flush last_flush_type;
u32 nr_unordered_events;
bool copy_on_queue;
};
@ -53,9 +47,7 @@ int ordered_events__queue(struct ordered_events *oe, union perf_event *event,
struct perf_sample *sample, u64 file_offset);
void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event);
int ordered_events__flush(struct ordered_events *oe, enum oe_flush how);
void ordered_events__init(struct ordered_events *oe, struct machines *machines,
struct perf_evlist *evlsit, struct perf_tool *tool,
ordered_events__deliver_t deliver);
void ordered_events__init(struct ordered_events *oe, ordered_events__deliver_t deliver);
void ordered_events__free(struct ordered_events *oe);
static inline

View File

@ -93,11 +93,20 @@ static void perf_session__set_comm_exec(struct perf_session *session)
}
static int ordered_events__deliver_event(struct ordered_events *oe,
struct ordered_event *event,
struct perf_sample *sample)
struct ordered_event *event)
{
return machines__deliver_event(oe->machines, oe->evlist, event->event,
sample, oe->tool, event->file_offset);
struct perf_sample sample;
struct perf_session *session = container_of(oe, struct perf_session,
ordered_events);
int ret = perf_evlist__parse_sample(session->evlist, event->event, &sample);
if (ret) {
pr_err("Can't parse sample, err = %d\n", ret);
return ret;
}
return machines__deliver_event(&session->machines, session->evlist, event->event,
&sample, session->tool, event->file_offset);
}
struct perf_session *perf_session__new(struct perf_data_file *file,
@ -109,7 +118,9 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
goto out;
session->repipe = repipe;
session->tool = tool;
machines__init(&session->machines);
ordered_events__init(&session->ordered_events, ordered_events__deliver_event);
if (file) {
if (perf_data_file__open(file))
@ -139,9 +150,6 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
tool->ordered_events = false;
} else {
ordered_events__init(&session->ordered_events, &session->machines,
session->evlist, tool, ordered_events__deliver_event);
}
return session;
@ -941,7 +949,7 @@ static s64 perf_session__process_user_event(struct perf_session *session,
u64 file_offset)
{
struct ordered_events *oe = &session->ordered_events;
struct perf_tool *tool = oe->tool;
struct perf_tool *tool = session->tool;
int fd = perf_data_file__fd(session->file);
int err;
@ -982,7 +990,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,
struct perf_sample *sample)
{
struct perf_evlist *evlist = session->evlist;
struct perf_tool *tool = session->ordered_events.tool;
struct perf_tool *tool = session->tool;
events_stats__inc(&evlist->stats, event->header.type);
@ -1060,7 +1068,7 @@ static s64 perf_session__process_event(struct perf_session *session,
union perf_event *event, u64 file_offset)
{
struct perf_evlist *evlist = session->evlist;
struct perf_tool *tool = session->ordered_events.tool;
struct perf_tool *tool = session->tool;
struct perf_sample sample;
int ret;
@ -1117,10 +1125,12 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
return thread;
}
static void perf_tool__warn_about_errors(const struct perf_tool *tool,
const struct events_stats *stats)
static void perf_session__warn_about_errors(const struct perf_session *session)
{
if (tool->lost == perf_event__process_lost &&
const struct events_stats *stats = &session->evlist->stats;
const struct ordered_events *oe = &session->ordered_events;
if (session->tool->lost == perf_event__process_lost &&
stats->nr_events[PERF_RECORD_LOST] != 0) {
ui__warning("Processed %d events and lost %d chunks!\n\n"
"Check IO/CPU overload!\n\n",
@ -1156,8 +1166,8 @@ static void perf_tool__warn_about_errors(const struct perf_tool *tool,
stats->nr_unprocessable_samples);
}
if (stats->nr_unordered_events != 0)
ui__warning("%u out of order events recorded.\n", stats->nr_unordered_events);
if (oe->nr_unordered_events != 0)
ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events);
}
volatile int session_done;
@ -1165,7 +1175,7 @@ volatile int session_done;
static int __perf_session__process_pipe_events(struct perf_session *session)
{
struct ordered_events *oe = &session->ordered_events;
struct perf_tool *tool = oe->tool;
struct perf_tool *tool = session->tool;
int fd = perf_data_file__fd(session->file);
union perf_event *event;
uint32_t size, cur_size = 0;
@ -1248,7 +1258,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session)
err = ordered_events__flush(oe, OE_FLUSH__FINAL);
out_err:
free(buf);
perf_tool__warn_about_errors(tool, &session->evlist->stats);
perf_session__warn_about_errors(session);
ordered_events__free(&session->ordered_events);
return err;
}
@ -1298,7 +1308,7 @@ static int __perf_session__process_events(struct perf_session *session,
u64 file_size)
{
struct ordered_events *oe = &session->ordered_events;
struct perf_tool *tool = oe->tool;
struct perf_tool *tool = session->tool;
int fd = perf_data_file__fd(session->file);
u64 head, page_offset, file_offset, file_pos, size;
int err, mmap_prot, mmap_flags, map_idx = 0;
@ -1394,7 +1404,7 @@ static int __perf_session__process_events(struct perf_session *session,
err = ordered_events__flush(oe, OE_FLUSH__FINAL);
out_err:
ui_progress__finish();
perf_tool__warn_about_errors(tool, &session->evlist->stats);
perf_session__warn_about_errors(session);
ordered_events__free(&session->ordered_events);
session->one_mmap = false;
return err;

View File

@ -26,6 +26,7 @@ struct perf_session {
u64 one_mmap_offset;
struct ordered_events ordered_events;
struct perf_data_file *file;
struct perf_tool *tool;
};
#define PRINT_IP_OPT_IP (1<<0)