Commit ffa86c2f authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo-4.12-20170314' of...

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

New features:

- Add PERF_RECORD_NAMESPACES so that the kernel can record information
  required to associate samples to namespaces, helping in container
  problem characterization.

  Now the 'perf record has a --namespace' option to ask for such info,
  and when present, it can be used, initially, via a new sort order,
  'cgroup_id', allowing histogram entry bucketization by a (device, inode)
  based cgroup identifier (Hari Bathini)

- Add --next option to 'perf sched timehist', showing what is the next
  thread to run (Brendan Gregg)

Fixes:

- Fix segfault with basic block 'cycles' sort dimension (Changbin Du)

- Add c2c to command-list.txt, making it appear in the 'perf help'
  output (Changbin Du)

- Fix zeroing of 'abs_path' variable in the perf hists browser switch
  file code (Changbin Du)

- Hide tips messages when -q/--quiet is given to 'perf report' (Namhyung Kim)

Infrastructure changes:

- Use ref_reloc_sym + offset to setup kretprobes (Naveen Rao)

- Ignore generated files pmu-events/{jevents,pmu-events.c} for git (Changbin Du)

Documentation changes:

- Document +field style argument support for --field option (Changbin Du)

- Clarify 'perf c2c --stats' help message (Namhyung Kim)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 84e5b549 5f6bee34
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* Copyright (C) 2017 Hari Bathini, IBM Corporation
*/
#ifndef __PERF_NAMESPACES_H
#define __PERF_NAMESPACES_H
#include "../perf.h"
#include <linux/list.h>
struct namespaces_event;
struct namespaces {
struct list_head list;
u64 end_time;
struct perf_ns_link_info link_info[];
};
struct namespaces *namespaces__new(struct namespaces_event *event);
void namespaces__free(struct namespaces *namespaces);
#endif /* __PERF_NAMESPACES_H */
...@@ -757,7 +757,9 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs, ...@@ -757,7 +757,9 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs,
} }
for (i = 0; i < ntevs; i++) { for (i = 0; i < ntevs; i++) {
if (!tevs[i].point.address || tevs[i].point.retprobe) if (!tevs[i].point.address)
continue;
if (tevs[i].point.retprobe && !kretprobe_offset_is_supported())
continue; continue;
/* If we found a wrong one, mark it by NULL symbol */ /* If we found a wrong one, mark it by NULL symbol */
if (kprobe_warn_out_range(tevs[i].point.symbol, if (kprobe_warn_out_range(tevs[i].point.symbol,
...@@ -1528,11 +1530,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) ...@@ -1528,11 +1530,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
return -EINVAL; return -EINVAL;
} }
if (pp->retprobe && !pp->function) {
semantic_error("Return probe requires an entry function.\n");
return -EINVAL;
}
if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
semantic_error("Offset/Line/Lazy pattern can't be used with " semantic_error("Offset/Line/Lazy pattern can't be used with "
"return probe.\n"); "return probe.\n");
...@@ -2841,7 +2838,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2841,7 +2838,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
} }
/* Note that the symbols in the kmodule are not relocated */ /* Note that the symbols in the kmodule are not relocated */
if (!pev->uprobes && !pp->retprobe && !pev->target) { if (!pev->uprobes && !pev->target &&
(!pp->retprobe || kretprobe_offset_is_supported())) {
reloc_sym = kernel_get_ref_reloc_sym(); reloc_sym = kernel_get_ref_reloc_sym();
if (!reloc_sym) { if (!reloc_sym) {
pr_warning("Relocated base symbol is not found!\n"); pr_warning("Relocated base symbol is not found!\n");
......
...@@ -877,35 +877,33 @@ int probe_cache__show_all_caches(struct strfilter *filter) ...@@ -877,35 +877,33 @@ int probe_cache__show_all_caches(struct strfilter *filter)
return 0; return 0;
} }
enum ftrace_readme {
FTRACE_README_PROBE_TYPE_X = 0,
FTRACE_README_KRETPROBE_OFFSET,
FTRACE_README_END,
};
static struct { static struct {
const char *pattern; const char *pattern;
bool avail; bool avail;
bool checked; } ftrace_readme_table[] = {
} probe_type_table[] = { #define DEFINE_TYPE(idx, pat) \
#define DEFINE_TYPE(idx, pat, def_avail) \ [idx] = {.pattern = pat, .avail = false}
[idx] = {.pattern = pat, .avail = (def_avail)} DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"),
DEFINE_TYPE(PROBE_TYPE_U, "* u8/16/32/64,*", true), DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"),
DEFINE_TYPE(PROBE_TYPE_S, "* s8/16/32/64,*", true),
DEFINE_TYPE(PROBE_TYPE_X, "* x8/16/32/64,*", false),
DEFINE_TYPE(PROBE_TYPE_STRING, "* string,*", true),
DEFINE_TYPE(PROBE_TYPE_BITFIELD,
"* b<bit-width>@<bit-offset>/<container-size>", true),
}; };
bool probe_type_is_available(enum probe_type type) static bool scan_ftrace_readme(enum ftrace_readme type)
{ {
int fd;
FILE *fp; FILE *fp;
char *buf = NULL; char *buf = NULL;
size_t len = 0; size_t len = 0;
bool target_line = false; bool ret = false;
bool ret = probe_type_table[type].avail; static bool scanned = false;
int fd;
if (type >= PROBE_TYPE_END) if (scanned)
return false; goto result;
/* We don't have to check the type which supported by default */
if (ret || probe_type_table[type].checked)
return ret;
fd = open_trace_file("README", false); fd = open_trace_file("README", false);
if (fd < 0) if (fd < 0)
...@@ -917,21 +915,34 @@ bool probe_type_is_available(enum probe_type type) ...@@ -917,21 +915,34 @@ bool probe_type_is_available(enum probe_type type)
return ret; return ret;
} }
while (getline(&buf, &len, fp) > 0 && !ret) { while (getline(&buf, &len, fp) > 0)
if (!target_line) { for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++)
target_line = !!strstr(buf, " type: "); if (!ftrace_readme_table[i].avail)
if (!target_line) ftrace_readme_table[i].avail =
continue; strglobmatch(buf, ftrace_readme_table[i].pattern);
} else if (strstr(buf, "\t ") != buf) scanned = true;
break;
ret = strglobmatch(buf, probe_type_table[type].pattern);
}
/* Cache the result */
probe_type_table[type].checked = true;
probe_type_table[type].avail = ret;
fclose(fp); fclose(fp);
free(buf); free(buf);
return ret; result:
if (type >= FTRACE_README_END)
return false;
return ftrace_readme_table[type].avail;
}
bool probe_type_is_available(enum probe_type type)
{
if (type >= PROBE_TYPE_END)
return false;
else if (type == PROBE_TYPE_X)
return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X);
return true;
}
bool kretprobe_offset_is_supported(void)
{
return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET);
} }
...@@ -65,6 +65,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache, ...@@ -65,6 +65,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
const char *group, const char *event); const char *group, const char *event);
int probe_cache__show_all_caches(struct strfilter *filter); int probe_cache__show_all_caches(struct strfilter *filter);
bool probe_type_is_available(enum probe_type type); bool probe_type_is_available(enum probe_type type);
bool kretprobe_offset_is_supported(void);
#else /* ! HAVE_LIBELF_SUPPORT */ #else /* ! HAVE_LIBELF_SUPPORT */
static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused) static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
{ {
......
...@@ -1239,6 +1239,8 @@ static int machines__deliver_event(struct machines *machines, ...@@ -1239,6 +1239,8 @@ static int machines__deliver_event(struct machines *machines,
return tool->mmap2(tool, event, sample, machine); return tool->mmap2(tool, event, sample, machine);
case PERF_RECORD_COMM: case PERF_RECORD_COMM:
return tool->comm(tool, event, sample, machine); return tool->comm(tool, event, sample, machine);
case PERF_RECORD_NAMESPACES:
return tool->namespaces(tool, event, sample, machine);
case PERF_RECORD_FORK: case PERF_RECORD_FORK:
return tool->fork(tool, event, sample, machine); return tool->fork(tool, event, sample, machine);
case PERF_RECORD_EXIT: case PERF_RECORD_EXIT:
...@@ -1494,6 +1496,11 @@ int perf_session__register_idle_thread(struct perf_session *session) ...@@ -1494,6 +1496,11 @@ int perf_session__register_idle_thread(struct perf_session *session)
err = -1; err = -1;
} }
if (thread == NULL || thread__set_namespaces(thread, 0, NULL)) {
pr_err("problem inserting idle task.\n");
err = -1;
}
/* machine__findnew_thread() got the thread, so put it */ /* machine__findnew_thread() got the thread, so put it */
thread__put(thread); thread__put(thread);
return err; return err;
......
...@@ -536,6 +536,46 @@ struct sort_entry sort_cpu = { ...@@ -536,6 +536,46 @@ struct sort_entry sort_cpu = {
.se_width_idx = HISTC_CPU, .se_width_idx = HISTC_CPU,
}; };
/* --sort cgroup_id */
static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev)
{
return (int64_t)(right_dev - left_dev);
}
static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino)
{
return (int64_t)(right_ino - left_ino);
}
static int64_t
sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right)
{
int64_t ret;
ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev);
if (ret != 0)
return ret;
return _sort__cgroup_inode_cmp(right->cgroup_id.ino,
left->cgroup_id.ino);
}
static int hist_entry__cgroup_id_snprintf(struct hist_entry *he,
char *bf, size_t size,
unsigned int width __maybe_unused)
{
return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev,
he->cgroup_id.ino);
}
struct sort_entry sort_cgroup_id = {
.se_header = "cgroup id (dev/inode)",
.se_cmp = sort__cgroup_id_cmp,
.se_snprintf = hist_entry__cgroup_id_snprintf,
.se_width_idx = HISTC_CGROUP_ID,
};
/* --sort socket */ /* --sort socket */
static int64_t static int64_t
...@@ -846,6 +886,9 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, ...@@ -846,6 +886,9 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
static int64_t static int64_t
sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
if (!left->branch_info || !right->branch_info)
return cmp_null(left->branch_info, right->branch_info);
return left->branch_info->flags.cycles - return left->branch_info->flags.cycles -
right->branch_info->flags.cycles; right->branch_info->flags.cycles;
} }
...@@ -853,6 +896,8 @@ sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -853,6 +896,8 @@ sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width) size_t size, unsigned int width)
{ {
if (!he->branch_info)
return scnprintf(bf, size, "%-.*s", width, "N/A");
if (he->branch_info->flags.cycles == 0) if (he->branch_info->flags.cycles == 0)
return repsep_snprintf(bf, size, "%-*s", width, "-"); return repsep_snprintf(bf, size, "%-*s", width, "-");
return repsep_snprintf(bf, size, "%-*hd", width, return repsep_snprintf(bf, size, "%-*hd", width,
...@@ -1459,6 +1504,7 @@ static struct sort_dimension common_sort_dimensions[] = { ...@@ -1459,6 +1504,7 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_TRANSACTION, "transaction", sort_transaction), DIM(SORT_TRANSACTION, "transaction", sort_transaction),
DIM(SORT_TRACE, "trace", sort_trace), DIM(SORT_TRACE, "trace", sort_trace),
DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size), DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size),
DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id),
}; };
#undef DIM #undef DIM
......
...@@ -54,6 +54,11 @@ struct he_stat { ...@@ -54,6 +54,11 @@ struct he_stat {
u32 nr_events; u32 nr_events;
}; };
struct namespace_id {
u64 dev;
u64 ino;
};
struct hist_entry_diff { struct hist_entry_diff {
bool computed; bool computed;
union { union {
...@@ -91,6 +96,7 @@ struct hist_entry { ...@@ -91,6 +96,7 @@ struct hist_entry {
struct map_symbol ms; struct map_symbol ms;
struct thread *thread; struct thread *thread;
struct comm *comm; struct comm *comm;
struct namespace_id cgroup_id;
u64 ip; u64 ip;
u64 transaction; u64 transaction;
s32 socket; s32 socket;
...@@ -212,6 +218,7 @@ enum sort_type { ...@@ -212,6 +218,7 @@ enum sort_type {
SORT_TRANSACTION, SORT_TRANSACTION,
SORT_TRACE, SORT_TRACE,
SORT_SYM_SIZE, SORT_SYM_SIZE,
SORT_CGROUP_ID,
/* branch stack specific sort keys */ /* branch stack specific sort keys */
__SORT_BRANCH_STACK, __SORT_BRANCH_STACK,
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "thread-stack.h" #include "thread-stack.h"
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "namespaces.h"
#include "comm.h" #include "comm.h"
#include "unwind.h" #include "unwind.h"
...@@ -40,6 +41,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) ...@@ -40,6 +41,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->tid = tid; thread->tid = tid;
thread->ppid = -1; thread->ppid = -1;
thread->cpu = -1; thread->cpu = -1;
INIT_LIST_HEAD(&thread->namespaces_list);
INIT_LIST_HEAD(&thread->comm_list); INIT_LIST_HEAD(&thread->comm_list);
comm_str = malloc(32); comm_str = malloc(32);
...@@ -66,7 +68,8 @@ struct thread *thread__new(pid_t pid, pid_t tid) ...@@ -66,7 +68,8 @@ struct thread *thread__new(pid_t pid, pid_t tid)
void thread__delete(struct thread *thread) void thread__delete(struct thread *thread)
{ {
struct comm *comm, *tmp; struct namespaces *namespaces, *tmp_namespaces;
struct comm *comm, *tmp_comm;
BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); BUG_ON(!RB_EMPTY_NODE(&thread->rb_node));
...@@ -76,7 +79,12 @@ void thread__delete(struct thread *thread) ...@@ -76,7 +79,12 @@ void thread__delete(struct thread *thread)
map_groups__put(thread->mg); map_groups__put(thread->mg);
thread->mg = NULL; thread->mg = NULL;
} }
list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { list_for_each_entry_safe(namespaces, tmp_namespaces,
&thread->namespaces_list, list) {
list_del(&namespaces->list);
namespaces__free(namespaces);
}
list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) {
list_del(&comm->list); list_del(&comm->list);
comm__free(comm); comm__free(comm);
} }
...@@ -104,6 +112,38 @@ void thread__put(struct thread *thread) ...@@ -104,6 +112,38 @@ void thread__put(struct thread *thread)
} }
} }
struct namespaces *thread__namespaces(const struct thread *thread)
{
if (list_empty(&thread->namespaces_list))
return NULL;
return list_first_entry(&thread->namespaces_list, struct namespaces, list);
}
int thread__set_namespaces(struct thread *thread, u64 timestamp,
struct namespaces_event *event)
{
struct namespaces *new, *curr = thread__namespaces(thread);
new = namespaces__new(event);
if (!new)
return -ENOMEM;
list_add(&new->list, &thread->namespaces_list);
if (timestamp && curr) {
/*
* setns syscall must have changed few or all the namespaces
* of this thread. Update end time for the namespaces
* previously used.
*/
curr = list_next_entry(new, list);
curr->end_time = timestamp;
}
return 0;
}
struct comm *thread__comm(const struct thread *thread) struct comm *thread__comm(const struct thread *thread)
{ {
if (list_empty(&thread->comm_list)) if (list_empty(&thread->comm_list))
......
...@@ -28,6 +28,7 @@ struct thread { ...@@ -28,6 +28,7 @@ struct thread {
bool comm_set; bool comm_set;
int comm_len; int comm_len;
bool dead; /* if set thread has exited */ bool dead; /* if set thread has exited */
struct list_head namespaces_list;
struct list_head comm_list; struct list_head comm_list;
u64 db_id; u64 db_id;
...@@ -40,6 +41,7 @@ struct thread { ...@@ -40,6 +41,7 @@ struct thread {
}; };
struct machine; struct machine;
struct namespaces;
struct comm; struct comm;
struct thread *thread__new(pid_t pid, pid_t tid); struct thread *thread__new(pid_t pid, pid_t tid);
...@@ -62,6 +64,10 @@ static inline void thread__exited(struct thread *thread) ...@@ -62,6 +64,10 @@ static inline void thread__exited(struct thread *thread)
thread->dead = true; thread->dead = true;
} }
struct namespaces *thread__namespaces(const struct thread *thread);
int thread__set_namespaces(struct thread *thread, u64 timestamp,
struct namespaces_event *event);
int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
bool exec); bool exec);
static inline int thread__set_comm(struct thread *thread, const char *comm, static inline int thread__set_comm(struct thread *thread, const char *comm,
......
...@@ -40,6 +40,7 @@ struct perf_tool { ...@@ -40,6 +40,7 @@ struct perf_tool {
event_op mmap, event_op mmap,
mmap2, mmap2,
comm, comm,
namespaces,
fork, fork,
exit, exit,
lost, lost,
...@@ -66,6 +67,7 @@ struct perf_tool { ...@@ -66,6 +67,7 @@ struct perf_tool {
event_op3 auxtrace; event_op3 auxtrace;
bool ordered_events; bool ordered_events;
bool ordering_requires_timestamps; bool ordering_requires_timestamps;
bool namespace_events;
}; };
#endif /* __PERF_TOOL_H */ #endif /* __PERF_TOOL_H */
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