Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf fixes from Ingo Molnar:
 "On the kernel side there's two x86 PMU driver fixes and a uprobes fix,
  plus on the tooling side there's a number of fixes and some late
  updates"

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (36 commits)
  perf sched timehist: Fix invalid period calculation
  perf sched timehist: Remove hardcoded 'comm_width' check at print_summary
  perf sched timehist: Enlarge default 'comm_width'
  perf sched timehist: Honour 'comm_width' when aligning the headers
  perf/x86: Fix overlap counter scheduling bug
  perf/x86/pebs: Fix handling of PEBS buffer overflows
  samples/bpf: Move open_raw_sock to separate header
  samples/bpf: Remove perf_event_open() declaration
  samples/bpf: Be consistent with bpf_load_program bpf_insn parameter
  tools lib bpf: Add bpf_prog_{attach,detach}
  samples/bpf: Switch over to libbpf
  perf diff: Do not overwrite valid build id
  perf annotate: Don't throw error for zero length symbols
  perf bench futex: Fix lock-pi help string
  perf trace: Check if MAP_32BIT is defined (again)
  samples/bpf: Make perf_event_read() static
  uprobes: Fix uprobes on MIPS, allow for a cache flush after ixol breakpoint creation
  samples/bpf: Make samples more libbpf-centric
  tools lib bpf: Add flags to bpf_create_map()
  tools lib bpf: use __u32 from linux/types.h
  ...
This commit is contained in:
Linus Torvalds 2016-12-23 16:49:12 -08:00
commit 00198dab3b
63 changed files with 1104 additions and 750 deletions

View file

@ -132,6 +132,10 @@ OPTIONS for 'perf sched timehist'
--migrations::
Show migration events.
-I::
--idle-hist::
Show idle-related events only.
--time::
Only analyze samples within given time window: <start>,<stop>. Times
have the format seconds.microseconds. If start is not given (i.e., time

View file

@ -201,6 +201,7 @@ goals := $(filter-out all sub-make, $(MAKECMDGOALS))
$(goals) all: sub-make
sub-make: fixdep
@./check-headers.sh
$(Q)$(MAKE) FIXDEP=1 -f Makefile.perf $(goals)
else # force_fixdep
@ -404,99 +405,6 @@ export JEVENTS
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
$(PERF_IN): prepare FORCE
@(test -f ../../include/uapi/linux/perf_event.h && ( \
(diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \
|| echo "Warning: tools/include/uapi/linux/perf_event.h differs from kernel" >&2 )) || true
@(test -f ../../include/linux/hash.h && ( \
(diff -B ../include/linux/hash.h ../../include/linux/hash.h >/dev/null) \
|| echo "Warning: tools/include/linux/hash.h differs from kernel" >&2 )) || true
@(test -f ../../include/uapi/linux/hw_breakpoint.h && ( \
(diff -B ../include/uapi/linux/hw_breakpoint.h ../../include/uapi/linux/hw_breakpoint.h >/dev/null) \
|| echo "Warning: tools/include/uapi/linux/hw_breakpoint.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/asm/disabled-features.h && ( \
(diff -B ../arch/x86/include/asm/disabled-features.h ../../arch/x86/include/asm/disabled-features.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/asm/disabled-features.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/asm/required-features.h && ( \
(diff -B ../arch/x86/include/asm/required-features.h ../../arch/x86/include/asm/required-features.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/asm/required-features.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/asm/cpufeatures.h && ( \
(diff -B ../arch/x86/include/asm/cpufeatures.h ../../arch/x86/include/asm/cpufeatures.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/asm/cpufeatures.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/lib/memcpy_64.S && ( \
(diff -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>" ../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memcpy_64.S >/dev/null) \
|| echo "Warning: tools/arch/x86/lib/memcpy_64.S differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/lib/memset_64.S && ( \
(diff -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>" ../arch/x86/lib/memset_64.S ../../arch/x86/lib/memset_64.S >/dev/null) \
|| echo "Warning: tools/arch/x86/lib/memset_64.S differs from kernel" >&2 )) || true
@(test -f ../../arch/arm/include/uapi/asm/perf_regs.h && ( \
(diff -B ../arch/arm/include/uapi/asm/perf_regs.h ../../arch/arm/include/uapi/asm/perf_regs.h >/dev/null) \
|| echo "Warning: tools/arch/arm/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
@(test -f ../../arch/arm64/include/uapi/asm/perf_regs.h && ( \
(diff -B ../arch/arm64/include/uapi/asm/perf_regs.h ../../arch/arm64/include/uapi/asm/perf_regs.h >/dev/null) \
|| echo "Warning: tools/arch/arm64/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
@(test -f ../../arch/powerpc/include/uapi/asm/perf_regs.h && ( \
(diff -B ../arch/powerpc/include/uapi/asm/perf_regs.h ../../arch/powerpc/include/uapi/asm/perf_regs.h >/dev/null) \
|| echo "Warning: tools/arch/powerpc/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/uapi/asm/perf_regs.h && ( \
(diff -B ../arch/x86/include/uapi/asm/perf_regs.h ../../arch/x86/include/uapi/asm/perf_regs.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/uapi/asm/kvm.h && ( \
(diff -B ../arch/x86/include/uapi/asm/kvm.h ../../arch/x86/include/uapi/asm/kvm.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/uapi/asm/kvm_perf.h && ( \
(diff -B ../arch/x86/include/uapi/asm/kvm_perf.h ../../arch/x86/include/uapi/asm/kvm_perf.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/uapi/asm/kvm_perf.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/uapi/asm/svm.h && ( \
(diff -B ../arch/x86/include/uapi/asm/svm.h ../../arch/x86/include/uapi/asm/svm.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/uapi/asm/svm.h differs from kernel" >&2 )) || true
@(test -f ../../arch/x86/include/uapi/asm/vmx.h && ( \
(diff -B ../arch/x86/include/uapi/asm/vmx.h ../../arch/x86/include/uapi/asm/vmx.h >/dev/null) \
|| echo "Warning: tools/arch/x86/include/uapi/asm/vmx.h differs from kernel" >&2 )) || true
@(test -f ../../arch/powerpc/include/uapi/asm/kvm.h && ( \
(diff -B ../arch/powerpc/include/uapi/asm/kvm.h ../../arch/powerpc/include/uapi/asm/kvm.h >/dev/null) \
|| echo "Warning: tools/arch/powerpc/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
@(test -f ../../arch/s390/include/uapi/asm/kvm.h && ( \
(diff -B ../arch/s390/include/uapi/asm/kvm.h ../../arch/s390/include/uapi/asm/kvm.h >/dev/null) \
|| echo "Warning: tools/arch/s390/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
@(test -f ../../arch/s390/include/uapi/asm/kvm_perf.h && ( \
(diff -B ../arch/s390/include/uapi/asm/kvm_perf.h ../../arch/s390/include/uapi/asm/kvm_perf.h >/dev/null) \
|| echo "Warning: tools/arch/s390/include/uapi/asm/kvm_perf.h differs from kernel" >&2 )) || true
@(test -f ../../arch/s390/include/uapi/asm/sie.h && ( \
(diff -B ../arch/s390/include/uapi/asm/sie.h ../../arch/s390/include/uapi/asm/sie.h >/dev/null) \
|| echo "Warning: tools/arch/s390/include/uapi/asm/sie.h differs from kernel" >&2 )) || true
@(test -f ../../arch/arm/include/uapi/asm/kvm.h && ( \
(diff -B ../arch/arm/include/uapi/asm/kvm.h ../../arch/arm/include/uapi/asm/kvm.h >/dev/null) \
|| echo "Warning: tools/arch/arm/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
@(test -f ../../arch/arm64/include/uapi/asm/kvm.h && ( \
(diff -B ../arch/arm64/include/uapi/asm/kvm.h ../../arch/arm64/include/uapi/asm/kvm.h >/dev/null) \
|| echo "Warning: tools/arch/arm64/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
@(test -f ../../include/asm-generic/bitops/arch_hweight.h && ( \
(diff -B ../include/asm-generic/bitops/arch_hweight.h ../../include/asm-generic/bitops/arch_hweight.h >/dev/null) \
|| echo "Warning: tools/include/asm-generic/bitops/arch_hweight.h differs from kernel" >&2 )) || true
@(test -f ../../include/asm-generic/bitops/const_hweight.h && ( \
(diff -B ../include/asm-generic/bitops/const_hweight.h ../../include/asm-generic/bitops/const_hweight.h >/dev/null) \
|| echo "Warning: tools/include/asm-generic/bitops/const_hweight.h differs from kernel" >&2 )) || true
@(test -f ../../include/asm-generic/bitops/__fls.h && ( \
(diff -B ../include/asm-generic/bitops/__fls.h ../../include/asm-generic/bitops/__fls.h >/dev/null) \
|| echo "Warning: tools/include/asm-generic/bitops/__fls.h differs from kernel" >&2 )) || true
@(test -f ../../include/asm-generic/bitops/fls.h && ( \
(diff -B ../include/asm-generic/bitops/fls.h ../../include/asm-generic/bitops/fls.h >/dev/null) \
|| echo "Warning: tools/include/asm-generic/bitops/fls.h differs from kernel" >&2 )) || true
@(test -f ../../include/asm-generic/bitops/fls64.h && ( \
(diff -B ../include/asm-generic/bitops/fls64.h ../../include/asm-generic/bitops/fls64.h >/dev/null) \
|| echo "Warning: tools/include/asm-generic/bitops/fls64.h differs from kernel" >&2 )) || true
@(test -f ../../include/linux/coresight-pmu.h && ( \
(diff -B ../include/linux/coresight-pmu.h ../../include/linux/coresight-pmu.h >/dev/null) \
|| echo "Warning: tools/include/linux/coresight-pmu.h differs from kernel" >&2 )) || true
@(test -f ../../include/uapi/asm-generic/mman-common.h && ( \
(diff -B ../include/uapi/asm-generic/mman-common.h ../../include/uapi/asm-generic/mman-common.h >/dev/null) \
|| echo "Warning: tools/include/uapi/asm-generic/mman-common.h differs from kernel" >&2 )) || true
@(test -f ../../include/uapi/asm-generic/mman.h && ( \
(diff -B -I "^#include <\(uapi/\)*asm-generic/mman-common.h>$$" ../include/uapi/asm-generic/mman.h ../../include/uapi/asm-generic/mman.h >/dev/null) \
|| echo "Warning: tools/include/uapi/asm-generic/mman.h differs from kernel" >&2 )) || true
@(test -f ../../include/uapi/linux/mman.h && ( \
(diff -B -I "^#include <\(uapi/\)*asm/mman.h>$$" ../include/uapi/linux/mman.h ../../include/uapi/linux/mman.h >/dev/null) \
|| echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true
$(Q)$(MAKE) $(build)=perf
$(JEVENTS_IN): FORCE

View file

@ -48,7 +48,7 @@ static const struct option options[] = {
};
static const char * const bench_futex_lock_pi_usage[] = {
"perf bench futex requeue <options>",
"perf bench futex lock-pi <options>",
NULL
};

View file

@ -208,7 +208,7 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel __maybe_unused,
struct perf_evsel *evsel,
struct machine *machine)
{
struct c2c_hists *c2c_hists = &c2c.hists;
@ -379,7 +379,7 @@ static int symbol_width(struct hists *hists, struct sort_entry *se)
static int c2c_width(struct perf_hpp_fmt *fmt,
struct perf_hpp *hpp __maybe_unused,
struct hists *hists __maybe_unused)
struct hists *hists)
{
struct c2c_fmt *c2c_fmt;
struct c2c_dimension *dim;
@ -1127,7 +1127,7 @@ MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
MEAN_ENTRY(mean_load_entry, load);
static int
cpucnt_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
cpucnt_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he)
{
struct c2c_hist_entry *c2c_he;
@ -1141,7 +1141,7 @@ cpucnt_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
}
static int
cl_idx_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
cl_idx_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he)
{
struct c2c_hist_entry *c2c_he;
@ -1155,7 +1155,7 @@ cl_idx_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
}
static int
cl_idx_empty_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he)
{
int width = c2c_width(fmt, hpp, he->hists);
@ -1779,7 +1779,6 @@ static int c2c_hists__init(struct c2c_hists *hists,
return hpp_list__parse(&hists->list, NULL, sort);
}
__maybe_unused
static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
const char *output,
const char *sort)
@ -2658,7 +2657,7 @@ out:
return err;
}
static int parse_record_events(const struct option *opt __maybe_unused,
static int parse_record_events(const struct option *opt,
const char *str, int unset __maybe_unused)
{
bool *event_set = (bool *) opt->value;

View file

@ -70,8 +70,8 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"),
OPT_BOOLEAN('K', "--all-kernel", &all_kernel, "collect only kernel level data"),
OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"),
OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"),
OPT_END()
};

View file

@ -1687,6 +1687,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
goto out;
}
/* Enable ignoring missing threads when -u option is defined. */
rec->opts.ignore_missing_thread = rec->opts.target.uid != UINT_MAX;
err = -ENOMEM;
if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0)
usage_with_options(record_usage, record_options);

View file

@ -648,7 +648,7 @@ report_parse_ignore_callees_opt(const struct option *opt __maybe_unused,
}
static int
parse_branch_mode(const struct option *opt __maybe_unused,
parse_branch_mode(const struct option *opt,
const char *str __maybe_unused, int unset)
{
int *branch_mode = opt->value;

View file

@ -200,6 +200,7 @@ struct perf_sched {
/* options for timehist command */
bool summary;
bool summary_only;
bool idle_hist;
bool show_callchain;
unsigned int max_stack;
bool show_cpu_visual;
@ -230,6 +231,15 @@ struct evsel_runtime {
u32 ncpu; /* highest cpu slot allocated */
};
/* per cpu idle time data */
struct idle_thread_runtime {
struct thread_runtime tr;
struct thread *last_thread;
struct rb_root sorted_root;
struct callchain_root callchain;
struct callchain_cursor cursor;
};
/* track idle times per cpu */
static struct thread **idle_threads;
static int idle_max_cpu;
@ -1765,7 +1775,7 @@ static u64 perf_evsel__get_time(struct perf_evsel *evsel, u32 cpu)
return r->last_time[cpu];
}
static int comm_width = 20;
static int comm_width = 30;
static char *timehist_get_commstr(struct thread *thread)
{
@ -1807,7 +1817,7 @@ static void timehist_header(struct perf_sched *sched)
printf(" ");
}
printf(" %-20s %9s %9s %9s",
printf(" %-*s %9s %9s %9s", comm_width,
"task name", "wait time", "sch delay", "run time");
printf("\n");
@ -1820,7 +1830,8 @@ static void timehist_header(struct perf_sched *sched)
if (sched->show_cpu_visual)
printf(" %*s ", ncpus, "");
printf(" %-20s %9s %9s %9s\n", "[tid/pid]", "(msec)", "(msec)", "(msec)");
printf(" %-*s %9s %9s %9s\n", comm_width,
"[tid/pid]", "(msec)", "(msec)", "(msec)");
/*
* separator
@ -1830,7 +1841,7 @@ static void timehist_header(struct perf_sched *sched)
if (sched->show_cpu_visual)
printf(" %.*s ", ncpus, graph_dotted_line);
printf(" %.20s %.9s %.9s %.9s",
printf(" %.*s %.9s %.9s %.9s", comm_width,
graph_dotted_line, graph_dotted_line, graph_dotted_line,
graph_dotted_line);
@ -1939,39 +1950,40 @@ static void timehist_update_runtime_stats(struct thread_runtime *r,
r->total_run_time += r->dt_run;
}
static bool is_idle_sample(struct perf_sched *sched,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
static bool is_idle_sample(struct perf_sample *sample,
struct perf_evsel *evsel)
{
struct thread *thread;
struct callchain_cursor *cursor = &callchain_cursor;
/* pid 0 == swapper == idle task */
if (sample->pid == 0)
return true;
if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0)
return perf_evsel__intval(evsel, sample, "prev_pid") == 0;
if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0) {
if (perf_evsel__intval(evsel, sample, "prev_pid") == 0)
return true;
}
return sample->pid == 0;
}
static void save_task_callchain(struct perf_sched *sched,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
{
struct callchain_cursor *cursor = &callchain_cursor;
struct thread *thread;
/* want main thread for process - has maps */
thread = machine__findnew_thread(machine, sample->pid, sample->pid);
if (thread == NULL) {
pr_debug("Failed to get thread for pid %d.\n", sample->pid);
return false;
return;
}
if (!symbol_conf.use_callchain || sample->callchain == NULL)
return false;
return;
if (thread__resolve_callchain(thread, cursor, evsel, sample,
NULL, NULL, sched->max_stack + 2) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");
return false;
return;
}
callchain_cursor_commit(cursor);
@ -1994,8 +2006,24 @@ static bool is_idle_sample(struct perf_sched *sched,
callchain_cursor_advance(cursor);
}
}
return false;
static int init_idle_thread(struct thread *thread)
{
struct idle_thread_runtime *itr;
thread__set_comm(thread, idle_comm, 0);
itr = zalloc(sizeof(*itr));
if (itr == NULL)
return -ENOMEM;
init_stats(&itr->tr.run_stats);
callchain_init(&itr->callchain);
callchain_cursor_reset(&itr->cursor);
thread__set_priv(thread, itr);
return 0;
}
/*
@ -2004,7 +2032,7 @@ static bool is_idle_sample(struct perf_sched *sched,
*/
static int init_idle_threads(int ncpu)
{
int i;
int i, ret;
idle_threads = zalloc(ncpu * sizeof(struct thread *));
if (!idle_threads)
@ -2018,7 +2046,9 @@ static int init_idle_threads(int ncpu)
if (idle_threads[i] == NULL)
return -ENOMEM;
thread__set_comm(idle_threads[i], idle_comm, 0);
ret = init_idle_thread(idle_threads[i]);
if (ret < 0)
return ret;
}
return 0;
@ -2065,14 +2095,23 @@ static struct thread *get_idle_thread(int cpu)
idle_threads[cpu] = thread__new(0, 0);
if (idle_threads[cpu]) {
idle_threads[cpu]->tid = 0;
thread__set_comm(idle_threads[cpu], idle_comm, 0);
if (init_idle_thread(idle_threads[cpu]) < 0)
return NULL;
}
}
return idle_threads[cpu];
}
static void save_idle_callchain(struct idle_thread_runtime *itr,
struct perf_sample *sample)
{
if (!symbol_conf.use_callchain || sample->callchain == NULL)
return;
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
}
/*
* handle runtime stats saved per thread
*/
@ -2111,7 +2150,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
{
struct thread *thread;
if (is_idle_sample(sched, sample, evsel, machine)) {
if (is_idle_sample(sample, evsel)) {
thread = get_idle_thread(sample->cpu);
if (thread == NULL)
pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
@ -2124,13 +2163,37 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
pr_debug("Failed to get thread for tid %d. skipping sample.\n",
sample->tid);
}
save_task_callchain(sched, sample, evsel, machine);
if (sched->idle_hist) {
struct thread *idle;
struct idle_thread_runtime *itr;
idle = get_idle_thread(sample->cpu);
if (idle == NULL) {
pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
return NULL;
}
itr = thread__priv(idle);
if (itr == NULL)
return NULL;
itr->last_thread = thread;
/* copy task callchain when entering to idle */
if (perf_evsel__intval(evsel, sample, "next_pid") == 0)
save_idle_callchain(itr, sample);
}
}
return thread;
}
static bool timehist_skip_sample(struct perf_sched *sched,
struct thread *thread)
struct thread *thread,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
bool rc = false;
@ -2139,10 +2202,19 @@ static bool timehist_skip_sample(struct perf_sched *sched,
sched->skipped_samples++;
}
if (sched->idle_hist) {
if (strcmp(perf_evsel__name(evsel), "sched:sched_switch"))
rc = true;
else if (perf_evsel__intval(evsel, sample, "prev_pid") != 0 &&
perf_evsel__intval(evsel, sample, "next_pid") != 0)
rc = true;
}
return rc;
}
static void timehist_print_wakeup_event(struct perf_sched *sched,
struct perf_evsel *evsel,
struct perf_sample *sample,
struct machine *machine,
struct thread *awakened)
@ -2155,8 +2227,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
return;
/* show wakeup unless both awakee and awaker are filtered */
if (timehist_skip_sample(sched, thread) &&
timehist_skip_sample(sched, awakened)) {
if (timehist_skip_sample(sched, thread, evsel, sample) &&
timehist_skip_sample(sched, awakened, evsel, sample)) {
return;
}
@ -2201,7 +2273,7 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool,
/* show wakeups if requested */
if (sched->show_wakeups &&
!perf_time__skip_sample(&sched->ptime, sample->time))
timehist_print_wakeup_event(sched, sample, machine, thread);
timehist_print_wakeup_event(sched, evsel, sample, machine, thread);
return 0;
}
@ -2228,8 +2300,8 @@ static void timehist_print_migration_event(struct perf_sched *sched,
if (thread == NULL)
return;
if (timehist_skip_sample(sched, thread) &&
timehist_skip_sample(sched, migrated)) {
if (timehist_skip_sample(sched, thread, evsel, sample) &&
timehist_skip_sample(sched, migrated, evsel, sample)) {
return;
}
@ -2314,7 +2386,7 @@ static int timehist_sched_change_event(struct perf_tool *tool,
goto out;
}
if (timehist_skip_sample(sched, thread))
if (timehist_skip_sample(sched, thread, evsel, sample))
goto out;
tr = thread__get_runtime(thread);
@ -2333,7 +2405,7 @@ static int timehist_sched_change_event(struct perf_tool *tool,
if (ptime->start && ptime->start > t)
goto out;
if (ptime->start > tprev)
if (tprev && ptime->start > tprev)
tprev = ptime->start;
/*
@ -2350,7 +2422,39 @@ static int timehist_sched_change_event(struct perf_tool *tool,
t = ptime->end;
}
timehist_update_runtime_stats(tr, t, tprev);
if (!sched->idle_hist || thread->tid == 0) {
timehist_update_runtime_stats(tr, t, tprev);
if (sched->idle_hist) {
struct idle_thread_runtime *itr = (void *)tr;
struct thread_runtime *last_tr;
BUG_ON(thread->tid != 0);
if (itr->last_thread == NULL)
goto out;
/* add current idle time as last thread's runtime */
last_tr = thread__get_runtime(itr->last_thread);
if (last_tr == NULL)
goto out;
timehist_update_runtime_stats(last_tr, t, tprev);
/*
* remove delta time of last thread as it's not updated
* and otherwise it will show an invalid value next
* time. we only care total run time and run stat.
*/
last_tr->dt_run = 0;
last_tr->dt_wait = 0;
last_tr->dt_delay = 0;
if (itr->cursor.nr)
callchain_append(&itr->callchain, &itr->cursor, t - tprev);
itr->last_thread = NULL;
}
}
if (!sched->summary_only)
timehist_print_sample(sched, sample, &al, thread, t);
@ -2457,6 +2561,60 @@ static int show_deadthread_runtime(struct thread *t, void *priv)
return __show_thread_runtime(t, priv);
}
static size_t callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
{
const char *sep = " <- ";
struct callchain_list *chain;
size_t ret = 0;
char bf[1024];
bool first;
if (node == NULL)
return 0;
ret = callchain__fprintf_folded(fp, node->parent);
first = (ret == 0);
list_for_each_entry(chain, &node->val, list) {
if (chain->ip >= PERF_CONTEXT_MAX)
continue;
if (chain->ms.sym && chain->ms.sym->ignore)
continue;
ret += fprintf(fp, "%s%s", first ? "" : sep,
callchain_list__sym_name(chain, bf, sizeof(bf),
false));
first = false;
}
return ret;
}
static size_t timehist_print_idlehist_callchain(struct rb_root *root)
{
size_t ret = 0;
FILE *fp = stdout;
struct callchain_node *chain;
struct rb_node *rb_node = rb_first(root);
printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains");
printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line,
graph_dotted_line);
while (rb_node) {
chain = rb_entry(rb_node, struct callchain_node, rb_node);
rb_node = rb_next(rb_node);
ret += fprintf(fp, " ");
print_sched_time(chain->hit, 12);
ret += 16; /* print_sched_time returns 2nd arg + 4 */
ret += fprintf(fp, " %8d ", chain->count);
ret += callchain__fprintf_folded(fp, chain);
ret += fprintf(fp, "\n");
}
return ret;
}
static void timehist_print_summary(struct perf_sched *sched,
struct perf_session *session)
{
@ -2469,12 +2627,15 @@ static void timehist_print_summary(struct perf_sched *sched,
memset(&totals, 0, sizeof(totals));
if (comm_width < 30)
comm_width = 30;
printf("\nRuntime summary\n");
printf("%*s parent sched-in ", comm_width, "comm");
printf(" run-time min-run avg-run max-run stddev migrations\n");
if (sched->idle_hist) {
printf("\nIdle-time summary\n");
printf("%*s parent sched-out ", comm_width, "comm");
printf(" idle-time min-idle avg-idle max-idle stddev migrations\n");
} else {
printf("\nRuntime summary\n");
printf("%*s parent sched-in ", comm_width, "comm");
printf(" run-time min-run avg-run max-run stddev migrations\n");
}
printf("%*s (count) ", comm_width, "");
printf(" (msec) (msec) (msec) (msec) %%\n");
printf("%.117s\n", graph_dotted_line);
@ -2490,7 +2651,7 @@ static void timehist_print_summary(struct perf_sched *sched,
printf("<no terminated tasks>\n");
/* CPU idle stats not tracked when samples were skipped */
if (sched->skipped_samples)
if (sched->skipped_samples && !sched->idle_hist)
return;
printf("\nIdle stats:\n");
@ -2509,6 +2670,35 @@ static void timehist_print_summary(struct perf_sched *sched,
printf(" CPU %2d idle entire time window\n", i);
}
if (sched->idle_hist && symbol_conf.use_callchain) {
callchain_param.mode = CHAIN_FOLDED;
callchain_param.value = CCVAL_PERIOD;
callchain_register_param(&callchain_param);
printf("\nIdle stats by callchain:\n");
for (i = 0; i < idle_max_cpu; ++i) {
struct idle_thread_runtime *itr;
t = idle_threads[i];
if (!t)
continue;
itr = thread__priv(t);
if (itr == NULL)
continue;
callchain_param.sort(&itr->sorted_root, &itr->callchain,
0, &callchain_param);
printf(" CPU %2d:", i);
print_sched_time(itr->tr.total_run_time, 6);
printf(" msec\n");
timehist_print_idlehist_callchain(&itr->sorted_root);
printf("\n");
}
}
printf("\n"
" Total number of unique tasks: %" PRIu64 "\n"
"Total number of context switches: %" PRIu64 "\n"
@ -3036,6 +3226,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('w', "wakeups", &sched.show_wakeups, "Show wakeup events"),
OPT_BOOLEAN('M', "migrations", &sched.show_migrations, "Show migration events"),
OPT_BOOLEAN('V', "cpu-visual", &sched.show_cpu_visual, "Add CPU visual"),
OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"),
OPT_STRING(0, "time", &sched.time_str, "str",
"Time span for analysis (start,stop)"),
OPT_PARENT(sched_options)

View file

@ -2195,7 +2195,7 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
}
static
int process_stat_config_event(struct perf_tool *tool __maybe_unused,
int process_stat_config_event(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session __maybe_unused)
{
@ -2238,7 +2238,7 @@ static int set_maps(struct perf_stat *st)
}
static
int process_thread_map_event(struct perf_tool *tool __maybe_unused,
int process_thread_map_event(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session __maybe_unused)
{
@ -2257,7 +2257,7 @@ int process_thread_map_event(struct perf_tool *tool __maybe_unused,
}
static
int process_cpu_map_event(struct perf_tool *tool __maybe_unused,
int process_cpu_map_event(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session __maybe_unused)
{

59
tools/perf/check-headers.sh Executable file
View file

@ -0,0 +1,59 @@
#!/bin/sh
HEADERS='
include/uapi/linux/perf_event.h
include/linux/hash.h
include/uapi/linux/hw_breakpoint.h
arch/x86/include/asm/disabled-features.h
arch/x86/include/asm/required-features.h
arch/x86/include/asm/cpufeatures.h
arch/arm/include/uapi/asm/perf_regs.h
arch/arm64/include/uapi/asm/perf_regs.h
arch/powerpc/include/uapi/asm/perf_regs.h
arch/x86/include/uapi/asm/perf_regs.h
arch/x86/include/uapi/asm/kvm.h
arch/x86/include/uapi/asm/kvm_perf.h
arch/x86/include/uapi/asm/svm.h
arch/x86/include/uapi/asm/vmx.h
arch/powerpc/include/uapi/asm/kvm.h
arch/s390/include/uapi/asm/kvm.h
arch/s390/include/uapi/asm/kvm_perf.h
arch/s390/include/uapi/asm/sie.h
arch/arm/include/uapi/asm/kvm.h
arch/arm64/include/uapi/asm/kvm.h
include/asm-generic/bitops/arch_hweight.h
include/asm-generic/bitops/const_hweight.h
include/asm-generic/bitops/__fls.h
include/asm-generic/bitops/fls.h
include/asm-generic/bitops/fls64.h
include/linux/coresight-pmu.h
include/uapi/asm-generic/mman-common.h
'
check () {
file=$1
opts=
shift
while [ -n "$*" ]; do
opts="$opts \"$1\""
shift
done
cmd="diff $opts ../$file ../../$file > /dev/null"
test -f ../../$file &&
eval $cmd || echo "Warning: $file differs from kernel" >&2
}
# simple diff check
for i in $HEADERS; do
check $i -B
done
# diff with extra ignore lines
check arch/x86/lib/memcpy_64.S -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>"
check arch/x86/lib/memset_64.S -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>"
check include/uapi/asm-generic/mman.h -B -I "^#include <\(uapi/\)*asm-generic/mman-common.h>"
check include/uapi/linux/mman.h -B -I "^#include <\(uapi/\)*asm/mman.h>"

View file

@ -55,6 +55,7 @@ struct record_opts {
bool all_user;
bool tail_synthesize;
bool overwrite;
bool ignore_missing_thread;
unsigned int freq;
unsigned int mmap_pages;
unsigned int auxtrace_mmap_pages;

View file

@ -185,6 +185,10 @@ static struct test generic_tests[] = {
.desc = "Synthesize thread map",
.func = test__thread_map_synthesize,
},
{
.desc = "Remove thread map",
.func = test__thread_map_remove,
},
{
.desc = "Synthesize cpu map",
.func = test__cpu_map_synthesize,

View file

@ -80,6 +80,7 @@ const char *test__bpf_subtest_get_desc(int subtest);
int test__bpf_subtest_get_nr(void);
int test_session_topology(int subtest);
int test__thread_map_synthesize(int subtest);
int test__thread_map_remove(int subtest);
int test__cpu_map_synthesize(int subtest);
int test__synthesize_stat_config(int subtest);
int test__synthesize_stat(int subtest);

View file

@ -1,3 +1,4 @@
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/prctl.h>
@ -93,3 +94,46 @@ int test__thread_map_synthesize(int subtest __maybe_unused)
return 0;
}
int test__thread_map_remove(int subtest __maybe_unused)
{
struct thread_map *threads;
char *str;
int i;
TEST_ASSERT_VAL("failed to allocate map string",
asprintf(&str, "%d,%d", getpid(), getppid()) >= 0);
threads = thread_map__new_str(str, NULL, 0);
TEST_ASSERT_VAL("failed to allocate thread_map",
threads);
if (verbose)
thread_map__fprintf(threads, stderr);
TEST_ASSERT_VAL("failed to remove thread",
!thread_map__remove(threads, 0));
TEST_ASSERT_VAL("thread_map count != 1", threads->nr == 1);
if (verbose)
thread_map__fprintf(threads, stderr);
TEST_ASSERT_VAL("failed to remove thread",
!thread_map__remove(threads, 0));
TEST_ASSERT_VAL("thread_map count != 0", threads->nr == 0);
if (verbose)
thread_map__fprintf(threads, stderr);
TEST_ASSERT_VAL("failed to not remove thread",
thread_map__remove(threads, 0));
for (i = 0; i < threads->nr; i++)
free(threads->map[i].comm);
free(threads);
return 0;
}

View file

@ -42,7 +42,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,
P_MMAP_FLAG(SHARED);
P_MMAP_FLAG(PRIVATE);
#ifdef MAP_32BIT
P_MMAP_FLAG(32BIT);
#endif
P_MMAP_FLAG(ANONYMOUS);
P_MMAP_FLAG(DENYWRITE);
P_MMAP_FLAG(EXECUTABLE);

View file

@ -215,7 +215,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
ui_browser__set_color(browser, color);
if (dl->ins.ops && dl->ins.ops->scnprintf) {
if (ins__is_jump(&dl->ins)) {
bool fwd = dl->ops.target.offset > (u64)dl->offset;
bool fwd = dl->ops.target.offset > dl->offset;
ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
SLSMG_UARROW_CHAR);
@ -245,7 +245,8 @@ static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sy
{
if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
|| !disasm_line__has_offset(dl)
|| dl->ops.target.offset >= symbol__size(sym))
|| dl->ops.target.offset < 0
|| dl->ops.target.offset >= (s64)symbol__size(sym))
return false;
return true;

View file

@ -223,13 +223,19 @@ bool ins__is_call(const struct ins *ins)
static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused)
{
const char *s = strchr(ops->raw, '+');
const char *c = strchr(ops->raw, ',');
ops->target.addr = strtoull(ops->raw, NULL, 16);
if (s++ != NULL)
ops->target.offset = strtoull(s, NULL, 16);
if (c++ != NULL)
ops->target.addr = strtoull(c, NULL, 16);
else
ops->target.offset = UINT64_MAX;
ops->target.addr = strtoull(ops->raw, NULL, 16);
if (s++ != NULL) {
ops->target.offset = strtoull(s, NULL, 16);
ops->target.offset_avail = true;
} else {
ops->target.offset_avail = false;
}
return 0;
}
@ -237,7 +243,7 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
if (!ops->target.addr)
if (!ops->target.addr || ops->target.offset < 0)
return ins__raw_scnprintf(ins, bf, size, ops);
return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);
@ -641,7 +647,8 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
if (addr < sym->start || addr >= sym->end) {
if ((addr < sym->start || addr >= sym->end) &&
(addr != sym->end || sym->start != sym->end)) {
pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
__func__, __LINE__, sym->name, sym->start, addr, sym->end);
return -ERANGE;
@ -1205,9 +1212,11 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
if (dl == NULL)
return -1;
if (dl->ops.target.offset == UINT64_MAX)
if (!disasm_line__has_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr -
map__rip_2objdump(map, sym->start);
dl->ops.target.offset_avail = true;
}
/* kcore has no symbols, so add the call target name */
if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) {

View file

@ -24,7 +24,8 @@ struct ins_operands {
char *raw;
char *name;
u64 addr;
u64 offset;
s64 offset;
bool offset_avail;
} target;
union {
struct {
@ -68,7 +69,7 @@ struct disasm_line {
static inline bool disasm_line__has_offset(const struct disasm_line *dl)
{
return dl->ops.target.offset != UINT64_MAX;
return dl->ops.target.offset_avail;
}
void disasm_line__free(struct disasm_line *dl);

View file

@ -990,6 +990,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
* it overloads any global configuration.
*/
apply_config_terms(evsel, opts);
evsel->ignore_missing_thread = opts->ignore_missing_thread;
}
static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
@ -1419,6 +1421,33 @@ static int __open_attr__fprintf(FILE *fp, const char *name, const char *val,
return fprintf(fp, " %-32s %s\n", name, val);
}
static bool ignore_missing_thread(struct perf_evsel *evsel,
struct thread_map *threads,
int thread, int err)
{
if (!evsel->ignore_missing_thread)
return false;
/* The system wide setup does not work with threads. */
if (evsel->system_wide)
return false;
/* The -ESRCH is perf event syscall errno for pid's not found. */
if (err != -ESRCH)
return false;
/* If there's only one thread, let it fail. */
if (threads->nr == 1)
return false;
if (thread_map__remove(threads, thread))
return false;
pr_warning("WARNING: Ignored open failure for pid %d\n",
thread_map__pid(threads, thread));
return true;
}
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
struct thread_map *threads)
{
@ -1474,7 +1503,7 @@ retry_sample_id:
for (cpu = 0; cpu < cpus->nr; cpu++) {
for (thread = 0; thread < nthreads; thread++) {
int group_fd;
int fd, group_fd;
if (!evsel->cgrp && !evsel->system_wide)
pid = thread_map__pid(threads, thread);
@ -1484,21 +1513,37 @@ retry_open:
pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",
pid, cpus->map[cpu], group_fd, flags);
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
pid,
cpus->map[cpu],
group_fd, flags);
if (FD(evsel, cpu, thread) < 0) {
fd = sys_perf_event_open(&evsel->attr, pid, cpus->map[cpu],
group_fd, flags);
FD(evsel, cpu, thread) = fd;
if (fd < 0) {
err = -errno;
if (ignore_missing_thread(evsel, threads, thread, err)) {
/*
* We just removed 1 thread, so take a step
* back on thread index and lower the upper
* nthreads limit.
*/
nthreads--;
thread--;
/* ... and pretend like nothing have happened. */
err = 0;
continue;
}
pr_debug2("\nsys_perf_event_open failed, error %d\n",
err);
goto try_fallback;
}
pr_debug2(" = %d\n", FD(evsel, cpu, thread));
pr_debug2(" = %d\n", fd);
if (evsel->bpf_fd >= 0) {
int evt_fd = FD(evsel, cpu, thread);
int evt_fd = fd;
int bpf_fd = evsel->bpf_fd;
err = ioctl(evt_fd,

View file

@ -120,6 +120,7 @@ struct perf_evsel {
bool tracking;
bool per_pkg;
bool precise_max;
bool ignore_missing_thread;
/* parse modifier helper */
int exclude_GH;
int nr_members;

View file

@ -1459,7 +1459,8 @@ int dso__load(struct dso *dso, struct map *map)
* Read the build id if possible. This is required for
* DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
*/
if (is_regular_file(dso->long_name) &&
if (!dso->has_build_id &&
is_regular_file(dso->long_name) &&
filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0)
dso__set_build_id(dso, build_id);

View file

@ -448,3 +448,25 @@ bool thread_map__has(struct thread_map *threads, pid_t pid)
return false;
}
int thread_map__remove(struct thread_map *threads, int idx)
{
int i;
if (threads->nr < 1)
return -EINVAL;
if (idx >= threads->nr)
return -EINVAL;
/*
* Free the 'idx' item and shift the rest up.
*/
free(threads->map[idx].comm);
for (i = idx; i < threads->nr - 1; i++)
threads->map[i] = threads->map[i + 1];
threads->nr--;
return 0;
}

View file

@ -58,4 +58,5 @@ static inline char *thread_map__comm(struct thread_map *map, int thread)
void thread_map__read_comms(struct thread_map *threads);
bool thread_map__has(struct thread_map *threads, pid_t pid);
int thread_map__remove(struct thread_map *threads, int idx);
#endif /* __PERF_THREAD_MAP_H */