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

Pull perf changes from Ingo Molnar:
 "Kernel side changes:

   - Consolidate the PMU interrupt-disabled code amongst architectures
     (Vince Weaver)

   - misc fixes

  Tooling changes (new features, user visible changes):

   - Add support for pagefault tracing in 'trace', please see multiple
     examples in the changeset messages (Stanislav Fomichev).

   - Add pagefault statistics in 'trace' (Stanislav Fomichev)

   - Add header for columns in 'top' and 'report' TUI browsers (Jiri
     Olsa)

   - Add pagefault statistics in 'trace' (Stanislav Fomichev)

   - Add IO mode into timechart command (Stanislav Fomichev)

   - Fallback to syscalls:* when raw_syscalls:* is not available in the
     perl and python perf scripts.  (Daniel Bristot de Oliveira)

   - Add --repeat global option to 'perf bench' to be used in benchmarks
     such as the existing 'futex' one, that was modified to use it
     instead of a local option.  (Davidlohr Bueso)

   - Fix fd -> pathname resolution in 'trace', be it using /proc or a
     vfs_getname probe point.  (Arnaldo Carvalho de Melo)

   - Add suggestion of how to set perf_event_paranoid sysctl, to help
     non-root users trying tools like 'trace' to get a working
     environment.  (Arnaldo Carvalho de Melo)

   - Updates from trace-cmd for traceevent plugin_kvm plus args cleanup
     (Steven Rostedt, Jan Kiszka)

   - Support S/390 in 'perf kvm stat' (Alexander Yarygin)

  Tooling infrastructure changes:

   - Allow reserving a row for header purposes in the hists browser
     (Arnaldo Carvalho de Melo)

   - Various fixes and prep work related to supporting Intel PT (Adrian
     Hunter)

   - Introduce multiple debug variables control (Jiri Olsa)

   - Add callchain and additional sample information for python scripts
     (Joseph Schuchart)

   - More prep work to support Intel PT: (Adrian Hunter)
     - Polishing 'script' BTS output
     - 'inject' can specify --kallsym
     - VDSO is per machine, not a global var
     - Expose data addr lookup functions previously private to 'script'
     - Large mmap fixes in events processing

   - Include standard stringify macros in power pc code (Sukadev
     Bhattiprolu)

  Tooling cleanups:

   - Convert open coded equivalents to asprintf() (Andy Shevchenko)

   - Remove needless reassignments in 'trace' (Arnaldo Carvalho de Melo)

   - Cache the is_exit syscall test in 'trace) (Arnaldo Carvalho de
     Melo)

   - No need to reimplement err() in 'perf bench sched-messaging', drop
     barf().  (Davidlohr Bueso).

   - Remove ev_name argument from perf_evsel__hists_browse, can be
     obtained from the other parameters.  (Jiri Olsa)

  Tooling fixes:

   - Fix memory leak in the 'sched-messaging' perf bench test.
     (Davidlohr Bueso)

   - The -o and -n 'perf bench mem' options are mutually exclusive, emit
     error when both are specified.  (Davidlohr Bueso)

   - Fix scrollbar refresh row index in the ui browser, problem exposed
     now that headers will be added and will be allowed to be switched
     on/off.  (Jiri Olsa)

   - Handle the num array type in python properly (Sebastian Andrzej
     Siewior)

   - Fix wrong condition for allocation failure (Jiri Olsa)

   - Adjust callchain based on DWARF debug info on powerpc (Sukadev
     Bhattiprolu)

   - Fix a risk for doing free on uninitialized pointer in traceevent
     lib (Rickard Strandqvist)

   - Update attr test with PERF_FLAG_FD_CLOEXEC flag (Jiri Olsa)

   - Enable close-on-exec flag on perf file descriptor (Yann Droneaud)

   - Fix build on gcc 4.4.7 (Arnaldo Carvalho de Melo)

   - Event ordering fixes (Jiri Olsa)"

* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (123 commits)
  Revert "perf tools: Fix jump label always changing during tracing"
  perf tools: Fix perf usage string leftover
  perf: Check permission only for parent tracepoint event
  perf record: Store PERF_RECORD_FINISHED_ROUND only for nonempty rounds
  perf record: Always force PERF_RECORD_FINISHED_ROUND event
  perf inject: Add --kallsyms parameter
  perf tools: Expose 'addr' functions so they can be reused
  perf session: Fix accounting of ordered samples queue
  perf powerpc: Include util/util.h and remove stringify macros
  perf tools: Fix build on gcc 4.4.7
  perf tools: Add thread parameter to vdso__dso_findnew()
  perf tools: Add dso__type()
  perf tools: Separate the VDSO map name from the VDSO dso name
  perf tools: Add vdso__new()
  perf machine: Fix the lifetime of the VDSO temporary file
  perf tools: Group VDSO global variables into a structure
  perf session: Add ability to skip 4GiB or more
  perf session: Add ability to 'skip' a non-piped event stream
  perf tools: Pass machine to vdso__dso_findnew()
  perf tools: Add dso__data_size()
  ...
This commit is contained in:
Linus Torvalds 2014-08-04 16:09:53 -07:00
commit ef35ad26f8
147 changed files with 3705 additions and 947 deletions

View File

@ -99,10 +99,6 @@ static int arc_pmu_event_init(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw;
int ret;
/* ARC 700 PMU does not support sampling events */
if (is_sampling_event(event))
return -ENOENT;
switch (event->attr.type) {
case PERF_TYPE_HARDWARE:
if (event->attr.config >= PERF_COUNT_HW_MAX)
@ -298,6 +294,9 @@ static int arc_pmu_device_probe(struct platform_device *pdev)
.read = arc_pmu_read,
};
/* ARC 700 PMU does not support sampling events */
arc_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
ret = perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW);
return ret;

View File

@ -389,14 +389,6 @@ static int bfin_pmu_event_init(struct perf_event *event)
if (attr->exclude_hv || attr->exclude_idle)
return -EPERM;
/*
* All of the on-chip counters are "limited", in that they have
* no interrupts, and are therefore unable to do sampling without
* further work and timer assistance.
*/
if (hwc->sample_period)
return -EINVAL;
ret = 0;
switch (attr->type) {
case PERF_TYPE_RAW:
@ -490,6 +482,13 @@ static int __init bfin_pmu_init(void)
{
int ret;
/*
* All of the on-chip counters are "limited", in that they have
* no interrupts, and are therefore unable to do sampling without
* further work and timer assistance.
*/
pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
ret = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
if (!ret)
perf_cpu_notifier(bfin_pmu_notifier);

View File

@ -567,16 +567,6 @@ static int _hw_perf_event_init(struct perf_event *event)
if (mapping == -1)
return -EINVAL;
/*
* Early cores have "limited" counters - they have no overflow
* interrupts - and so are unable to do sampling without extra work
* and timer assistance.
*/
if (metag_pmu->max_period == 0) {
if (hwc->sample_period)
return -EINVAL;
}
/*
* Don't assign an index until the event is placed into the hardware.
* -1 signifies that we're still deciding where to put it. On SMP
@ -866,6 +856,15 @@ static int __init init_hw_perf_events(void)
pr_info("enabled with %s PMU driver, %d counters available\n",
metag_pmu->name, metag_pmu->max_events);
/*
* Early cores have "limited" counters - they have no overflow
* interrupts - and so are unable to do sampling without extra work
* and timer assistance.
*/
if (metag_pmu->max_period == 0) {
metag_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
}
/* Initialise the active events and reservation mutex */
atomic_set(&metag_pmu->active_events, 0);
mutex_init(&metag_pmu->reserve_mutex);

View File

@ -387,8 +387,7 @@ static int h_24x7_event_init(struct perf_event *event)
event->attr.exclude_hv ||
event->attr.exclude_idle ||
event->attr.exclude_host ||
event->attr.exclude_guest ||
is_sampling_event(event)) /* no sampling */
event->attr.exclude_guest)
return -EINVAL;
/* no branch sampling */
@ -513,6 +512,9 @@ static int hv_24x7_init(void)
if (!hv_page_cache)
return -ENOMEM;
/* sampling not supported */
h_24x7_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1);
if (r)
return r;

View File

@ -210,8 +210,7 @@ static int h_gpci_event_init(struct perf_event *event)
event->attr.exclude_hv ||
event->attr.exclude_idle ||
event->attr.exclude_host ||
event->attr.exclude_guest ||
is_sampling_event(event)) /* no sampling */
event->attr.exclude_guest)
return -EINVAL;
/* no branch sampling */
@ -284,6 +283,9 @@ static int hv_gpci_init(void)
return -ENODEV;
}
/* sampling not supported */
h_gpci_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
r = perf_pmu_register(&h_gpci_pmu, h_gpci_pmu.name, -1);
if (r)
return r;

View File

@ -16,6 +16,7 @@ header-y += ioctls.h
header-y += ipcbuf.h
header-y += kvm.h
header-y += kvm_para.h
header-y += kvm_perf.h
header-y += kvm_virtio.h
header-y += mman.h
header-y += monwriter.h

View File

@ -0,0 +1,25 @@
/*
* Definitions for perf-kvm on s390
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*/
#ifndef __LINUX_KVM_PERF_S390_H
#define __LINUX_KVM_PERF_S390_H
#include <asm/sie.h>
#define DECODE_STR_LEN 40
#define VCPU_ID "id"
#define KVM_ENTRY_TRACE "kvm:kvm_s390_sie_enter"
#define KVM_EXIT_TRACE "kvm:kvm_s390_sie_exit"
#define KVM_EXIT_REASON "icptcode"
#endif

View File

@ -411,12 +411,6 @@ static int cpumf_pmu_event_init(struct perf_event *event)
case PERF_TYPE_HARDWARE:
case PERF_TYPE_HW_CACHE:
case PERF_TYPE_RAW:
/* The CPU measurement counter facility does not have overflow
* interrupts to do sampling. Sampling must be provided by
* external means, for example, by timers.
*/
if (is_sampling_event(event))
return -ENOENT;
err = __hw_perf_event_init(event);
break;
default:
@ -681,6 +675,12 @@ static int __init cpumf_pmu_init(void)
goto out;
}
/* The CPU measurement counter facility does not have overflow
* interrupts to do sampling. Sampling must be provided by
* external means, for example, by timers.
*/
cpumf_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
cpumf_pmu.attr_groups = cpumf_cf_event_group();
rc = perf_pmu_register(&cpumf_pmu, "cpum_cf", PERF_TYPE_RAW);
if (rc) {

View File

@ -128,14 +128,6 @@ static int __hw_perf_event_init(struct perf_event *event)
if (!sh_pmu_initialized())
return -ENODEV;
/*
* All of the on-chip counters are "limited", in that they have
* no interrupts, and are therefore unable to do sampling without
* further work and timer assistance.
*/
if (hwc->sample_period)
return -EINVAL;
/*
* See if we need to reserve the counter.
*
@ -392,6 +384,13 @@ int register_sh_pmu(struct sh_pmu *_pmu)
pr_info("Performance Events: %s support registered\n", _pmu->name);
/*
* All of the on-chip counters are "limited", in that they have
* no interrupts, and are therefore unable to do sampling without
* further work and timer assistance.
*/
pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
WARN_ON(_pmu->num_events > MAX_HWEVENTS);
perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);

View File

@ -22,6 +22,7 @@ header-y += ipcbuf.h
header-y += ist.h
header-y += kvm.h
header-y += kvm_para.h
header-y += kvm_perf.h
header-y += ldt.h
header-y += mce.h
header-y += mman.h

View File

@ -0,0 +1,16 @@
#ifndef _ASM_X86_KVM_PERF_H
#define _ASM_X86_KVM_PERF_H
#include <asm/svm.h>
#include <asm/vmx.h>
#include <asm/kvm.h>
#define DECODE_STR_LEN 20
#define VCPU_ID "vcpu_id"
#define KVM_ENTRY_TRACE "kvm:kvm_entry"
#define KVM_EXIT_TRACE "kvm:kvm_exit"
#define KVM_EXIT_REASON "exit_reason"
#endif /* _ASM_X86_KVM_PERF_H */

View File

@ -294,31 +294,41 @@ static struct amd_uncore *amd_uncore_alloc(unsigned int cpu)
cpu_to_node(cpu));
}
static void amd_uncore_cpu_up_prepare(unsigned int cpu)
static int amd_uncore_cpu_up_prepare(unsigned int cpu)
{
struct amd_uncore *uncore;
struct amd_uncore *uncore_nb = NULL, *uncore_l2;
if (amd_uncore_nb) {
uncore = amd_uncore_alloc(cpu);
uncore->cpu = cpu;
uncore->num_counters = NUM_COUNTERS_NB;
uncore->rdpmc_base = RDPMC_BASE_NB;
uncore->msr_base = MSR_F15H_NB_PERF_CTL;
uncore->active_mask = &amd_nb_active_mask;
uncore->pmu = &amd_nb_pmu;
*per_cpu_ptr(amd_uncore_nb, cpu) = uncore;
uncore_nb = amd_uncore_alloc(cpu);
if (!uncore_nb)
goto fail;
uncore_nb->cpu = cpu;
uncore_nb->num_counters = NUM_COUNTERS_NB;
uncore_nb->rdpmc_base = RDPMC_BASE_NB;
uncore_nb->msr_base = MSR_F15H_NB_PERF_CTL;
uncore_nb->active_mask = &amd_nb_active_mask;
uncore_nb->pmu = &amd_nb_pmu;
*per_cpu_ptr(amd_uncore_nb, cpu) = uncore_nb;
}
if (amd_uncore_l2) {
uncore = amd_uncore_alloc(cpu);
uncore->cpu = cpu;
uncore->num_counters = NUM_COUNTERS_L2;
uncore->rdpmc_base = RDPMC_BASE_L2;
uncore->msr_base = MSR_F16H_L2I_PERF_CTL;
uncore->active_mask = &amd_l2_active_mask;
uncore->pmu = &amd_l2_pmu;
*per_cpu_ptr(amd_uncore_l2, cpu) = uncore;
uncore_l2 = amd_uncore_alloc(cpu);
if (!uncore_l2)
goto fail;
uncore_l2->cpu = cpu;
uncore_l2->num_counters = NUM_COUNTERS_L2;
uncore_l2->rdpmc_base = RDPMC_BASE_L2;
uncore_l2->msr_base = MSR_F16H_L2I_PERF_CTL;
uncore_l2->active_mask = &amd_l2_active_mask;
uncore_l2->pmu = &amd_l2_pmu;
*per_cpu_ptr(amd_uncore_l2, cpu) = uncore_l2;
}
return 0;
fail:
kfree(uncore_nb);
return -ENOMEM;
}
static struct amd_uncore *
@ -441,7 +451,7 @@ static void uncore_dead(unsigned int cpu, struct amd_uncore * __percpu *uncores)
if (!--uncore->refcnt)
kfree(uncore);
*per_cpu_ptr(amd_uncore_nb, cpu) = NULL;
*per_cpu_ptr(uncores, cpu) = NULL;
}
static void amd_uncore_cpu_dead(unsigned int cpu)
@ -461,7 +471,8 @@ amd_uncore_cpu_notifier(struct notifier_block *self, unsigned long action,
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_UP_PREPARE:
amd_uncore_cpu_up_prepare(cpu);
if (amd_uncore_cpu_up_prepare(cpu))
return notifier_from_errno(-ENOMEM);
break;
case CPU_STARTING:
@ -501,20 +512,33 @@ static void __init init_cpu_already_online(void *dummy)
amd_uncore_cpu_online(cpu);
}
static void cleanup_cpu_online(void *dummy)
{
unsigned int cpu = smp_processor_id();
amd_uncore_cpu_dead(cpu);
}
static int __init amd_uncore_init(void)
{
unsigned int cpu;
unsigned int cpu, cpu2;
int ret = -ENODEV;
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return -ENODEV;
goto fail_nodev;
if (!cpu_has_topoext)
return -ENODEV;
goto fail_nodev;
if (cpu_has_perfctr_nb) {
amd_uncore_nb = alloc_percpu(struct amd_uncore *);
perf_pmu_register(&amd_nb_pmu, amd_nb_pmu.name, -1);
if (!amd_uncore_nb) {
ret = -ENOMEM;
goto fail_nb;
}
ret = perf_pmu_register(&amd_nb_pmu, amd_nb_pmu.name, -1);
if (ret)
goto fail_nb;
printk(KERN_INFO "perf: AMD NB counters detected\n");
ret = 0;
@ -522,20 +546,28 @@ static int __init amd_uncore_init(void)
if (cpu_has_perfctr_l2) {
amd_uncore_l2 = alloc_percpu(struct amd_uncore *);
perf_pmu_register(&amd_l2_pmu, amd_l2_pmu.name, -1);
if (!amd_uncore_l2) {
ret = -ENOMEM;
goto fail_l2;
}
ret = perf_pmu_register(&amd_l2_pmu, amd_l2_pmu.name, -1);
if (ret)
goto fail_l2;
printk(KERN_INFO "perf: AMD L2I counters detected\n");
ret = 0;
}
if (ret)
return -ENODEV;
goto fail_nodev;
cpu_notifier_register_begin();
/* init cpus already online before registering for hotplug notifier */
for_each_online_cpu(cpu) {
amd_uncore_cpu_up_prepare(cpu);
ret = amd_uncore_cpu_up_prepare(cpu);
if (ret)
goto fail_online;
smp_call_function_single(cpu, init_cpu_already_online, NULL, 1);
}
@ -543,5 +575,30 @@ static int __init amd_uncore_init(void)
cpu_notifier_register_done();
return 0;
fail_online:
for_each_online_cpu(cpu2) {
if (cpu2 == cpu)
break;
smp_call_function_single(cpu, cleanup_cpu_online, NULL, 1);
}
cpu_notifier_register_done();
/* amd_uncore_nb/l2 should have been freed by cleanup_cpu_online */
amd_uncore_nb = amd_uncore_l2 = NULL;
if (cpu_has_perfctr_l2)
perf_pmu_unregister(&amd_l2_pmu);
fail_l2:
if (cpu_has_perfctr_nb)
perf_pmu_unregister(&amd_nb_pmu);
if (amd_uncore_l2)
free_percpu(amd_uncore_l2);
fail_nb:
if (amd_uncore_nb)
free_percpu(amd_uncore_nb);
fail_nodev:
return ret;
}
device_initcall(amd_uncore_init);

View File

@ -2947,10 +2947,7 @@ nhmex_rbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event
* extra registers. If we failed to take an extra
* register, try the alternative.
*/
if (idx % 2)
idx--;
else
idx++;
idx ^= 1;
if (idx != reg1->idx % 6) {
if (idx == 2)
config1 >>= 8;

View File

@ -5266,6 +5266,12 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
goto got_name;
} else {
if (vma->vm_ops && vma->vm_ops->name) {
name = (char *) vma->vm_ops->name(vma);
if (name)
goto cpy_name;
}
name = (char *)arch_vma_name(vma);
if (name)
goto cpy_name;
@ -7804,7 +7810,7 @@ inherit_task_group(struct perf_event *event, struct task_struct *parent,
/*
* Initialize the perf_event context in task_struct
*/
int perf_event_init_context(struct task_struct *child, int ctxn)
static int perf_event_init_context(struct task_struct *child, int ctxn)
{
struct perf_event_context *child_ctx, *parent_ctx;
struct perf_event_context *cloned_ctx;

View File

@ -30,6 +30,18 @@ static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
return ret;
}
/*
* We checked and allowed to create parent,
* allow children without checking.
*/
if (p_event->parent)
return 0;
/*
* It's ok to check current process (owner) permissions in here,
* because code below is called only via perf_event_open syscall.
*/
/* The ftrace function trace is allowed only for root. */
if (ftrace_event_is_function(tp_event)) {
if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN))

View File

@ -2395,7 +2395,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
char *token;
char *token = NULL;
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_FLAGS;
@ -2448,7 +2448,7 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
char *token;
char *token = NULL;
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_SYMBOL;
@ -2487,7 +2487,7 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
char *token;
char *token = NULL;
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_HEX;

View File

@ -5,8 +5,7 @@
#include "event-parse.h"
static unsigned long long
process___le16_to_cpup(struct trace_seq *s,
unsigned long long *args)
process___le16_to_cpup(struct trace_seq *s, unsigned long long *args)
{
uint16_t *val = (uint16_t *) (unsigned long) args[0];
return val ? (long long) le16toh(*val) : 0;

View File

@ -30,8 +30,7 @@
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
static unsigned long long
process_jbd2_dev_to_name(struct trace_seq *s,
unsigned long long *args)
process_jbd2_dev_to_name(struct trace_seq *s, unsigned long long *args)
{
unsigned int dev = args[0];
@ -40,8 +39,7 @@ process_jbd2_dev_to_name(struct trace_seq *s,
}
static unsigned long long
process_jiffies_to_msecs(struct trace_seq *s,
unsigned long long *args)
process_jiffies_to_msecs(struct trace_seq *s, unsigned long long *args)
{
unsigned long long jiffies = args[0];

View File

@ -240,25 +240,38 @@ static const char *find_exit_reason(unsigned isa, int val)
for (i = 0; strings[i].val >= 0; i++)
if (strings[i].val == val)
break;
if (strings[i].str)
return strings[i].str;
return "UNKNOWN";
return strings[i].str;
}
static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
static int print_exit_reason(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, const char *field)
{
unsigned long long isa;
unsigned long long val;
unsigned long long info1 = 0, info2 = 0;
const char *reason;
if (pevent_get_field_val(s, event, "exit_reason", record, &val, 1) < 0)
if (pevent_get_field_val(s, event, field, record, &val, 1) < 0)
return -1;
if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0)
isa = 1;
trace_seq_printf(s, "reason %s", find_exit_reason(isa, val));
reason = find_exit_reason(isa, val);
if (reason)
trace_seq_printf(s, "reason %s", reason);
else
trace_seq_printf(s, "reason UNKNOWN (%llu)", val);
return 0;
}
static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
{
unsigned long long info1 = 0, info2 = 0;
if (print_exit_reason(s, record, event, "exit_reason") < 0)
return -1;
pevent_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1);
@ -313,6 +326,29 @@ static int kvm_emulate_insn_handler(struct trace_seq *s,
return 0;
}
static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
{
if (print_exit_reason(s, record, event, "exit_code") < 0)
return -1;
pevent_print_num_field(s, " info1 %llx", event, "exit_info1", record, 1);
pevent_print_num_field(s, " info2 %llx", event, "exit_info2", record, 1);
pevent_print_num_field(s, " int_info %llx", event, "exit_int_info", record, 1);
pevent_print_num_field(s, " int_info_err %llx", event, "exit_int_info_err", record, 1);
return 0;
}
static int kvm_nested_vmexit_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
{
pevent_print_num_field(s, "rip %llx ", event, "rip", record, 1);
return kvm_nested_vmexit_inject_handler(s, record, event, context);
}
union kvm_mmu_page_role {
unsigned word;
struct {
@ -409,6 +445,12 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
pevent_register_event_handler(pevent, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);
pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit",
kvm_nested_vmexit_handler, NULL);
pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
kvm_nested_vmexit_inject_handler, NULL);
pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page",
kvm_mmu_get_page_handler, NULL);
@ -443,6 +485,12 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit",
kvm_nested_vmexit_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
kvm_nested_vmexit_inject_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page",
kvm_mmu_get_page_handler, NULL);

View File

@ -16,6 +16,10 @@ This 'perf bench' command is a general framework for benchmark suites.
COMMON OPTIONS
--------------
-r::
--repeat=::
Specify amount of times to repeat the run (default 10).
-f::
--format=::
Specify format style.

View File

@ -41,6 +41,9 @@ OPTIONS
tasks slept. sched_switch contains a callchain where a task slept and
sched_stat contains a timeslice how long a task slept.
--kallsyms=<file>::
kallsyms pathname
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]

View File

@ -51,9 +51,9 @@ There are a couple of variants of perf kvm:
'perf kvm stat <command>' to run a command and gather performance counter
statistics.
Especially, perf 'kvm stat record/report' generates a statistical analysis
of KVM events. Currently, vmexit, mmio and ioport events are supported.
'perf kvm stat record <command>' records kvm events and the events between
start and end <command>.
of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only)
events are supported. 'perf kvm stat record <command>' records kvm events
and the events between start and end <command>.
And this command produces a file which contains tracing results of kvm
events.
@ -103,8 +103,8 @@ STAT REPORT OPTIONS
analyze events which occures on this vcpu. (default: all vcpus)
--event=<value>::
event to be analyzed. Possible values: vmexit, mmio, ioport.
(default: vmexit)
event to be analyzed. Possible values: vmexit, mmio (x86 only),
ioport (x86 only). (default: vmexit)
-k::
--key=<value>::
Sorting key. Possible values: sample (default, sort by samples
@ -138,7 +138,8 @@ STAT LIVE OPTIONS
--event=<value>::
event to be analyzed. Possible values: vmexit, mmio, ioport.
event to be analyzed. Possible values: vmexit,
mmio (x86 only), ioport (x86 only).
(default: vmexit)
-k::
@ -147,7 +148,8 @@ STAT LIVE OPTIONS
number), time (sort by average time).
--duration=<value>::
Show events other than HLT that take longer than duration usecs.
Show events other than HLT (x86 only) or Wait state (s390 only)
that take longer than duration usecs.
SEE ALSO
--------

View File

@ -15,10 +15,20 @@ DESCRIPTION
There are two variants of perf timechart:
'perf timechart record <command>' to record the system level events
of an arbitrary workload.
of an arbitrary workload. By default timechart records only scheduler
and CPU events (task switches, running times, CPU power states, etc),
but it's possible to record IO (disk, network) activity using -I argument.
'perf timechart' to turn a trace into a Scalable Vector Graphics file,
that can be viewed with popular SVG viewers such as 'Inkscape'.
that can be viewed with popular SVG viewers such as 'Inkscape'. Depending
on the events in the perf.data file, timechart will contain scheduler/cpu
events or IO events.
In IO mode, every bar has two charts: upper and lower.
Upper bar shows incoming events (disk reads, ingress network packets).
Lower bar shows outgoing events (disk writes, egress network packets).
There are also poll bars which show how much time application spent
in poll/epoll/select syscalls.
TIMECHART OPTIONS
-----------------
@ -54,6 +64,19 @@ TIMECHART OPTIONS
duration or tasks with given name. If number is given it's interpreted
as number of nanoseconds. If non-numeric string is given it's
interpreted as task name.
--io-skip-eagain::
Don't draw EAGAIN IO events.
--io-min-time=<nsecs>::
Draw small events as if they lasted min-time. Useful when you need
to see very small and fast IO. It's possible to specify ms or us
suffix to specify time in milliseconds or microseconds.
Default value is 1ms.
--io-merge-dist=<nsecs>::
Merge events that are merge-dist nanoseconds apart.
Reduces number of figures on the SVG and makes it more render-friendly.
It's possible to specify ms or us suffix to specify time in
milliseconds or microseconds.
Default value is 1us.
RECORD OPTIONS
--------------
@ -63,6 +86,9 @@ RECORD OPTIONS
-T::
--tasks-only::
Record only tasks-related events
-I::
--io-only::
Record only io-related events
-g::
--callchain::
Do call-graph (stack chain/backtrace) recording
@ -87,6 +113,14 @@ Record system-wide timechart:
$ perf timechart --highlight gcc
Record system-wide IO events:
$ perf timechart record -I
then generate timechart:
$ perf timechart
SEE ALSO
--------
linkperf:perf-record[1]

View File

@ -107,6 +107,52 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
Show tool stats such as number of times fd->pathname was discovered thru
hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc.
-F=[all|min|maj]::
--pf=[all|min|maj]::
Trace pagefaults. Optionally, you can specify whether you want minor,
major or all pagefaults. Default value is maj.
--syscalls::
Trace system calls. This options is enabled by default.
PAGEFAULTS
----------
When tracing pagefaults, the format of the trace is as follows:
<min|maj>fault [<ip.symbol>+<ip.offset>] => <addr.dso@addr.offset> (<map type><addr level>).
- min/maj indicates whether fault event is minor or major;
- ip.symbol shows symbol for instruction pointer (the code that generated the
fault); if no debug symbols available, perf trace will print raw IP;
- addr.dso shows DSO for the faulted address;
- map type is either 'd' for non-executable maps or 'x' for executable maps;
- addr level is either 'k' for kernel dso or '.' for user dso.
For symbols resolution you may need to install debugging symbols.
Please be aware that duration is currently always 0 and doesn't reflect actual
time it took for fault to be handled!
When --verbose specified, perf trace tries to print all available information
for both IP and fault address in the form of dso@symbol+offset.
EXAMPLES
--------
Trace only major pagefaults:
$ perf trace --no-syscalls -F
Trace syscalls, major and minor pagefaults:
$ perf trace -F all
1416.547 ( 0.000 ms): python/20235 majfault [CRYPTO_push_info_+0x0] => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0@0x61be0 (x.)
As you can see, there was major pagefault in python process, from
CRYPTO_push_info_ routine which faulted somewhere in libcrypto.so.
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-script[1]

View File

@ -8,7 +8,15 @@ perf - Performance analysis tools for Linux
SYNOPSIS
--------
[verse]
'perf' [--version] [--help] COMMAND [ARGS]
'perf' [--version] [--help] [OPTIONS] COMMAND [ARGS]
OPTIONS
-------
--debug::
Setup debug variable (just verbose for now) in value
range (0, 10). Use like:
--debug verbose # sets verbose = 1
--debug verbose=2 # sets verbose = 2
DESCRIPTION
-----------

View File

@ -37,3 +37,6 @@ arch/x86/include/asm/kvm_host.h
arch/x86/include/uapi/asm/svm.h
arch/x86/include/uapi/asm/vmx.h
arch/x86/include/uapi/asm/kvm.h
arch/x86/include/uapi/asm/kvm_perf.h
arch/s390/include/uapi/asm/sie.h
arch/s390/include/uapi/asm/kvm_perf.h

View File

@ -295,11 +295,13 @@ LIB_H += util/intlist.h
LIB_H += util/perf_regs.h
LIB_H += util/unwind.h
LIB_H += util/vdso.h
LIB_H += util/tsc.h
LIB_H += ui/helpline.h
LIB_H += ui/progress.h
LIB_H += ui/util.h
LIB_H += ui/ui.h
LIB_H += util/data.h
LIB_H += util/kvm-stat.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@ -373,6 +375,8 @@ LIB_OBJS += $(OUTPUT)util/stat.o
LIB_OBJS += $(OUTPUT)util/record.o
LIB_OBJS += $(OUTPUT)util/srcline.o
LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)util/tsc.o
LIB_OBJS += $(OUTPUT)util/cloexec.o
LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o

View File

@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o

View File

@ -5,9 +5,7 @@
#include <string.h>
#include "../../util/header.h"
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#include "../../util/util.h"
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \

View File

@ -0,0 +1,266 @@
/*
* Use DWARF Debug information to skip unnecessary callchain entries.
*
* Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation.
* Copyright (C) 2014 Ulrich Weigand, IBM Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <inttypes.h>
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include "util/thread.h"
#include "util/callchain.h"
/*
* When saving the callchain on Power, the kernel conservatively saves
* excess entries in the callchain. A few of these entries are needed
* in some cases but not others. If the unnecessary entries are not
* ignored, we end up with duplicate arcs in the call-graphs. Use
* DWARF debug information to skip over any unnecessary callchain
* entries.
*
* See function header for arch_adjust_callchain() below for more details.
*
* The libdwfl code in this file is based on code from elfutils
* (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
*/
static char *debuginfo_path;
static const Dwfl_Callbacks offline_callbacks = {
.debuginfo_path = &debuginfo_path,
.find_debuginfo = dwfl_standard_find_debuginfo,
.section_address = dwfl_offline_section_address,
};
/*
* Use the DWARF expression for the Call-frame-address and determine
* if return address is in LR and if a new frame was allocated.
*/
static int check_return_reg(int ra_regno, Dwarf_Frame *frame)
{
Dwarf_Op ops_mem[2];
Dwarf_Op dummy;
Dwarf_Op *ops = &dummy;
size_t nops;
int result;
result = dwarf_frame_register(frame, ra_regno, ops_mem, &ops, &nops);
if (result < 0) {
pr_debug("dwarf_frame_register() %s\n", dwarf_errmsg(-1));
return -1;
}
/*
* Check if return address is on the stack.
*/
if (nops != 0 || ops != NULL)
return 0;
/*
* Return address is in LR. Check if a frame was allocated
* but not-yet used.
*/
result = dwarf_frame_cfa(frame, &ops, &nops);
if (result < 0) {
pr_debug("dwarf_frame_cfa() returns %d, %s\n", result,
dwarf_errmsg(-1));
return -1;
}
/*
* If call frame address is in r1, no new frame was allocated.
*/
if (nops == 1 && ops[0].atom == DW_OP_bregx && ops[0].number == 1 &&
ops[0].number2 == 0)
return 1;
/*
* A new frame was allocated but has not yet been used.
*/
return 2;
}
/*
* Get the DWARF frame from the .eh_frame section.
*/
static Dwarf_Frame *get_eh_frame(Dwfl_Module *mod, Dwarf_Addr pc)
{
int result;
Dwarf_Addr bias;
Dwarf_CFI *cfi;
Dwarf_Frame *frame;
cfi = dwfl_module_eh_cfi(mod, &bias);
if (!cfi) {
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
result = dwarf_cfi_addrframe(cfi, pc, &frame);
if (result) {
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
return frame;
}
/*
* Get the DWARF frame from the .debug_frame section.
*/
static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
{
Dwarf_CFI *cfi;
Dwarf_Addr bias;
Dwarf_Frame *frame;
int result;
cfi = dwfl_module_dwarf_cfi(mod, &bias);
if (!cfi) {
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
result = dwarf_cfi_addrframe(cfi, pc, &frame);
if (result) {
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
return frame;
}
/*
* Return:
* 0 if return address for the program counter @pc is on stack
* 1 if return address is in LR and no new stack frame was allocated
* 2 if return address is in LR and a new frame was allocated (but not
* yet used)
* -1 in case of errors
*/
static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
{
int rc = -1;
Dwfl *dwfl;
Dwfl_Module *mod;
Dwarf_Frame *frame;
int ra_regno;
Dwarf_Addr start = pc;
Dwarf_Addr end = pc;
bool signalp;
dwfl = dwfl_begin(&offline_callbacks);
if (!dwfl) {
pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
return -1;
}
if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) {
pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1));
goto out;
}
mod = dwfl_addrmodule(dwfl, pc);
if (!mod) {
pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
goto out;
}
/*
* To work with split debug info files (eg: glibc), check both
* .eh_frame and .debug_frame sections of the ELF header.
*/
frame = get_eh_frame(mod, pc);
if (!frame) {
frame = get_dwarf_frame(mod, pc);
if (!frame)
goto out;
}
ra_regno = dwarf_frame_info(frame, &start, &end, &signalp);
if (ra_regno < 0) {
pr_debug("Return address register unavailable: %s\n",
dwarf_errmsg(-1));
goto out;
}
rc = check_return_reg(ra_regno, frame);
out:
dwfl_end(dwfl);
return rc;
}
/*
* The callchain saved by the kernel always includes the link register (LR).
*
* 0: PERF_CONTEXT_USER
* 1: Program counter (Next instruction pointer)
* 2: LR value
* 3: Caller's caller
* 4: ...
*
* The value in LR is only needed when it holds a return address. If the
* return address is on the stack, we should ignore the LR value.
*
* Further, when the return address is in the LR, if a new frame was just
* allocated but the LR was not saved into it, then the LR contains the
* caller, slot 4: contains the caller's caller and the contents of slot 3:
* (chain->ips[3]) is undefined and must be ignored.
*
* Use DWARF debug information to determine if any entries need to be skipped.
*
* Return:
* index: of callchain entry that needs to be ignored (if any)
* -1 if no entry needs to be ignored or in case of errors
*/
int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
struct ip_callchain *chain)
{
struct addr_location al;
struct dso *dso = NULL;
int rc;
u64 ip;
u64 skip_slot = -1;
if (chain->nr < 3)
return skip_slot;
ip = chain->ips[2];
thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
if (al.map)
dso = al.map->dso;
if (!dso) {
pr_debug("%" PRIx64 " dso is NULL\n", ip);
return skip_slot;
}
rc = check_return_addr(dso->long_name, ip);
pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n",
dso->long_name, chain->nr, ip, rc);
if (rc == 0) {
/*
* Return address on stack. Ignore LR value in callchain
*/
skip_slot = 2;
} else if (rc == 2) {
/*
* New frame allocated but return address still in LR.
* Ignore the caller's caller entry in callchain.
*/
skip_slot = 3;
}
return skip_slot;
}

View File

@ -2,3 +2,6 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
HAVE_KVM_STAT_SUPPORT := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o

View File

@ -0,0 +1,28 @@
/*
* Implementation of get_cpuid().
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "../../util/header.h"
int get_cpuid(char *buffer, size_t sz)
{
const char *cpuid = "IBM/S390";
if (strlen(cpuid) + 1 > sz)
return -1;
strcpy(buffer, cpuid);
return 0;
}

View File

@ -0,0 +1,105 @@
/*
* Arch specific functions for perf kvm stat.
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*/
#include "../../util/kvm-stat.h"
#include <asm/kvm_perf.h>
define_exit_reasons_table(sie_exit_reasons, sie_intercept_code);
define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes);
define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes);
define_exit_reasons_table(sie_diagnose_codes, diagnose_codes);
define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes);
static void event_icpt_insn_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
unsigned long insn;
insn = perf_evsel__intval(evsel, sample, "instruction");
key->key = icpt_insn_decoder(insn);
key->exit_reasons = sie_icpt_insn_codes;
}
static void event_sigp_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "order_code");
key->exit_reasons = sie_sigp_order_codes;
}
static void event_diag_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "code");
key->exit_reasons = sie_diagnose_codes;
}
static void event_icpt_prog_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "code");
key->exit_reasons = sie_icpt_prog_codes;
}
static struct child_event_ops child_events[] = {
{ .name = "kvm:kvm_s390_intercept_instruction",
.get_key = event_icpt_insn_get_key },
{ .name = "kvm:kvm_s390_handle_sigp",
.get_key = event_sigp_get_key },
{ .name = "kvm:kvm_s390_handle_diag",
.get_key = event_diag_get_key },
{ .name = "kvm:kvm_s390_intercept_prog",
.get_key = event_icpt_prog_get_key },
{ NULL, NULL },
};
static struct kvm_events_ops exit_events = {
.is_begin_event = exit_event_begin,
.is_end_event = exit_event_end,
.child_ops = child_events,
.decode_key = exit_event_decode_key,
.name = "VM-EXIT"
};
const char * const kvm_events_tp[] = {
"kvm:kvm_s390_sie_enter",
"kvm:kvm_s390_sie_exit",
"kvm:kvm_s390_intercept_instruction",
"kvm:kvm_s390_handle_sigp",
"kvm:kvm_s390_handle_diag",
"kvm:kvm_s390_intercept_prog",
NULL,
};
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
{ .name = "vmexit", .ops = &exit_events },
{ NULL, NULL },
};
const char * const kvm_skip_events[] = {
"Wait state",
NULL,
};
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
{
if (strstr(cpuid, "IBM/S390")) {
kvm->exit_reasons = sie_exit_reasons;
kvm->exit_reasons_isa = "SIE";
} else
return -ENOTSUP;
return 0;
}

View File

@ -15,3 +15,5 @@ endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o
LIB_H += arch/$(ARCH)/util/tsc.h
HAVE_KVM_STAT_SUPPORT := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o

View File

@ -3,6 +3,7 @@
#include "thread.h"
#include "map.h"
#include "event.h"
#include "debug.h"
#include "tests/tests.h"
#define STACK_SIZE 8192

View File

@ -0,0 +1,156 @@
#include "../../util/kvm-stat.h"
#include <asm/kvm_perf.h>
define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
static struct kvm_events_ops exit_events = {
.is_begin_event = exit_event_begin,
.is_end_event = exit_event_end,
.decode_key = exit_event_decode_key,
.name = "VM-EXIT"
};
/*
* For the mmio events, we treat:
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
*/
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "gpa");
key->info = perf_evsel__intval(evsel, sample, "type");
}
#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
#define KVM_TRACE_MMIO_READ 1
#define KVM_TRACE_MMIO_WRITE 2
static bool mmio_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
/* MMIO read begin event in kernel. */
if (kvm_exit_event(evsel))
return true;
/* MMIO write begin event in kernel. */
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
/* MMIO write end event in kernel. */
if (kvm_entry_event(evsel))
return true;
/* MMIO read end event in kernel.*/
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char *decode)
{
scnprintf(decode, DECODE_STR_LEN, "%#lx:%s",
(unsigned long)key->key,
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
}
static struct kvm_events_ops mmio_events = {
.is_begin_event = mmio_event_begin,
.is_end_event = mmio_event_end,
.decode_key = mmio_event_decode_key,
.name = "MMIO Access"
};
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
static void ioport_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "port");
key->info = perf_evsel__intval(evsel, sample, "rw");
}
static bool ioport_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
if (!strcmp(evsel->name, "kvm:kvm_pio")) {
ioport_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool ioport_event_end(struct perf_evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
return kvm_entry_event(evsel);
}
static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char *decode)
{
scnprintf(decode, DECODE_STR_LEN, "%#llx:%s",
(unsigned long long)key->key,
key->info ? "POUT" : "PIN");
}
static struct kvm_events_ops ioport_events = {
.is_begin_event = ioport_event_begin,
.is_end_event = ioport_event_end,
.decode_key = ioport_event_decode_key,
.name = "IO Port Access"
};
const char * const kvm_events_tp[] = {
"kvm:kvm_entry",
"kvm:kvm_exit",
"kvm:kvm_mmio",
"kvm:kvm_pio",
NULL,
};
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
{ .name = "vmexit", .ops = &exit_events },
{ .name = "mmio", .ops = &mmio_events },
{ .name = "ioport", .ops = &ioport_events },
{ NULL, NULL },
};
const char * const kvm_skip_events[] = {
"HLT",
NULL,
};
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
{
if (strstr(cpuid, "Intel")) {
kvm->exit_reasons = vmx_exit_reasons;
kvm->exit_reasons_isa = "VMX";
} else if (strstr(cpuid, "AMD")) {
kvm->exit_reasons = svm_exit_reasons;
kvm->exit_reasons_isa = "SVM";
} else
return -ENOTSUP;
return 0;
}

View File

@ -6,29 +6,9 @@
#include "../../perf.h"
#include <linux/types.h>
#include "../../util/debug.h"
#include "../../util/tsc.h"
#include "tsc.h"
u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc)
{
u64 t, quot, rem;
t = ns - tc->time_zero;
quot = t / tc->time_mult;
rem = t % tc->time_mult;
return (quot << tc->time_shift) +
(rem << tc->time_shift) / tc->time_mult;
}
u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
{
u64 quot, rem;
quot = cyc >> tc->time_shift;
rem = cyc & ((1 << tc->time_shift) - 1);
return tc->time_zero + quot * tc->time_mult +
((rem * tc->time_mult) >> tc->time_shift);
}
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc)
{
@ -57,3 +37,12 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
return 0;
}
u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}

View File

@ -14,7 +14,4 @@ struct perf_event_mmap_page;
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc);
u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */

View File

@ -3,6 +3,7 @@
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
#ifdef HAVE_ARCH_X86_64_SUPPORT
int libunwind__arch_reg_id(int regnum)

View File

@ -43,5 +43,6 @@ extern int bench_futex_requeue(int argc, const char **argv, const char *prefix);
#define BENCH_FORMAT_UNKNOWN -1
extern int bench_format;
extern unsigned int bench_repeat;
#endif

View File

@ -29,13 +29,6 @@ static u_int32_t futex1 = 0, futex2 = 0;
*/
static unsigned int nrequeue = 1;
/*
* There can be significant variance from run to run,
* the more repeats, the more exact the overall avg and
* the better idea of the futex latency.
*/
static unsigned int repeat = 10;
static pthread_t *worker;
static bool done = 0, silent = 0;
static pthread_mutex_t thread_lock;
@ -46,7 +39,6 @@ static unsigned int ncpus, threads_starting, nthreads = 0;
static const struct option options[] = {
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"),
OPT_UINTEGER('r', "repeat", &repeat, "Specify amount of times to repeat the run"),
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
OPT_END()
};
@ -146,7 +138,7 @@ int bench_futex_requeue(int argc, const char **argv,
pthread_cond_init(&thread_parent, NULL);
pthread_cond_init(&thread_worker, NULL);
for (j = 0; j < repeat && !done; j++) {
for (j = 0; j < bench_repeat && !done; j++) {
unsigned int nrequeued = 0;
struct timeval start, end, runtime;

View File

@ -30,15 +30,8 @@ static u_int32_t futex1 = 0;
*/
static unsigned int nwakes = 1;
/*
* There can be significant variance from run to run,
* the more repeats, the more exact the overall avg and
* the better idea of the futex latency.
*/
static unsigned int repeat = 10;
pthread_t *worker;
static bool done = 0, silent = 0;
static bool done = false, silent = false;
static pthread_mutex_t thread_lock;
static pthread_cond_t thread_parent, thread_worker;
static struct stats waketime_stats, wakeup_stats;
@ -47,7 +40,6 @@ static unsigned int ncpus, threads_starting, nthreads = 0;
static const struct option options[] = {
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"),
OPT_UINTEGER('r', "repeat", &repeat, "Specify amount of times to repeat the run"),
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
OPT_END()
};
@ -149,7 +141,7 @@ int bench_futex_wake(int argc, const char **argv,
pthread_cond_init(&thread_parent, NULL);
pthread_cond_init(&thread_worker, NULL);
for (j = 0; j < repeat && !done; j++) {
for (j = 0; j < bench_repeat && !done; j++) {
unsigned int nwoken = 0;
struct timeval start, end, runtime;

View File

@ -10,6 +10,7 @@
#include "../util/util.h"
#include "../util/parse-options.h"
#include "../util/header.h"
#include "../util/cloexec.h"
#include "bench.h"
#include "mem-memcpy-arch.h"
@ -83,7 +84,8 @@ static struct perf_event_attr cycle_attr = {
static void init_cycle(void)
{
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
perf_event_open_cloexec_flag());
if (cycle_fd < 0 && errno == ENOSYS)
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
@ -189,6 +191,11 @@ int bench_mem_memcpy(int argc, const char **argv,
argc = parse_options(argc, argv, options,
bench_mem_memcpy_usage, 0);
if (no_prefault && only_prefault) {
fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n");
return 1;
}
if (use_cycle)
init_cycle();

View File

@ -10,6 +10,7 @@
#include "../util/util.h"
#include "../util/parse-options.h"
#include "../util/header.h"
#include "../util/cloexec.h"
#include "bench.h"
#include "mem-memset-arch.h"
@ -83,7 +84,8 @@ static struct perf_event_attr cycle_attr = {
static void init_cycle(void)
{
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
perf_event_open_cloexec_flag());
if (cycle_fd < 0 && errno == ENOSYS)
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
@ -181,6 +183,11 @@ int bench_mem_memset(int argc, const char **argv,
argc = parse_options(argc, argv, options,
bench_mem_memset_usage, 0);
if (no_prefault && only_prefault) {
fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n");
return 1;
}
if (use_cycle)
init_cycle();

View File

@ -28,6 +28,7 @@
#include <sys/time.h>
#include <sys/poll.h>
#include <limits.h>
#include <err.h>
#define DATASIZE 100
@ -50,12 +51,6 @@ struct receiver_context {
int wakefd;
};
static void barf(const char *msg)
{
fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
exit(1);
}
static void fdpair(int fds[2])
{
if (use_pipes) {
@ -66,7 +61,7 @@ static void fdpair(int fds[2])
return;
}
barf(use_pipes ? "pipe()" : "socketpair()");
err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()");
}
/* Block until we're ready to go */
@ -77,11 +72,11 @@ static void ready(int ready_out, int wakefd)
/* Tell them we're ready. */
if (write(ready_out, &dummy, 1) != 1)
barf("CLIENT: ready write");
err(EXIT_FAILURE, "CLIENT: ready write");
/* Wait for "GO" signal */
if (poll(&pollfd, 1, -1) != 1)
barf("poll");
err(EXIT_FAILURE, "poll");
}
/* Sender sprays loops messages down each file descriptor */
@ -101,7 +96,7 @@ static void *sender(struct sender_context *ctx)
ret = write(ctx->out_fds[j], data + done,
sizeof(data)-done);
if (ret < 0)
barf("SENDER: write");
err(EXIT_FAILURE, "SENDER: write");
done += ret;
if (done < DATASIZE)
goto again;
@ -131,7 +126,7 @@ static void *receiver(struct receiver_context* ctx)
again:
ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
if (ret < 0)
barf("SERVER: read");
err(EXIT_FAILURE, "SERVER: read");
done += ret;
if (done < DATASIZE)
goto again;
@ -144,14 +139,14 @@ static pthread_t create_worker(void *ctx, void *(*func)(void *))
{
pthread_attr_t attr;
pthread_t childid;
int err;
int ret;
if (!thread_mode) {
/* process mode */
/* Fork the receiver. */
switch (fork()) {
case -1:
barf("fork()");
err(EXIT_FAILURE, "fork()");
break;
case 0:
(*func) (ctx);
@ -165,19 +160,17 @@ static pthread_t create_worker(void *ctx, void *(*func)(void *))
}
if (pthread_attr_init(&attr) != 0)
barf("pthread_attr_init:");
err(EXIT_FAILURE, "pthread_attr_init:");
#ifndef __ia64__
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
barf("pthread_attr_setstacksize");
err(EXIT_FAILURE, "pthread_attr_setstacksize");
#endif
err = pthread_create(&childid, &attr, func, ctx);
if (err != 0) {
fprintf(stderr, "pthread_create failed: %s (%d)\n",
strerror(err), err);
exit(-1);
}
ret = pthread_create(&childid, &attr, func, ctx);
if (ret != 0)
err(EXIT_FAILURE, "pthread_create failed");
return childid;
}
@ -207,14 +200,14 @@ static unsigned int group(pthread_t *pth,
+ num_fds * sizeof(int));
if (!snd_ctx)
barf("malloc()");
err(EXIT_FAILURE, "malloc()");
for (i = 0; i < num_fds; i++) {
int fds[2];
struct receiver_context *ctx = malloc(sizeof(*ctx));
if (!ctx)
barf("malloc()");
err(EXIT_FAILURE, "malloc()");
/* Create the pipe between client and server */
@ -281,7 +274,7 @@ int bench_sched_messaging(int argc, const char **argv,
pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
if (!pth_tab)
barf("main:malloc()");
err(EXIT_FAILURE, "main:malloc()");
fdpair(readyfds);
fdpair(wakefds);
@ -294,13 +287,13 @@ int bench_sched_messaging(int argc, const char **argv,
/* Wait for everyone to be ready */
for (i = 0; i < total_children; i++)
if (read(readyfds[0], &dummy, 1) != 1)
barf("Reading for readyfds");
err(EXIT_FAILURE, "Reading for readyfds");
gettimeofday(&start, NULL);
/* Kick them off */
if (write(wakefds[1], &dummy, 1) != 1)
barf("Writing to start them");
err(EXIT_FAILURE, "Writing to start them");
/* Reap them all */
for (i = 0; i < total_children; i++)
@ -332,5 +325,7 @@ int bench_sched_messaging(int argc, const char **argv,
break;
}
free(pth_tab);
return 0;
}

View File

@ -104,9 +104,11 @@ static const char *bench_format_str;
/* Output/formatting style, exported to benchmark modules: */
int bench_format = BENCH_FORMAT_DEFAULT;
unsigned int bench_repeat = 10; /* default number of times to repeat the run */
static const struct option bench_options[] = {
OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"),
OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"),
OPT_END()
};
@ -226,6 +228,11 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
goto end;
}
if (bench_repeat == 0) {
printf("Invalid repeat option: Must specify a positive value\n");
goto end;
}
if (argc < 1) {
print_usage();
goto end;

View File

@ -125,7 +125,8 @@ static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
return ret;
}
static int build_id_cache__add_kcore(const char *filename, const char *debugdir)
static int build_id_cache__add_kcore(const char *filename, const char *debugdir,
bool force)
{
char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1];
char from_dir[PATH_MAX], to_dir[PATH_MAX];
@ -144,7 +145,8 @@ static int build_id_cache__add_kcore(const char *filename, const char *debugdir)
scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s",
debugdir, sbuildid);
if (!build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
if (!force &&
!build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
pr_debug("same kcore found in %s\n", to_dir);
return 0;
}
@ -389,7 +391,7 @@ int cmd_buildid_cache(int argc, const char **argv,
}
if (kcore_filename &&
build_id_cache__add_kcore(kcore_filename, debugdir))
build_id_cache__add_kcore(kcore_filename, debugdir, force))
pr_warning("Couldn't add %s\n", kcore_filename);
return ret;

View File

@ -15,6 +15,7 @@
#include "util/parse-options.h"
#include "util/session.h"
#include "util/data.h"
#include "util/debug.h"
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
{

View File

@ -11,6 +11,7 @@
#include "util/parse-options.h"
#include "util/run-command.h"
#include "util/help.h"
#include "util/debug.h"
static struct man_viewer_list {
struct man_viewer_list *next;

View File

@ -389,6 +389,9 @@ static int __cmd_inject(struct perf_inject *inject)
ret = perf_session__process_events(session, &inject->tool);
if (!file_out->is_pipe) {
if (inject->build_ids)
perf_header__set_feat(&session->header,
HEADER_BUILD_ID);
session->header.data_size = inject->bytes_written;
perf_session__write_header(session, session->evlist, file_out->fd, true);
}
@ -436,6 +439,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
"where and how long tasks slept"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show build ids, etc)"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
"kallsyms pathname"),
OPT_END()
};
const char * const inject_usage[] = {

View File

@ -29,114 +29,25 @@
#include <pthread.h>
#include <math.h>
#if defined(__i386__) || defined(__x86_64__)
#include <asm/svm.h>
#include <asm/vmx.h>
#include <asm/kvm.h>
#ifdef HAVE_KVM_STAT_SUPPORT
#include <asm/kvm_perf.h>
#include "util/kvm-stat.h"
struct event_key {
#define INVALID_KEY (~0ULL)
u64 key;
int info;
};
struct kvm_event_stats {
u64 time;
struct stats stats;
};
struct kvm_event {
struct list_head hash_entry;
struct rb_node rb;
struct event_key key;
struct kvm_event_stats total;
#define DEFAULT_VCPU_NUM 8
int max_vcpu;
struct kvm_event_stats *vcpu;
};
typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
struct kvm_event_key {
const char *name;
key_cmp_fun key;
};
struct perf_kvm_stat;
struct kvm_events_ops {
bool (*is_begin_event)(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
bool (*is_end_event)(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key);
void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key,
char decode[20]);
const char *name;
};
struct exit_reasons_table {
unsigned long exit_code;
const char *reason;
};
#define EVENTS_BITS 12
#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
struct perf_kvm_stat {
struct perf_tool tool;
struct record_opts opts;
struct perf_evlist *evlist;
struct perf_session *session;
const char *file_name;
const char *report_event;
const char *sort_key;
int trace_vcpu;
struct exit_reasons_table *exit_reasons;
int exit_reasons_size;
const char *exit_reasons_isa;
struct kvm_events_ops *events_ops;
key_cmp_fun compare;
struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
u64 total_time;
u64 total_count;
u64 lost_events;
u64 duration;
const char *pid_str;
struct intlist *pid_list;
struct rb_root result;
int timerfd;
unsigned int display_time;
bool live;
};
static void exit_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
void exit_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->info = 0;
key->key = perf_evsel__intval(evsel, sample, "exit_reason");
key->key = perf_evsel__intval(evsel, sample, KVM_EXIT_REASON);
}
static bool kvm_exit_event(struct perf_evsel *evsel)
bool kvm_exit_event(struct perf_evsel *evsel)
{
return !strcmp(evsel->name, "kvm:kvm_exit");
return !strcmp(evsel->name, KVM_EXIT_TRACE);
}
static bool exit_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key)
bool exit_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
if (kvm_exit_event(evsel)) {
exit_event_get_key(evsel, sample, key);
@ -146,32 +57,23 @@ static bool exit_event_begin(struct perf_evsel *evsel,
return false;
}
static bool kvm_entry_event(struct perf_evsel *evsel)
bool kvm_entry_event(struct perf_evsel *evsel)
{
return !strcmp(evsel->name, "kvm:kvm_entry");
return !strcmp(evsel->name, KVM_ENTRY_TRACE);
}
static bool exit_event_end(struct perf_evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
bool exit_event_end(struct perf_evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
return kvm_entry_event(evsel);
}
static struct exit_reasons_table vmx_exit_reasons[] = {
VMX_EXIT_REASONS
};
static struct exit_reasons_table svm_exit_reasons[] = {
SVM_EXIT_REASONS
};
static const char *get_exit_reason(struct perf_kvm_stat *kvm, u64 exit_code)
static const char *get_exit_reason(struct perf_kvm_stat *kvm,
struct exit_reasons_table *tbl,
u64 exit_code)
{
int i = kvm->exit_reasons_size;
struct exit_reasons_table *tbl = kvm->exit_reasons;
while (i--) {
while (tbl->reason != NULL) {
if (tbl->exit_code == exit_code)
return tbl->reason;
tbl++;
@ -182,146 +84,28 @@ static const char *get_exit_reason(struct perf_kvm_stat *kvm, u64 exit_code)
return "UNKNOWN";
}
static void exit_event_decode_key(struct perf_kvm_stat *kvm,
struct event_key *key,
char decode[20])
void exit_event_decode_key(struct perf_kvm_stat *kvm,
struct event_key *key,
char *decode)
{
const char *exit_reason = get_exit_reason(kvm, key->key);
const char *exit_reason = get_exit_reason(kvm, key->exit_reasons,
key->key);
scnprintf(decode, 20, "%s", exit_reason);
scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason);
}
static struct kvm_events_ops exit_events = {
.is_begin_event = exit_event_begin,
.is_end_event = exit_event_end,
.decode_key = exit_event_decode_key,
.name = "VM-EXIT"
};
/*
* For the mmio events, we treat:
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
*/
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "gpa");
key->info = perf_evsel__intval(evsel, sample, "type");
}
#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
#define KVM_TRACE_MMIO_READ 1
#define KVM_TRACE_MMIO_WRITE 2
static bool mmio_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
/* MMIO read begin event in kernel. */
if (kvm_exit_event(evsel))
return true;
/* MMIO write begin event in kernel. */
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
/* MMIO write end event in kernel. */
if (kvm_entry_event(evsel))
return true;
/* MMIO read end event in kernel.*/
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char decode[20])
{
scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key,
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
}
static struct kvm_events_ops mmio_events = {
.is_begin_event = mmio_event_begin,
.is_end_event = mmio_event_end,
.decode_key = mmio_event_decode_key,
.name = "MMIO Access"
};
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
static void ioport_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "port");
key->info = perf_evsel__intval(evsel, sample, "rw");
}
static bool ioport_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
if (!strcmp(evsel->name, "kvm:kvm_pio")) {
ioport_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool ioport_event_end(struct perf_evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
return kvm_entry_event(evsel);
}
static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char decode[20])
{
scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key,
key->info ? "POUT" : "PIN");
}
static struct kvm_events_ops ioport_events = {
.is_begin_event = ioport_event_begin,
.is_end_event = ioport_event_end,
.decode_key = ioport_event_decode_key,
.name = "IO Port Access"
};
static bool register_kvm_events_ops(struct perf_kvm_stat *kvm)
{
bool ret = true;
struct kvm_reg_events_ops *events_ops = kvm_reg_events_ops;
if (!strcmp(kvm->report_event, "vmexit"))
kvm->events_ops = &exit_events;
else if (!strcmp(kvm->report_event, "mmio"))
kvm->events_ops = &mmio_events;
else if (!strcmp(kvm->report_event, "ioport"))
kvm->events_ops = &ioport_events;
else {
pr_err("Unknown report event:%s\n", kvm->report_event);
ret = false;
for (events_ops = kvm_reg_events_ops; events_ops->name; events_ops++) {
if (!strcmp(events_ops->name, kvm->report_event)) {
kvm->events_ops = events_ops->ops;
return true;
}
}
return ret;
return false;
}
struct vcpu_event_record {
@ -477,6 +261,54 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
return true;
}
static bool is_child_event(struct perf_kvm_stat *kvm,
struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
struct child_event_ops *child_ops;
child_ops = kvm->events_ops->child_ops;
if (!child_ops)
return false;
for (; child_ops->name; child_ops++) {
if (!strcmp(evsel->name, child_ops->name)) {
child_ops->get_key(evsel, sample, key);
return true;
}
}
return false;
}
static bool handle_child_event(struct perf_kvm_stat *kvm,
struct vcpu_event_record *vcpu_record,
struct event_key *key,
struct perf_sample *sample __maybe_unused)
{
struct kvm_event *event = NULL;
if (key->key != INVALID_KEY)
event = find_create_kvm_event(kvm, key);
vcpu_record->last_event = event;
return true;
}
static bool skip_event(const char *event)
{
const char * const *skip_events;
for (skip_events = kvm_skip_events; *skip_events; skip_events++)
if (!strcmp(event, *skip_events))
return true;
return false;
}
static bool handle_end_event(struct perf_kvm_stat *kvm,
struct vcpu_event_record *vcpu_record,
struct event_key *key,
@ -525,10 +357,10 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
time_diff = sample->time - time_begin;
if (kvm->duration && time_diff > kvm->duration) {
char decode[32];
char decode[DECODE_STR_LEN];
kvm->events_ops->decode_key(kvm, &event->key, decode);
if (strcmp(decode, "HLT")) {
if (!skip_event(decode)) {
pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n",
sample->time, sample->pid, vcpu_record->vcpu_id,
decode, time_diff/1000);
@ -553,7 +385,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
return NULL;
}
vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, "vcpu_id");
vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID);
thread->priv = vcpu_record;
}
@ -566,7 +398,8 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
struct perf_sample *sample)
{
struct vcpu_event_record *vcpu_record;
struct event_key key = {.key = INVALID_KEY};
struct event_key key = { .key = INVALID_KEY,
.exit_reasons = kvm->exit_reasons };
vcpu_record = per_vcpu_record(thread, evsel, sample);
if (!vcpu_record)
@ -580,6 +413,9 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
if (kvm->events_ops->is_begin_event(evsel, sample, &key))
return handle_begin_event(kvm, vcpu_record, &key, sample->time);
if (is_child_event(kvm, evsel, sample, &key))
return handle_child_event(kvm, vcpu_record, &key, sample);
if (kvm->events_ops->is_end_event(evsel, sample, &key))
return handle_end_event(kvm, vcpu_record, &key, sample);
@ -740,7 +576,7 @@ static void show_timeofday(void)
static void print_result(struct perf_kvm_stat *kvm)
{
char decode[20];
char decode[DECODE_STR_LEN];
struct kvm_event *event;
int vcpu = kvm->trace_vcpu;
@ -751,7 +587,7 @@ static void print_result(struct perf_kvm_stat *kvm)
pr_info("\n\n");
print_vcpu_info(kvm);
pr_info("%20s ", kvm->events_ops->name);
pr_info("%*s ", DECODE_STR_LEN, kvm->events_ops->name);
pr_info("%10s ", "Samples");
pr_info("%9s ", "Samples%");
@ -770,7 +606,7 @@ static void print_result(struct perf_kvm_stat *kvm)
min = get_event_min(event, vcpu);
kvm->events_ops->decode_key(kvm, &event->key, decode);
pr_info("%20s ", decode);
pr_info("%*s ", DECODE_STR_LEN, decode);
pr_info("%10llu ", (unsigned long long)ecount);
pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100);
pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100);
@ -839,34 +675,28 @@ static int process_sample_event(struct perf_tool *tool,
static int cpu_isa_config(struct perf_kvm_stat *kvm)
{
char buf[64], *cpuid;
int err, isa;
int err;
if (kvm->live) {
err = get_cpuid(buf, sizeof(buf));
if (err != 0) {
pr_err("Failed to look up CPU type (Intel or AMD)\n");
pr_err("Failed to look up CPU type\n");
return err;
}
cpuid = buf;
} else
cpuid = kvm->session->header.env.cpuid;
if (strstr(cpuid, "Intel"))
isa = 1;
else if (strstr(cpuid, "AMD"))
isa = 0;
else {
if (!cpuid) {
pr_err("Failed to look up CPU type\n");
return -EINVAL;
}
err = cpu_isa_init(kvm, cpuid);
if (err == -ENOTSUP)
pr_err("CPU %s is not supported.\n", cpuid);
return -ENOTSUP;
}
if (isa == 1) {
kvm->exit_reasons = vmx_exit_reasons;
kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons);
kvm->exit_reasons_isa = "VMX";
}
return 0;
return err;
}
static bool verify_vcpu(int vcpu)
@ -1300,13 +1130,6 @@ static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm)
return ret;
}
static const char * const kvm_events_tp[] = {
"kvm:kvm_entry",
"kvm:kvm_exit",
"kvm:kvm_mmio",
"kvm:kvm_pio",
};
#define STRDUP_FAIL_EXIT(s) \
({ char *_p; \
_p = strdup(s); \
@ -1318,7 +1141,7 @@ static const char * const kvm_events_tp[] = {
static int
kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
{
unsigned int rec_argc, i, j;
unsigned int rec_argc, i, j, events_tp_size;
const char **rec_argv;
const char * const record_args[] = {
"record",
@ -1326,9 +1149,14 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
"-m", "1024",
"-c", "1",
};
const char * const *events_tp;
events_tp_size = 0;
for (events_tp = kvm_events_tp; *events_tp; events_tp++)
events_tp_size++;
rec_argc = ARRAY_SIZE(record_args) + argc + 2 +
2 * ARRAY_SIZE(kvm_events_tp);
2 * events_tp_size;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (rec_argv == NULL)
@ -1337,7 +1165,7 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
for (i = 0; i < ARRAY_SIZE(record_args); i++)
rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]);
for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) {
for (j = 0; j < events_tp_size; j++) {
rec_argv[i++] = "-e";
rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]);
}
@ -1356,7 +1184,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
{
const struct option kvm_events_report_options[] = {
OPT_STRING(0, "event", &kvm->report_event, "report event",
"event for reporting: vmexit, mmio, ioport"),
"event for reporting: vmexit, "
"mmio (x86 only), ioport (x86 only)"),
OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu,
"vcpu id to report"),
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
@ -1391,16 +1220,16 @@ static struct perf_evlist *kvm_live_event_list(void)
{
struct perf_evlist *evlist;
char *tp, *name, *sys;
unsigned int j;
int err = -1;
const char * const *events_tp;
evlist = perf_evlist__new();
if (evlist == NULL)
return NULL;
for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) {
for (events_tp = kvm_events_tp; *events_tp; events_tp++) {
tp = strdup(kvm_events_tp[j]);
tp = strdup(*events_tp);
if (tp == NULL)
goto out;
@ -1409,7 +1238,7 @@ static struct perf_evlist *kvm_live_event_list(void)
name = strchr(tp, ':');
if (name == NULL) {
pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n",
kvm_events_tp[j]);
*events_tp);
free(tp);
goto out;
}
@ -1417,7 +1246,7 @@ static struct perf_evlist *kvm_live_event_list(void)
name++;
if (perf_evlist__add_newtp(evlist, sys, name, NULL)) {
pr_err("Failed to add %s tracepoint to the list\n", kvm_events_tp[j]);
pr_err("Failed to add %s tracepoint to the list\n", *events_tp);
free(tp);
goto out;
}
@ -1462,7 +1291,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
"key for sorting: sample(sort by samples number)"
" time (sort by avg time)"),
OPT_U64(0, "duration", &kvm->duration,
"show events other than HALT that take longer than duration usecs"),
"show events other than"
" HLT (x86 only) or Wait state (s390 only)"
" that take longer than duration usecs"),
OPT_END()
};
const char * const live_usage[] = {
@ -1585,9 +1416,6 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)
.report_event = "vmexit",
.sort_key = "sample",
.exit_reasons = svm_exit_reasons,
.exit_reasons_size = ARRAY_SIZE(svm_exit_reasons),
.exit_reasons_isa = "SVM",
};
if (argc == 1) {
@ -1609,7 +1437,7 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)
perf_stat:
return cmd_stat(argc, argv, NULL);
}
#endif
#endif /* HAVE_KVM_STAT_SUPPORT */
static int __cmd_record(const char *file_name, int argc, const char **argv)
{
@ -1726,7 +1554,7 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
return cmd_top(argc, argv, NULL);
else if (!strncmp(argv[0], "buildid-list", 12))
return __cmd_buildid_list(file_name, argc, argv);
#if defined(__i386__) || defined(__x86_64__)
#ifdef HAVE_KVM_STAT_SUPPORT
else if (!strncmp(argv[0], "stat", 4))
return kvm_cmd_stat(file_name, argc, argv);
#endif

View File

@ -238,6 +238,7 @@ static struct perf_event_header finished_round_event = {
static int record__mmap_read_all(struct record *rec)
{
u64 bytes_written = rec->bytes_written;
int i;
int rc = 0;
@ -250,7 +251,11 @@ static int record__mmap_read_all(struct record *rec)
}
}
if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
/*
* Mark the round finished in case we wrote
* at least one event.
*/
if (bytes_written != rec->bytes_written)
rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));
out:

View File

@ -10,6 +10,7 @@
#include "util/header.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/cloexec.h"
#include "util/parse-options.h"
#include "util/trace-event.h"
@ -434,7 +435,8 @@ static int self_open_counters(void)
attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_TASK_CLOCK;
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
fd = sys_perf_event_open(&attr, 0, -1, -1,
perf_event_open_cloexec_flag());
if (fd < 0)
pr_err("Error: sys_perf_event_open() syscall returned "
@ -935,8 +937,8 @@ static int latency_switch_event(struct perf_sched *sched,
return -1;
}
sched_out = machine__findnew_thread(machine, 0, prev_pid);
sched_in = machine__findnew_thread(machine, 0, next_pid);
sched_out = machine__findnew_thread(machine, -1, prev_pid);
sched_in = machine__findnew_thread(machine, -1, next_pid);
out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
if (!out_events) {
@ -979,7 +981,7 @@ static int latency_runtime_event(struct perf_sched *sched,
{
const u32 pid = perf_evsel__intval(evsel, sample, "pid");
const u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
struct thread *thread = machine__findnew_thread(machine, 0, pid);
struct thread *thread = machine__findnew_thread(machine, -1, pid);
struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
u64 timestamp = sample->time;
int cpu = sample->cpu;
@ -1012,7 +1014,7 @@ static int latency_wakeup_event(struct perf_sched *sched,
struct thread *wakee;
u64 timestamp = sample->time;
wakee = machine__findnew_thread(machine, 0, pid);
wakee = machine__findnew_thread(machine, -1, pid);
atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
if (!atoms) {
if (thread_atoms_insert(sched, wakee))
@ -1072,7 +1074,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
if (sched->profile_cpu == -1)
return 0;
migrant = machine__findnew_thread(machine, 0, pid);
migrant = machine__findnew_thread(machine, -1, pid);
atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
if (!atoms) {
if (thread_atoms_insert(sched, migrant))
@ -1290,7 +1292,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
return -1;
}
sched_in = machine__findnew_thread(machine, 0, next_pid);
sched_in = machine__findnew_thread(machine, -1, next_pid);
sched->curr_thread[this_cpu] = sched_in;

View File

@ -358,27 +358,6 @@ static void print_sample_start(struct perf_sample *sample,
}
}
static bool is_bts_event(struct perf_event_attr *attr)
{
return ((attr->type == PERF_TYPE_HARDWARE) &&
(attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
(attr->sample_period == 1));
}
static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
{
if ((attr->type == PERF_TYPE_SOFTWARE) &&
((attr->config == PERF_COUNT_SW_PAGE_FAULTS) ||
(attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) ||
(attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)))
return true;
if (is_bts_event(attr))
return true;
return false;
}
static void print_sample_addr(union perf_event *event,
struct perf_sample *sample,
struct machine *machine,
@ -386,24 +365,13 @@ static void print_sample_addr(union perf_event *event,
struct perf_event_attr *attr)
{
struct addr_location al;
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
printf("%16" PRIx64, sample->addr);
if (!sample_addr_correlates_sym(attr))
return;
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
sample->addr, &al);
if (!al.map)
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
sample->addr, &al);
al.cpu = sample->cpu;
al.sym = NULL;
if (al.map)
al.sym = map__find_symbol(al.map, al.addr, NULL);
perf_event__preprocess_sample_addr(event, sample, machine, thread, &al);
if (PRINT_FIELD(SYM)) {
printf(" ");
@ -427,25 +395,35 @@ static void print_sample_bts(union perf_event *event,
struct addr_location *al)
{
struct perf_event_attr *attr = &evsel->attr;
bool print_srcline_last = false;
/* print branch_from information */
if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain)
printf(" ");
else
unsigned int print_opts = output[attr->type].print_ip_opts;
if (symbol_conf.use_callchain && sample->callchain) {
printf("\n");
perf_evsel__print_ip(evsel, sample, al,
output[attr->type].print_ip_opts,
} else {
printf(" ");
if (print_opts & PRINT_IP_OPT_SRCLINE) {
print_srcline_last = true;
print_opts &= ~PRINT_IP_OPT_SRCLINE;
}
}
perf_evsel__print_ip(evsel, sample, al, print_opts,
PERF_MAX_STACK_DEPTH);
}
printf(" => ");
/* print branch_to information */
if (PRINT_FIELD(ADDR) ||
((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[attr->type].user_set))
!output[attr->type].user_set)) {
printf(" => ");
print_sample_addr(event, sample, al->machine, thread, attr);
}
if (print_srcline_last)
map__fprintf_srcline(al->map, al->addr, "\n ", stdout);
printf("\n");
}

View File

@ -184,7 +184,7 @@ static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
{
evsel->priv = zalloc(sizeof(struct perf_stat));
if (evsel == NULL)
if (evsel->priv == NULL)
return -ENOMEM;
perf_evsel__reset_stat_priv(evsel);
return 0;

View File

@ -37,6 +37,7 @@
#include "util/svghelper.h"
#include "util/tool.h"
#include "util/data.h"
#include "util/debug.h"
#define SUPPORT_OLD_POWER_EVENTS 1
#define PWR_EVENT_EXIT -1
@ -60,10 +61,17 @@ struct timechart {
tasks_only,
with_backtrace,
topology;
/* IO related settings */
u64 io_events;
bool io_only,
skip_eagain;
u64 min_time,
merge_dist;
};
struct per_pidcomm;
struct cpu_sample;
struct io_sample;
/*
* Datastructure layout:
@ -84,6 +92,7 @@ struct per_pid {
u64 start_time;
u64 end_time;
u64 total_time;
u64 total_bytes;
int display;
struct per_pidcomm *all;
@ -97,6 +106,8 @@ struct per_pidcomm {
u64 start_time;
u64 end_time;
u64 total_time;
u64 max_bytes;
u64 total_bytes;
int Y;
int display;
@ -107,6 +118,7 @@ struct per_pidcomm {
char *comm;
struct cpu_sample *samples;
struct io_sample *io_samples;
};
struct sample_wrapper {
@ -131,6 +143,27 @@ struct cpu_sample {
const char *backtrace;
};
enum {
IOTYPE_READ,
IOTYPE_WRITE,
IOTYPE_SYNC,
IOTYPE_TX,
IOTYPE_RX,
IOTYPE_POLL,
};
struct io_sample {
struct io_sample *next;
u64 start_time;
u64 end_time;
u64 bytes;
int type;
int fd;
int err;
int merges;
};
#define CSTATE 1
#define PSTATE 2
@ -213,7 +246,7 @@ static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
pid_set_comm(tchart, pid, pp->current->comm);
p->start_time = timestamp;
if (p->current) {
if (p->current && !p->current->start_time) {
p->current->start_time = timestamp;
p->current->state_since = timestamp;
}
@ -682,6 +715,249 @@ static void end_sample_processing(struct timechart *tchart)
}
}
static int pid_begin_io_sample(struct timechart *tchart, int pid, int type,
u64 start, int fd)
{
struct per_pid *p = find_create_pid(tchart, pid);
struct per_pidcomm *c = p->current;
struct io_sample *sample;
struct io_sample *prev;
if (!c) {
c = zalloc(sizeof(*c));
if (!c)
return -ENOMEM;
p->current = c;
c->next = p->all;
p->all = c;
}
prev = c->io_samples;
if (prev && prev->start_time && !prev->end_time) {
pr_warning("Skip invalid start event: "
"previous event already started!\n");
/* remove previous event that has been started,
* we are not sure we will ever get an end for it */
c->io_samples = prev->next;
free(prev);
return 0;
}
sample = zalloc(sizeof(*sample));
if (!sample)
return -ENOMEM;
sample->start_time = start;
sample->type = type;
sample->fd = fd;
sample->next = c->io_samples;
c->io_samples = sample;
if (c->start_time == 0 || c->start_time > start)
c->start_time = start;
return 0;
}
static int pid_end_io_sample(struct timechart *tchart, int pid, int type,
u64 end, long ret)
{
struct per_pid *p = find_create_pid(tchart, pid);
struct per_pidcomm *c = p->current;
struct io_sample *sample, *prev;
if (!c) {
pr_warning("Invalid pidcomm!\n");
return -1;
}
sample = c->io_samples;
if (!sample) /* skip partially captured events */
return 0;
if (sample->end_time) {
pr_warning("Skip invalid end event: "
"previous event already ended!\n");
return 0;
}
if (sample->type != type) {
pr_warning("Skip invalid end event: invalid event type!\n");
return 0;
}
sample->end_time = end;
prev = sample->next;
/* we want to be able to see small and fast transfers, so make them
* at least min_time long, but don't overlap them */
if (sample->end_time - sample->start_time < tchart->min_time)
sample->end_time = sample->start_time + tchart->min_time;
if (prev && sample->start_time < prev->end_time) {
if (prev->err) /* try to make errors more visible */
sample->start_time = prev->end_time;
else
prev->end_time = sample->start_time;
}
if (ret < 0) {
sample->err = ret;
} else if (type == IOTYPE_READ || type == IOTYPE_WRITE ||
type == IOTYPE_TX || type == IOTYPE_RX) {
if ((u64)ret > c->max_bytes)
c->max_bytes = ret;
c->total_bytes += ret;
p->total_bytes += ret;
sample->bytes = ret;
}
/* merge two requests to make svg smaller and render-friendly */
if (prev &&
prev->type == sample->type &&
prev->err == sample->err &&
prev->fd == sample->fd &&
prev->end_time + tchart->merge_dist >= sample->start_time) {
sample->bytes += prev->bytes;
sample->merges += prev->merges + 1;
sample->start_time = prev->start_time;
sample->next = prev->next;
free(prev);
if (!sample->err && sample->bytes > c->max_bytes)
c->max_bytes = sample->bytes;
}
tchart->io_events++;
return 0;
}
static int
process_enter_read(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ,
sample->time, fd);
}
static int
process_exit_read(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ,
sample->time, ret);
}
static int
process_enter_write(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE,
sample->time, fd);
}
static int
process_exit_write(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE,
sample->time, ret);
}
static int
process_enter_sync(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC,
sample->time, fd);
}
static int
process_exit_sync(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC,
sample->time, ret);
}
static int
process_enter_tx(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX,
sample->time, fd);
}
static int
process_exit_tx(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX,
sample->time, ret);
}
static int
process_enter_rx(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX,
sample->time, fd);
}
static int
process_exit_rx(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX,
sample->time, ret);
}
static int
process_enter_poll(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL,
sample->time, fd);
}
static int
process_exit_poll(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL,
sample->time, ret);
}
/*
* Sort the pid datastructure
*/
@ -852,6 +1128,121 @@ static void draw_cpu_usage(struct timechart *tchart)
}
}
static void draw_io_bars(struct timechart *tchart)
{
const char *suf;
double bytes;
char comm[256];
struct per_pid *p;
struct per_pidcomm *c;
struct io_sample *sample;
int Y = 1;
p = tchart->all_data;
while (p) {
c = p->all;
while (c) {
if (!c->display) {
c->Y = 0;
c = c->next;
continue;
}
svg_box(Y, c->start_time, c->end_time, "process3");
sample = c->io_samples;
for (sample = c->io_samples; sample; sample = sample->next) {
double h = (double)sample->bytes / c->max_bytes;
if (tchart->skip_eagain &&
sample->err == -EAGAIN)
continue;
if (sample->err)
h = 1;
if (sample->type == IOTYPE_SYNC)
svg_fbox(Y,
sample->start_time,
sample->end_time,
1,
sample->err ? "error" : "sync",
sample->fd,
sample->err,
sample->merges);
else if (sample->type == IOTYPE_POLL)
svg_fbox(Y,
sample->start_time,
sample->end_time,
1,
sample->err ? "error" : "poll",
sample->fd,
sample->err,
sample->merges);
else if (sample->type == IOTYPE_READ)
svg_ubox(Y,
sample->start_time,
sample->end_time,
h,
sample->err ? "error" : "disk",
sample->fd,
sample->err,
sample->merges);
else if (sample->type == IOTYPE_WRITE)
svg_lbox(Y,
sample->start_time,
sample->end_time,
h,
sample->err ? "error" : "disk",
sample->fd,
sample->err,
sample->merges);
else if (sample->type == IOTYPE_RX)
svg_ubox(Y,
sample->start_time,
sample->end_time,
h,
sample->err ? "error" : "net",
sample->fd,
sample->err,
sample->merges);
else if (sample->type == IOTYPE_TX)
svg_lbox(Y,
sample->start_time,
sample->end_time,
h,
sample->err ? "error" : "net",
sample->fd,
sample->err,
sample->merges);
}
suf = "";
bytes = c->total_bytes;
if (bytes > 1024) {
bytes = bytes / 1024;
suf = "K";
}
if (bytes > 1024) {
bytes = bytes / 1024;
suf = "M";
}
if (bytes > 1024) {
bytes = bytes / 1024;
suf = "G";
}
sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf);
svg_text(Y, c->start_time, comm);
c->Y = Y;
Y++;
c = c->next;
}
p = p->next;
}
}
static void draw_process_bars(struct timechart *tchart)
{
struct per_pid *p;
@ -987,9 +1378,6 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold)
struct per_pidcomm *c;
int count = 0;
if (process_filter)
return determine_display_tasks_filtered(tchart);
p = tchart->all_data;
while (p) {
p->display = 0;
@ -1025,15 +1413,46 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold)
return count;
}
static int determine_display_io_tasks(struct timechart *timechart, u64 threshold)
{
struct per_pid *p;
struct per_pidcomm *c;
int count = 0;
p = timechart->all_data;
while (p) {
/* no exit marker, task kept running to the end */
if (p->end_time == 0)
p->end_time = timechart->last_time;
c = p->all;
while (c) {
c->display = 0;
if (c->total_bytes >= threshold) {
c->display = 1;
count++;
}
if (c->end_time == 0)
c->end_time = timechart->last_time;
c = c->next;
}
p = p->next;
}
return count;
}
#define BYTES_THRESH (1 * 1024 * 1024)
#define TIME_THRESH 10000000
static void write_svg_file(struct timechart *tchart, const char *filename)
{
u64 i;
int count;
int thresh = TIME_THRESH;
int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH;
if (tchart->power_only)
tchart->proc_num = 0;
@ -1041,28 +1460,43 @@ static void write_svg_file(struct timechart *tchart, const char *filename)
/* We'd like to show at least proc_num tasks;
* be less picky if we have fewer */
do {
count = determine_display_tasks(tchart, thresh);
if (process_filter)
count = determine_display_tasks_filtered(tchart);
else if (tchart->io_events)
count = determine_display_io_tasks(tchart, thresh);
else
count = determine_display_tasks(tchart, thresh);
thresh /= 10;
} while (!process_filter && thresh && count < tchart->proc_num);
if (!tchart->proc_num)
count = 0;
open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
if (tchart->io_events) {
open_svg(filename, 0, count, tchart->first_time, tchart->last_time);
svg_time_grid();
svg_legenda();
svg_time_grid(0.5);
svg_io_legenda();
for (i = 0; i < tchart->numcpus; i++)
svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
draw_io_bars(tchart);
} else {
open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
draw_cpu_usage(tchart);
if (tchart->proc_num)
draw_process_bars(tchart);
if (!tchart->tasks_only)
draw_c_p_states(tchart);
if (tchart->proc_num)
draw_wakeups(tchart);
svg_time_grid(0);
svg_legenda();
for (i = 0; i < tchart->numcpus; i++)
svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
draw_cpu_usage(tchart);
if (tchart->proc_num)
draw_process_bars(tchart);
if (!tchart->tasks_only)
draw_c_p_states(tchart);
if (tchart->proc_num)
draw_wakeups(tchart);
}
svg_close();
}
@ -1110,6 +1544,56 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
{ "power:power_end", process_sample_power_end },
{ "power:power_frequency", process_sample_power_frequency },
#endif
{ "syscalls:sys_enter_read", process_enter_read },
{ "syscalls:sys_enter_pread64", process_enter_read },
{ "syscalls:sys_enter_readv", process_enter_read },
{ "syscalls:sys_enter_preadv", process_enter_read },
{ "syscalls:sys_enter_write", process_enter_write },
{ "syscalls:sys_enter_pwrite64", process_enter_write },
{ "syscalls:sys_enter_writev", process_enter_write },
{ "syscalls:sys_enter_pwritev", process_enter_write },
{ "syscalls:sys_enter_sync", process_enter_sync },
{ "syscalls:sys_enter_sync_file_range", process_enter_sync },
{ "syscalls:sys_enter_fsync", process_enter_sync },
{ "syscalls:sys_enter_msync", process_enter_sync },
{ "syscalls:sys_enter_recvfrom", process_enter_rx },
{ "syscalls:sys_enter_recvmmsg", process_enter_rx },
{ "syscalls:sys_enter_recvmsg", process_enter_rx },
{ "syscalls:sys_enter_sendto", process_enter_tx },
{ "syscalls:sys_enter_sendmsg", process_enter_tx },
{ "syscalls:sys_enter_sendmmsg", process_enter_tx },
{ "syscalls:sys_enter_epoll_pwait", process_enter_poll },
{ "syscalls:sys_enter_epoll_wait", process_enter_poll },
{ "syscalls:sys_enter_poll", process_enter_poll },
{ "syscalls:sys_enter_ppoll", process_enter_poll },
{ "syscalls:sys_enter_pselect6", process_enter_poll },
{ "syscalls:sys_enter_select", process_enter_poll },
{ "syscalls:sys_exit_read", process_exit_read },
{ "syscalls:sys_exit_pread64", process_exit_read },
{ "syscalls:sys_exit_readv", process_exit_read },
{ "syscalls:sys_exit_preadv", process_exit_read },
{ "syscalls:sys_exit_write", process_exit_write },
{ "syscalls:sys_exit_pwrite64", process_exit_write },
{ "syscalls:sys_exit_writev", process_exit_write },
{ "syscalls:sys_exit_pwritev", process_exit_write },
{ "syscalls:sys_exit_sync", process_exit_sync },
{ "syscalls:sys_exit_sync_file_range", process_exit_sync },
{ "syscalls:sys_exit_fsync", process_exit_sync },
{ "syscalls:sys_exit_msync", process_exit_sync },
{ "syscalls:sys_exit_recvfrom", process_exit_rx },
{ "syscalls:sys_exit_recvmmsg", process_exit_rx },
{ "syscalls:sys_exit_recvmsg", process_exit_rx },
{ "syscalls:sys_exit_sendto", process_exit_tx },
{ "syscalls:sys_exit_sendmsg", process_exit_tx },
{ "syscalls:sys_exit_sendmmsg", process_exit_tx },
{ "syscalls:sys_exit_epoll_pwait", process_exit_poll },
{ "syscalls:sys_exit_epoll_wait", process_exit_poll },
{ "syscalls:sys_exit_poll", process_exit_poll },
{ "syscalls:sys_exit_ppoll", process_exit_poll },
{ "syscalls:sys_exit_pselect6", process_exit_poll },
{ "syscalls:sys_exit_select", process_exit_poll },
};
struct perf_data_file file = {
.path = input_name,
@ -1154,6 +1638,139 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
return ret;
}
static int timechart__io_record(int argc, const char **argv)
{
unsigned int rec_argc, i;
const char **rec_argv;
const char **p;
char *filter = NULL;
const char * const common_args[] = {
"record", "-a", "-R", "-c", "1",
};
unsigned int common_args_nr = ARRAY_SIZE(common_args);
const char * const disk_events[] = {
"syscalls:sys_enter_read",
"syscalls:sys_enter_pread64",
"syscalls:sys_enter_readv",
"syscalls:sys_enter_preadv",
"syscalls:sys_enter_write",
"syscalls:sys_enter_pwrite64",
"syscalls:sys_enter_writev",
"syscalls:sys_enter_pwritev",
"syscalls:sys_enter_sync",
"syscalls:sys_enter_sync_file_range",
"syscalls:sys_enter_fsync",
"syscalls:sys_enter_msync",
"syscalls:sys_exit_read",
"syscalls:sys_exit_pread64",
"syscalls:sys_exit_readv",
"syscalls:sys_exit_preadv",
"syscalls:sys_exit_write",
"syscalls:sys_exit_pwrite64",
"syscalls:sys_exit_writev",
"syscalls:sys_exit_pwritev",
"syscalls:sys_exit_sync",
"syscalls:sys_exit_sync_file_range",
"syscalls:sys_exit_fsync",
"syscalls:sys_exit_msync",
};
unsigned int disk_events_nr = ARRAY_SIZE(disk_events);
const char * const net_events[] = {
"syscalls:sys_enter_recvfrom",
"syscalls:sys_enter_recvmmsg",
"syscalls:sys_enter_recvmsg",
"syscalls:sys_enter_sendto",
"syscalls:sys_enter_sendmsg",
"syscalls:sys_enter_sendmmsg",
"syscalls:sys_exit_recvfrom",
"syscalls:sys_exit_recvmmsg",
"syscalls:sys_exit_recvmsg",
"syscalls:sys_exit_sendto",
"syscalls:sys_exit_sendmsg",
"syscalls:sys_exit_sendmmsg",
};
unsigned int net_events_nr = ARRAY_SIZE(net_events);
const char * const poll_events[] = {
"syscalls:sys_enter_epoll_pwait",
"syscalls:sys_enter_epoll_wait",
"syscalls:sys_enter_poll",
"syscalls:sys_enter_ppoll",
"syscalls:sys_enter_pselect6",
"syscalls:sys_enter_select",
"syscalls:sys_exit_epoll_pwait",
"syscalls:sys_exit_epoll_wait",
"syscalls:sys_exit_poll",
"syscalls:sys_exit_ppoll",
"syscalls:sys_exit_pselect6",
"syscalls:sys_exit_select",
};
unsigned int poll_events_nr = ARRAY_SIZE(poll_events);
rec_argc = common_args_nr +
disk_events_nr * 4 +
net_events_nr * 4 +
poll_events_nr * 4 +
argc;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (rec_argv == NULL)
return -ENOMEM;
if (asprintf(&filter, "common_pid != %d", getpid()) < 0)
return -ENOMEM;
p = rec_argv;
for (i = 0; i < common_args_nr; i++)
*p++ = strdup(common_args[i]);
for (i = 0; i < disk_events_nr; i++) {
if (!is_valid_tracepoint(disk_events[i])) {
rec_argc -= 4;
continue;
}
*p++ = "-e";
*p++ = strdup(disk_events[i]);
*p++ = "--filter";
*p++ = filter;
}
for (i = 0; i < net_events_nr; i++) {
if (!is_valid_tracepoint(net_events[i])) {
rec_argc -= 4;
continue;
}
*p++ = "-e";
*p++ = strdup(net_events[i]);
*p++ = "--filter";
*p++ = filter;
}
for (i = 0; i < poll_events_nr; i++) {
if (!is_valid_tracepoint(poll_events[i])) {
rec_argc -= 4;
continue;
}
*p++ = "-e";
*p++ = strdup(poll_events[i]);
*p++ = "--filter";
*p++ = filter;
}
for (i = 0; i < (unsigned int)argc; i++)
*p++ = argv[i];
return cmd_record(rec_argc, rec_argv, NULL);
}
static int timechart__record(struct timechart *tchart, int argc, const char **argv)
{
unsigned int rec_argc, i, j;
@ -1270,6 +1887,30 @@ parse_highlight(const struct option *opt __maybe_unused, const char *arg,
return 0;
}
static int
parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
{
char unit = 'n';
u64 *value = opt->value;
if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) {
switch (unit) {
case 'm':
*value *= 1000000;
break;
case 'u':
*value *= 1000;
break;
case 'n':
break;
default:
return -1;
}
}
return 0;
}
int cmd_timechart(int argc, const char **argv,
const char *prefix __maybe_unused)
{
@ -1282,6 +1923,8 @@ int cmd_timechart(int argc, const char **argv,
.ordered_samples = true,
},
.proc_num = 15,
.min_time = 1000000,
.merge_dist = 1000,
};
const char *output_name = "output.svg";
const struct option timechart_options[] = {
@ -1303,6 +1946,14 @@ int cmd_timechart(int argc, const char **argv,
"min. number of tasks to print"),
OPT_BOOLEAN('t', "topology", &tchart.topology,
"sort CPUs according to topology"),
OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain,
"skip EAGAIN errors"),
OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time",
"all IO faster than min-time will visually appear longer",
parse_time),
OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time",
"merge events that are merge-dist us apart",
parse_time),
OPT_END()
};
const char * const timechart_usage[] = {
@ -1314,6 +1965,8 @@ int cmd_timechart(int argc, const char **argv,
OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
"output processes data only"),
OPT_BOOLEAN('I', "io-only", &tchart.io_only,
"record only IO data"),
OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
OPT_END()
};
@ -1340,7 +1993,10 @@ int cmd_timechart(int argc, const char **argv,
return -1;
}
return timechart__record(&tchart, argc, argv);
if (tchart.io_only)
return timechart__io_record(argc, argv);
else
return timechart__record(&tchart, argc, argv);
} else if (argc)
usage_with_options(timechart_usage, timechart_options);

View File

@ -1108,6 +1108,7 @@ struct syscall {
struct event_format *tp_format;
const char *name;
bool filtered;
bool is_exit;
struct syscall_fmt *fmt;
size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg);
void **arg_parm;
@ -1132,6 +1133,7 @@ struct thread_trace {
u64 exit_time;
bool entry_pending;
unsigned long nr_events;
unsigned long pfmaj, pfmin;
char *entry_str;
double runtime_ms;
struct {
@ -1177,6 +1179,9 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
return NULL;
}
#define TRACE_PFMAJ (1 << 0)
#define TRACE_PFMIN (1 << 1)
struct trace {
struct perf_tool tool;
struct {
@ -1211,6 +1216,8 @@ struct trace {
bool summary_only;
bool show_comm;
bool show_tool_stats;
bool trace_syscalls;
int trace_pgfaults;
};
static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname)
@ -1276,11 +1283,11 @@ static const char *thread__fd_path(struct thread *thread, int fd,
if (fd < 0)
return NULL;
if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL))
if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) {
if (!trace->live)
return NULL;
++trace->stats.proc_getname;
if (thread__read_fd_path(thread, fd)) {
if (thread__read_fd_path(thread, fd))
return NULL;
}
@ -1473,6 +1480,8 @@ static int trace__read_syscall_info(struct trace *trace, int id)
if (sc->tp_format == NULL)
return -1;
sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
return syscall__set_arg_fmts(sc);
}
@ -1535,6 +1544,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
}
typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event,
struct perf_sample *sample);
static struct syscall *trace__syscall_info(struct trace *trace,
@ -1607,6 +1617,7 @@ static void thread__update_stats(struct thread_trace *ttrace,
}
static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
char *msg;
@ -1629,7 +1640,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
return -1;
args = perf_evsel__sc_tp_ptr(evsel, args, sample);
ttrace = thread->priv;
if (ttrace->entry_str == NULL) {
ttrace->entry_str = malloc(1024);
@ -1644,7 +1654,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed,
args, trace, thread);
if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) {
if (sc->is_exit) {
if (!trace->duration_filter && !trace->summary_only) {
trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
fprintf(trace->output, "%-70s\n", ttrace->entry_str);
@ -1656,6 +1666,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
}
static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
int ret;
@ -1687,8 +1698,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
++trace->stats.vfs_getname;
}
ttrace = thread->priv;
ttrace->exit_time = sample->time;
if (ttrace->entry_time) {
@ -1735,6 +1744,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
}
static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname");
@ -1742,6 +1752,7 @@ static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
}
static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
@ -1768,6 +1779,80 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs
return 0;
}
static void print_location(FILE *f, struct perf_sample *sample,
struct addr_location *al,
bool print_dso, bool print_sym)
{
if ((verbose || print_dso) && al->map)
fprintf(f, "%s@", al->map->dso->long_name);
if ((verbose || print_sym) && al->sym)
fprintf(f, "%s+0x%" PRIx64, al->sym->name,
al->addr - al->sym->start);
else if (al->map)
fprintf(f, "0x%" PRIx64, al->addr);
else
fprintf(f, "0x%" PRIx64, sample->addr);
}
static int trace__pgfault(struct trace *trace,
struct perf_evsel *evsel,
union perf_event *event,
struct perf_sample *sample)
{
struct thread *thread;
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
struct addr_location al;
char map_type = 'd';
struct thread_trace *ttrace;
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
ttrace = thread__trace(thread, trace->output);
if (ttrace == NULL)
return -1;
if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)
ttrace->pfmaj++;
else
ttrace->pfmin++;
if (trace->summary_only)
return 0;
thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION,
sample->ip, &al);
trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output);
fprintf(trace->output, "%sfault [",
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
"maj" : "min");
print_location(trace->output, sample, &al, false, true);
fprintf(trace->output, "] => ");
thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE,
sample->addr, &al);
if (!al.map) {
thread__find_addr_location(thread, trace->host, cpumode,
MAP__FUNCTION, sample->addr, &al);
if (al.map)
map_type = 'x';
else
map_type = '?';
}
print_location(trace->output, sample, &al, true, false);
fprintf(trace->output, " (%c%c)\n", map_type, al.level);
return 0;
}
static bool skip_sample(struct trace *trace, struct perf_sample *sample)
{
if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) ||
@ -1781,7 +1866,7 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample)
}
static int trace__process_sample(struct perf_tool *tool,
union perf_event *event __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine __maybe_unused)
@ -1799,7 +1884,7 @@ static int trace__process_sample(struct perf_tool *tool,
if (handler) {
++trace->nr_events;
handler(trace, evsel, sample);
handler(trace, evsel, event, sample);
}
return err;
@ -1826,7 +1911,7 @@ static int parse_target_str(struct trace *trace)
return 0;
}
static int trace__record(int argc, const char **argv)
static int trace__record(struct trace *trace, int argc, const char **argv)
{
unsigned int rec_argc, i, j;
const char **rec_argv;
@ -1835,34 +1920,54 @@ static int trace__record(int argc, const char **argv)
"-R",
"-m", "1024",
"-c", "1",
"-e",
};
const char * const sc_args[] = { "-e", };
unsigned int sc_args_nr = ARRAY_SIZE(sc_args);
const char * const majpf_args[] = { "-e", "major-faults" };
unsigned int majpf_args_nr = ARRAY_SIZE(majpf_args);
const char * const minpf_args[] = { "-e", "minor-faults" };
unsigned int minpf_args_nr = ARRAY_SIZE(minpf_args);
/* +1 is for the event string below */
rec_argc = ARRAY_SIZE(record_args) + 1 + argc;
rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 1 +
majpf_args_nr + minpf_args_nr + argc;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (rec_argv == NULL)
return -ENOMEM;
j = 0;
for (i = 0; i < ARRAY_SIZE(record_args); i++)
rec_argv[i] = record_args[i];
rec_argv[j++] = record_args[i];
/* event string may be different for older kernels - e.g., RHEL6 */
if (is_valid_tracepoint("raw_syscalls:sys_enter"))
rec_argv[i] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit";
else if (is_valid_tracepoint("syscalls:sys_enter"))
rec_argv[i] = "syscalls:sys_enter,syscalls:sys_exit";
else {
pr_err("Neither raw_syscalls nor syscalls events exist.\n");
return -1;
if (trace->trace_syscalls) {
for (i = 0; i < sc_args_nr; i++)
rec_argv[j++] = sc_args[i];
/* event string may be different for older kernels - e.g., RHEL6 */
if (is_valid_tracepoint("raw_syscalls:sys_enter"))
rec_argv[j++] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit";
else if (is_valid_tracepoint("syscalls:sys_enter"))
rec_argv[j++] = "syscalls:sys_enter,syscalls:sys_exit";
else {
pr_err("Neither raw_syscalls nor syscalls events exist.\n");
return -1;
}
}
i++;
for (j = 0; j < (unsigned int)argc; j++, i++)
rec_argv[i] = argv[j];
if (trace->trace_pgfaults & TRACE_PFMAJ)
for (i = 0; i < majpf_args_nr; i++)
rec_argv[j++] = majpf_args[i];
return cmd_record(i, rec_argv, NULL);
if (trace->trace_pgfaults & TRACE_PFMIN)
for (i = 0; i < minpf_args_nr; i++)
rec_argv[j++] = minpf_args[i];
for (i = 0; i < (unsigned int)argc; i++)
rec_argv[j++] = argv[i];
return cmd_record(j, rec_argv, NULL);
}
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
@ -1882,6 +1987,30 @@ static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
perf_evlist__add(evlist, evsel);
}
static int perf_evlist__add_pgfault(struct perf_evlist *evlist,
u64 config)
{
struct perf_evsel *evsel;
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.mmap_data = 1,
};
attr.config = config;
attr.sample_period = 1;
event_attr_init(&attr);
evsel = perf_evsel__new(&attr);
if (!evsel)
return -ENOMEM;
evsel->handler = trace__pgfault;
perf_evlist__add(evlist, evsel);
return 0;
}
static int trace__run(struct trace *trace, int argc, const char **argv)
{
struct perf_evlist *evlist = perf_evlist__new();
@ -1897,10 +2026,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out;
}
if (perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, trace__sys_exit))
if (trace->trace_syscalls &&
perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
trace__sys_exit))
goto out_error_tp;
perf_evlist__add_vfs_getname(evlist);
if (trace->trace_syscalls)
perf_evlist__add_vfs_getname(evlist);
if ((trace->trace_pgfaults & TRACE_PFMAJ) &&
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ))
goto out_error_tp;
if ((trace->trace_pgfaults & TRACE_PFMIN) &&
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN))
goto out_error_tp;
if (trace->sched &&
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
@ -1982,7 +2122,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto next_event;
}
if (sample.raw_data == NULL) {
if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
sample.raw_data == NULL) {
fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
perf_evsel__name(evsel), sample.tid,
sample.cpu, sample.raw_size);
@ -1990,7 +2131,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
}
handler = evsel->handler;
handler(trace, evsel, &sample);
handler(trace, evsel, event, &sample);
next_event:
perf_evlist__mmap_consume(evlist, i);
@ -2093,13 +2234,10 @@ static int trace__replay(struct trace *trace)
if (evsel == NULL)
evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
"syscalls:sys_enter");
if (evsel == NULL) {
pr_err("Data file does not have raw_syscalls:sys_enter event\n");
goto out;
}
if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 ||
perf_evsel__init_sc_tp_ptr_field(evsel, args)) {
if (evsel &&
(perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 ||
perf_evsel__init_sc_tp_ptr_field(evsel, args))) {
pr_err("Error during initialize raw_syscalls:sys_enter event\n");
goto out;
}
@ -2109,15 +2247,19 @@ static int trace__replay(struct trace *trace)
if (evsel == NULL)
evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
"syscalls:sys_exit");
if (evsel == NULL) {
pr_err("Data file does not have raw_syscalls:sys_exit event\n");
if (evsel &&
(perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 ||
perf_evsel__init_sc_tp_uint_field(evsel, ret))) {
pr_err("Error during initialize raw_syscalls:sys_exit event\n");
goto out;
}
if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 ||
perf_evsel__init_sc_tp_uint_field(evsel, ret)) {
pr_err("Error during initialize raw_syscalls:sys_exit event\n");
goto out;
evlist__for_each(session->evlist, evsel) {
if (evsel->attr.type == PERF_TYPE_SOFTWARE &&
(evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ||
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN ||
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS))
evsel->handler = trace__pgfault;
}
err = parse_target_str(trace);
@ -2217,6 +2359,10 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid);
printed += fprintf(fp, "%lu events, ", ttrace->nr_events);
printed += fprintf(fp, "%.1f%%", ratio);
if (ttrace->pfmaj)
printed += fprintf(fp, ", %lu majfaults", ttrace->pfmaj);
if (ttrace->pfmin)
printed += fprintf(fp, ", %lu minfaults", ttrace->pfmin);
printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
printed += thread__dump_stats(ttrace, trace, fp);
@ -2264,6 +2410,23 @@ static int trace__open_output(struct trace *trace, const char *filename)
return trace->output == NULL ? -errno : 0;
}
static int parse_pagefaults(const struct option *opt, const char *str,
int unset __maybe_unused)
{
int *trace_pgfaults = opt->value;
if (strcmp(str, "all") == 0)
*trace_pgfaults |= TRACE_PFMAJ | TRACE_PFMIN;
else if (strcmp(str, "maj") == 0)
*trace_pgfaults |= TRACE_PFMAJ;
else if (strcmp(str, "min") == 0)
*trace_pgfaults |= TRACE_PFMIN;
else
return -1;
return 0;
}
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
{
const char * const trace_usage[] = {
@ -2293,6 +2456,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
},
.output = stdout,
.show_comm = true,
.trace_syscalls = true,
};
const char *output_name = NULL;
const char *ev_qualifier_str = NULL;
@ -2330,20 +2494,34 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
"Show only syscall summary with statistics"),
OPT_BOOLEAN('S', "with-summary", &trace.summary,
"Show all syscalls and summary with statistics"),
OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min",
"Trace pagefaults", parse_pagefaults, "maj"),
OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"),
OPT_END()
};
int err;
char bf[BUFSIZ];
if ((argc > 1) && (strcmp(argv[1], "record") == 0))
return trace__record(argc-2, &argv[2]);
argc = parse_options(argc, argv, trace_options, trace_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
argc = parse_options(argc, argv, trace_options, trace_usage, 0);
if (trace.trace_pgfaults) {
trace.opts.sample_address = true;
trace.opts.sample_time = true;
}
if ((argc >= 1) && (strcmp(argv[0], "record") == 0))
return trace__record(&trace, argc-1, &argv[1]);
/* summary_only implies summary option, but don't overwrite summary if set */
if (trace.summary_only)
trace.summary = trace.summary_only;
if (!trace.trace_syscalls && !trace.trace_pgfaults) {
pr_err("Please specify something to trace.\n");
return -1;
}
if (output_name != NULL) {
err = trace__open_output(&trace, output_name);
if (err < 0) {

View File

@ -48,6 +48,10 @@ ifneq ($(ARCH),$(filter $(ARCH),x86 arm))
NO_LIBDW_DWARF_UNWIND := 1
endif
ifeq ($(ARCH),powerpc)
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
endif
ifeq ($(LIBUNWIND_LIBS),)
NO_LIBUNWIND := 1
else
@ -160,6 +164,7 @@ CORE_FEATURE_TESTS = \
backtrace \
dwarf \
fortify-source \
sync-compare-and-swap \
glibc \
gtk2 \
gtk2-infobar \
@ -195,6 +200,7 @@ LIB_FEATURE_TESTS = \
VF_FEATURE_TESTS = \
backtrace \
fortify-source \
sync-compare-and-swap \
gtk2-infobar \
libelf-getphdrnum \
libelf-mmap \
@ -268,6 +274,10 @@ CFLAGS += -I$(LIB_INCLUDE)
CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
ifeq ($(feature-sync-compare-and-swap), 1)
CFLAGS += -DHAVE_SYNC_COMPARE_AND_SWAP_SUPPORT
endif
ifndef NO_BIONIC
$(call feature_check,bionic)
ifeq ($(feature-bionic), 1)
@ -590,6 +600,10 @@ ifndef NO_LIBNUMA
endif
endif
ifdef HAVE_KVM_STAT_SUPPORT
CFLAGS += -DHAVE_KVM_STAT_SUPPORT
endif
# Among the variables below, these:
# perfexecdir
# template_dir

View File

@ -5,6 +5,7 @@ FILES= \
test-bionic.bin \
test-dwarf.bin \
test-fortify-source.bin \
test-sync-compare-and-swap.bin \
test-glibc.bin \
test-gtk2.bin \
test-gtk2-infobar.bin \
@ -141,6 +142,9 @@ test-timerfd.bin:
test-libdw-dwarf-unwind.bin:
$(BUILD)
test-sync-compare-and-swap.bin:
$(BUILD) -Werror
-include *.d
###############################

View File

@ -89,6 +89,10 @@
# include "test-libdw-dwarf-unwind.c"
#undef main
#define main main_test_sync_compare_and_swap
# include "test-sync-compare-and-swap.c"
#undef main
int main(int argc, char *argv[])
{
main_test_libpython();
@ -111,6 +115,7 @@ int main(int argc, char *argv[])
main_test_timerfd();
main_test_stackprotector_all();
main_test_libdw_dwarf_unwind();
main_test_sync_compare_and_swap(argc, argv);
return 0;
}

View File

@ -0,0 +1,14 @@
#include <stdint.h>
volatile uint64_t x;
int main(int argc, char *argv[])
{
uint64_t old, new = argc;
argv = argv;
do {
old = __sync_val_compare_and_swap(&x, 0, 0);
} while (!__sync_bool_compare_and_swap(&x, old, new));
return old == new;
}

View File

@ -54,6 +54,7 @@
#define mb() asm volatile("bcr 15,0" ::: "memory")
#define wmb() asm volatile("bcr 15,0" ::: "memory")
#define rmb() asm volatile("bcr 15,0" ::: "memory")
#define CPUINFO_PROC "vendor_id"
#endif
#ifdef __sh__

View File

@ -13,11 +13,12 @@
#include "util/quote.h"
#include "util/run-command.h"
#include "util/parse-events.h"
#include "util/debug.h"
#include <api/fs/debugfs.h>
#include <pthread.h>
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
"perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
const char perf_more_info_string[] =
"See 'perf help COMMAND' for more information on a specific command.";
@ -212,6 +213,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
printf("%s ", p->cmd);
}
exit(0);
} else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) {
fprintf(stderr, "No variable specified for --debug.\n");
usage(perf_usage_string);
}
if (perf_debug_option((*argv)[1]))
usage(perf_usage_string);
(*argv)++;
(*argc)--;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(perf_usage_string);

View File

@ -1,2 +1,3 @@
#!/bin/bash
perf record -e raw_syscalls:sys_exit $@
(perf record -e raw_syscalls:sys_exit $@ || \
perf record -e syscalls:sys_exit $@) 2> /dev/null

View File

@ -26,6 +26,11 @@ sub raw_syscalls::sys_exit
}
}
sub syscalls::sys_exit
{
raw_syscalls::sys_exit(@_)
}
sub trace_end
{
printf("\nfailed syscalls by comm:\n\n");

View File

@ -107,12 +107,13 @@ def taskState(state):
class EventHeaders:
def __init__(self, common_cpu, common_secs, common_nsecs,
common_pid, common_comm):
common_pid, common_comm, common_callchain):
self.cpu = common_cpu
self.secs = common_secs
self.nsecs = common_nsecs
self.pid = common_pid
self.comm = common_comm
self.callchain = common_callchain
def ts(self):
return (self.secs * (10 ** 9)) + self.nsecs

View File

@ -1,2 +1,3 @@
#!/bin/bash
perf record -e raw_syscalls:sys_exit $@
(perf record -e raw_syscalls:sys_exit $@ || \
perf record -e syscalls:sys_exit $@) 2> /dev/null

View File

@ -1,2 +1,3 @@
#!/bin/bash
perf record -e raw_syscalls:sys_enter $@
(perf record -e raw_syscalls:sys_enter $@ || \
perf record -e syscalls:sys_enter $@) 2> /dev/null

View File

@ -1,2 +1,3 @@
#!/bin/bash
perf record -e raw_syscalls:sys_enter $@
(perf record -e raw_syscalls:sys_enter $@ || \
perf record -e syscalls:sys_enter $@) 2> /dev/null

View File

@ -1,2 +1,3 @@
#!/bin/bash
perf record -e raw_syscalls:sys_enter $@
(perf record -e raw_syscalls:sys_enter $@ || \
perf record -e syscalls:sys_enter $@) 2> /dev/null

View File

@ -27,7 +27,7 @@ def trace_end():
def irq__softirq_entry(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
vec):
common_callchain, vec):
print_header(event_name, common_cpu, common_secs, common_nsecs,
common_pid, common_comm)
@ -38,7 +38,7 @@ def irq__softirq_entry(event_name, context, common_cpu,
def kmem__kmalloc(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
call_site, ptr, bytes_req, bytes_alloc,
common_callchain, call_site, ptr, bytes_req, bytes_alloc,
gfp_flags):
print_header(event_name, common_cpu, common_secs, common_nsecs,
common_pid, common_comm)

View File

@ -39,7 +39,7 @@ def trace_end():
def raw_syscalls__sys_exit(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, ret):
common_callchain, id, ret):
if (for_comm and common_comm != for_comm) or \
(for_pid and common_pid != for_pid ):
return
@ -50,6 +50,11 @@ def raw_syscalls__sys_exit(event_name, context, common_cpu,
except TypeError:
syscalls[common_comm][common_pid][id][ret] = 1
def syscalls__sys_exit(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, ret):
raw_syscalls__sys_exit(**locals())
def print_error_totals():
if for_comm is not None:
print "\nsyscall errors for %s:\n\n" % (for_comm),

View File

@ -21,7 +21,7 @@ thread_blocktime = {}
lock_waits = {} # long-lived stats on (tid,lock) blockage elapsed time
process_names = {} # long-lived pid-to-execname mapping
def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm,
def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, callchain,
nr, uaddr, op, val, utime, uaddr2, val3):
cmd = op & FUTEX_CMD_MASK
if cmd != FUTEX_WAIT:
@ -31,7 +31,7 @@ def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm,
thread_thislock[tid] = uaddr
thread_blocktime[tid] = nsecs(s, ns)
def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm,
def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm, callchain,
nr, ret):
if thread_blocktime.has_key(tid):
elapsed = nsecs(s, ns) - thread_blocktime[tid]

View File

@ -66,7 +66,7 @@ def trace_end():
print_drop_table()
# called from perf, when it finds a correspoinding event
def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain,
skbaddr, location, protocol):
slocation = str(location)
try:

View File

@ -224,75 +224,75 @@ def trace_end():
(len(rx_skb_list), of_count_rx_skb_list)
# called from perf, when it finds a correspoinding event
def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec):
def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, callchain, vec):
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
return
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
all_event_list.append(event_info)
def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec):
def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, callchain, vec):
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
return
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
all_event_list.append(event_info)
def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec):
def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, callchain, vec):
if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
return
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
all_event_list.append(event_info)
def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm,
irq, irq_name):
callchain, irq, irq_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
irq, irq_name)
all_event_list.append(event_info)
def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret):
def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, callchain, irq, ret):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret)
all_event_list.append(event_info)
def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name):
def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, callchain, napi, dev_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
napi, dev_name)
all_event_list.append(event_info)
def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr,
def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr,
skblen, dev_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, skblen, dev_name)
all_event_list.append(event_info)
def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr,
def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr,
skblen, dev_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, skblen, dev_name)
all_event_list.append(event_info)
def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm,
def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm, callchain,
skbaddr, skblen, dev_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, skblen, dev_name)
all_event_list.append(event_info)
def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm,
def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm, callchain,
skbaddr, skblen, rc, dev_name):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, skblen, rc ,dev_name)
all_event_list.append(event_info)
def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain,
skbaddr, protocol, location):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, protocol, location)
all_event_list.append(event_info)
def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr):
def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr)
all_event_list.append(event_info)
def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,
def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm, callchain,
skbaddr, skblen):
event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
skbaddr, skblen)

View File

@ -369,93 +369,92 @@ def trace_end():
def sched__sched_stat_runtime(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, runtime, vruntime):
common_callchain, comm, pid, runtime, vruntime):
pass
def sched__sched_stat_iowait(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, delay):
common_callchain, comm, pid, delay):
pass
def sched__sched_stat_sleep(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, delay):
common_callchain, comm, pid, delay):
pass
def sched__sched_stat_wait(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, delay):
common_callchain, comm, pid, delay):
pass
def sched__sched_process_fork(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
parent_comm, parent_pid, child_comm, child_pid):
common_callchain, parent_comm, parent_pid, child_comm, child_pid):
pass
def sched__sched_process_wait(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio):
common_callchain, comm, pid, prio):
pass
def sched__sched_process_exit(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio):
common_callchain, comm, pid, prio):
pass
def sched__sched_process_free(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio):
common_callchain, comm, pid, prio):
pass
def sched__sched_migrate_task(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio, orig_cpu,
common_callchain, comm, pid, prio, orig_cpu,
dest_cpu):
headers = EventHeaders(common_cpu, common_secs, common_nsecs,
common_pid, common_comm)
common_pid, common_comm, common_callchain)
parser.migrate(headers, pid, prio, orig_cpu, dest_cpu)
def sched__sched_switch(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
common_secs, common_nsecs, common_pid, common_comm, common_callchain,
prev_comm, prev_pid, prev_prio, prev_state,
next_comm, next_pid, next_prio):
headers = EventHeaders(common_cpu, common_secs, common_nsecs,
common_pid, common_comm)
common_pid, common_comm, common_callchain)
parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state,
next_comm, next_pid, next_prio)
def sched__sched_wakeup_new(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio, success,
common_callchain, comm, pid, prio, success,
target_cpu):
headers = EventHeaders(common_cpu, common_secs, common_nsecs,
common_pid, common_comm)
common_pid, common_comm, common_callchain)
parser.wake_up(headers, comm, pid, success, target_cpu, 1)
def sched__sched_wakeup(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio, success,
common_callchain, comm, pid, prio, success,
target_cpu):
headers = EventHeaders(common_cpu, common_secs, common_nsecs,
common_pid, common_comm)
common_pid, common_comm, common_callchain)
parser.wake_up(headers, comm, pid, success, target_cpu, 0)
def sched__sched_wait_task(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio):
common_callchain, comm, pid, prio):
pass
def sched__sched_kthread_stop_ret(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
ret):
common_callchain, ret):
pass
def sched__sched_kthread_stop(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid):
common_callchain, comm, pid):
pass
def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
common_pid, common_comm):
def trace_unhandled(event_name, context, event_fields_dict):
pass

View File

@ -44,7 +44,7 @@ def trace_begin():
def raw_syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
common_callchain, id, args):
if for_comm is not None:
if common_comm != for_comm:
return
@ -53,6 +53,11 @@ def raw_syscalls__sys_enter(event_name, context, common_cpu,
except TypeError:
syscalls[id] = 1
def syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
raw_syscalls__sys_enter(**locals())
def print_syscall_totals(interval):
while 1:
clear_term()

View File

@ -38,7 +38,7 @@ def trace_end():
def raw_syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
common_callchain, id, args):
if (for_comm and common_comm != for_comm) or \
(for_pid and common_pid != for_pid ):
@ -48,6 +48,11 @@ def raw_syscalls__sys_enter(event_name, context, common_cpu,
except TypeError:
syscalls[common_comm][common_pid][id] = 1
def syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
raw_syscalls__sys_enter(**locals())
def print_syscall_totals():
if for_comm is not None:
print "\nsyscall events for %s:\n\n" % (for_comm),

View File

@ -35,7 +35,7 @@ def trace_end():
def raw_syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
common_callchain, id, args):
if for_comm is not None:
if common_comm != for_comm:
return
@ -44,6 +44,11 @@ def raw_syscalls__sys_enter(event_name, context, common_cpu,
except TypeError:
syscalls[id] = 1
def syscalls__sys_enter(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
id, args):
raw_syscalls__sys_enter(**locals())
def print_syscall_totals():
if for_comm is not None:
print "\nsyscall events for %s:\n\n" % (for_comm),

View File

@ -1,7 +1,8 @@
[event]
fd=1
group_fd=-1
flags=0
# 0 or PERF_FLAG_FD_CLOEXEC flag
flags=0|8
cpu=*
type=0|1
size=96

View File

@ -1,7 +1,8 @@
[event]
fd=1
group_fd=-1
flags=0
# 0 or PERF_FLAG_FD_CLOEXEC flag
flags=0|8
cpu=*
type=0
size=96

View File

@ -25,6 +25,7 @@
#include "tests.h"
#include "debug.h"
#include "perf.h"
#include "cloexec.h"
static int fd1;
static int fd2;
@ -78,7 +79,8 @@ static int bp_event(void *fn, int setup_signal)
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
fd = sys_perf_event_open(&pe, 0, -1, -1,
perf_event_open_cloexec_flag());
if (fd < 0) {
pr_debug("failed opening event %llx\n", pe.config);
return TEST_FAIL;

View File

@ -24,6 +24,7 @@
#include "tests.h"
#include "debug.h"
#include "perf.h"
#include "cloexec.h"
static int overflows;
@ -91,7 +92,8 @@ int test__bp_signal_overflow(void)
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
fd = sys_perf_event_open(&pe, 0, -1, -1,
perf_event_open_cloexec_flag());
if (fd < 0) {
pr_debug("failed opening event %llx\n", pe.config);
return TEST_FAIL;

View File

@ -10,6 +10,7 @@
#include "machine.h"
#include "symbol.h"
#include "tests.h"
#include "debug.h"
static char *test_file(int size)
{

View File

@ -2,6 +2,7 @@
#include "evsel.h"
#include "parse-events.h"
#include "tests.h"
#include "debug.h"
static int perf_evsel__roundtrip_cache_name_test(void)
{

View File

@ -1,6 +1,7 @@
#include <traceevent/event-parse.h>
#include "evsel.h"
#include "tests.h"
#include "debug.h"
static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
int size, bool should_be_signed)

View File

@ -3,6 +3,7 @@
#include "evsel.h"
#include "thread_map.h"
#include "tests.h"
#include "debug.h"
int test__syscall_open_tp_fields(void)
{

View File

@ -5,6 +5,7 @@
#include <api/fs/fs.h>
#include <api/fs/debugfs.h>
#include "tests.h"
#include "debug.h"
#include <linux/hw_breakpoint.h>
#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \

View File

@ -7,6 +7,7 @@
#include "evlist.h"
#include "header.h"
#include "util.h"
#include "debug.h"
static int process_event(struct perf_evlist **pevlist, union perf_event *event)
{

View File

@ -8,10 +8,9 @@
#include "evsel.h"
#include "thread_map.h"
#include "cpumap.h"
#include "tsc.h"
#include "tests.h"
#include "../arch/x86/util/tsc.h"
#define CHECK__(x) { \
while ((x) < 0) { \
pr_debug(#x " failed!\n"); \
@ -26,15 +25,6 @@
} \
}
static u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}
/**
* test__perf_time_to_tsc - test converting perf time to TSC.
*

View File

@ -6,6 +6,7 @@
#include "perf.h"
#include "debug.h"
#include "tests.h"
#include "cloexec.h"
#if defined(__x86_64__) || defined(__i386__)
@ -104,7 +105,8 @@ static int __test__rdpmc(void)
sa.sa_sigaction = segfault_handler;
sigaction(SIGSEGV, &sa, NULL);
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
fd = sys_perf_event_open(&attr, 0, -1, -1,
perf_event_open_cloexec_flag());
if (fd < 0) {
pr_err("Error: sys_perf_event_open() syscall returned "
"with %d (%s)\n", fd, strerror(errno));

View File

@ -4,6 +4,7 @@
#include "util.h"
#include "event.h"
#include "evsel.h"
#include "debug.h"
#include "tests.h"

View File

@ -2,6 +2,7 @@
#include "machine.h"
#include "thread.h"
#include "map.h"
#include "debug.h"
int test__thread_mg_share(void)
{

View File

@ -150,7 +150,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
while (nd != NULL) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, nd, row);
if (++row == browser->height)
if (++row == browser->rows)
break;
nd = rb_next(nd);
}
@ -166,7 +166,7 @@ bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
void ui_browser__refresh_dimensions(struct ui_browser *browser)
{
browser->width = SLtt_Screen_Cols - 1;
browser->height = SLtt_Screen_Rows - 2;
browser->height = browser->rows = SLtt_Screen_Rows - 2;
browser->y = 1;
browser->x = 0;
}
@ -250,7 +250,10 @@ int ui_browser__show(struct ui_browser *browser, const char *title,
int err;
va_list ap;
ui_browser__refresh_dimensions(browser);
if (browser->refresh_dimensions == NULL)
browser->refresh_dimensions = ui_browser__refresh_dimensions;
browser->refresh_dimensions(browser);
pthread_mutex_lock(&ui__lock);
__ui_browser__show_title(browser, title);
@ -279,7 +282,7 @@ static void ui_browser__scrollbar_set(struct ui_browser *browser)
{
int height = browser->height, h = 0, pct = 0,
col = browser->width,
row = browser->y - 1;
row = 0;
if (browser->nr_entries > 1) {
pct = ((browser->index * (browser->height - 1)) /
@ -367,7 +370,7 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
if (key == K_RESIZE) {
ui__refresh_dimensions(false);
ui_browser__refresh_dimensions(browser);
browser->refresh_dimensions(browser);
__ui_browser__show_title(browser, browser->title);
ui_helpline__puts(browser->helpline);
continue;
@ -389,7 +392,7 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
if (browser->index == browser->nr_entries - 1)
break;
++browser->index;
if (browser->index == browser->top_idx + browser->height) {
if (browser->index == browser->top_idx + browser->rows) {
++browser->top_idx;
browser->seek(browser, +1, SEEK_CUR);
}
@ -405,10 +408,10 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
break;
case K_PGDN:
case ' ':
if (browser->top_idx + browser->height > browser->nr_entries - 1)
if (browser->top_idx + browser->rows > browser->nr_entries - 1)
break;
offset = browser->height;
offset = browser->rows;
if (browser->index + offset > browser->nr_entries - 1)
offset = browser->nr_entries - 1 - browser->index;
browser->index += offset;
@ -419,10 +422,10 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
if (browser->top_idx == 0)
break;
if (browser->top_idx < browser->height)
if (browser->top_idx < browser->rows)
offset = browser->top_idx;
else
offset = browser->height;
offset = browser->rows;
browser->index -= offset;
browser->top_idx -= offset;
@ -432,7 +435,7 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
ui_browser__reset_index(browser);
break;
case K_END:
offset = browser->height - 1;
offset = browser->rows - 1;
if (offset >= browser->nr_entries)
offset = browser->nr_entries - 1;
@ -462,7 +465,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
if (!browser->filter || !browser->filter(browser, pos)) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row);
if (++row == browser->height)
if (++row == browser->rows)
break;
}
}
@ -587,7 +590,7 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
if (!browser->filter || !browser->filter(browser, *pos)) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row);
if (++row == browser->height)
if (++row == browser->rows)
break;
}
@ -623,7 +626,7 @@ static void __ui_browser__line_arrow_up(struct ui_browser *browser,
SLsmg_set_char_set(1);
if (start < browser->top_idx + browser->height) {
if (start < browser->top_idx + browser->rows) {
row = start - browser->top_idx;
ui_browser__gotorc(browser, row, column);
SLsmg_write_char(SLSMG_LLCORN_CHAR);
@ -633,7 +636,7 @@ static void __ui_browser__line_arrow_up(struct ui_browser *browser,
if (row-- == 0)
goto out;
} else
row = browser->height - 1;
row = browser->rows - 1;
if (end > browser->top_idx)
end_row = end - browser->top_idx;
@ -675,8 +678,8 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser,
} else
row = 0;
if (end >= browser->top_idx + browser->height)
end_row = browser->height - 1;
if (end >= browser->top_idx + browser->rows)
end_row = browser->rows - 1;
else
end_row = end - browser->top_idx;
@ -684,7 +687,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser,
SLsmg_draw_vline(end_row - row + 1);
ui_browser__gotorc(browser, end_row, column);
if (end < browser->top_idx + browser->height) {
if (end < browser->top_idx + browser->rows) {
SLsmg_write_char(SLSMG_LLCORN_CHAR);
ui_browser__gotorc(browser, end_row, column + 1);
SLsmg_write_char(SLSMG_HLINE_CHAR);

View File

@ -14,11 +14,12 @@
struct ui_browser {
u64 index, top_idx;
void *top, *entries;
u16 y, x, width, height;
u16 y, x, width, height, rows;
int current_color;
void *priv;
const char *title;
char *helpline;
void (*refresh_dimensions)(struct ui_browser *browser);
unsigned int (*refresh)(struct ui_browser *browser);
void (*write)(struct ui_browser *browser, void *entry, int row);
void (*seek)(struct ui_browser *browser, off_t offset, int whence);

View File

@ -26,6 +26,7 @@ struct hist_browser {
struct map_symbol *selection;
int print_seq;
bool show_dso;
bool show_headers;
float min_pcnt;
u64 nr_non_filtered_entries;
u64 nr_callchain_rows;
@ -33,8 +34,7 @@ struct hist_browser {
extern void hist_browser__init_hpp(void);
static int hists__browser_title(struct hists *hists, char *bf, size_t size,
const char *ev_name);
static int hists__browser_title(struct hists *hists, char *bf, size_t size);
static void hist_browser__update_nr_entries(struct hist_browser *hb);
static struct rb_node *hists__filter_entries(struct rb_node *nd,
@ -57,11 +57,42 @@ static u32 hist_browser__nr_entries(struct hist_browser *hb)
return nr_entries + hb->nr_callchain_rows;
}
static void hist_browser__refresh_dimensions(struct hist_browser *browser)
static void hist_browser__update_rows(struct hist_browser *hb)
{
struct ui_browser *browser = &hb->b;
u16 header_offset = hb->show_headers ? 1 : 0, index_row;
browser->rows = browser->height - header_offset;
/*
* Verify if we were at the last line and that line isn't
* visibe because we now show the header line(s).
*/
index_row = browser->index - browser->top_idx;
if (index_row >= browser->rows)
browser->index -= index_row - browser->rows + 1;
}
static void hist_browser__refresh_dimensions(struct ui_browser *browser)
{
struct hist_browser *hb = container_of(browser, struct hist_browser, b);
/* 3 == +/- toggle symbol before actual hist_entry rendering */
browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
sizeof("[k]"));
browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
/*
* FIXME: Just keeping existing behaviour, but this really should be
* before updating browser->width, as it will invalidate the
* calculation above. Fix this and the fallout in another
* changeset.
*/
ui_browser__refresh_dimensions(browser);
hist_browser__update_rows(hb);
}
static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
{
u16 header_offset = browser->show_headers ? 1 : 0;
ui_browser__gotorc(&browser->b, row + header_offset, column);
}
static void hist_browser__reset(struct hist_browser *browser)
@ -74,7 +105,7 @@ static void hist_browser__reset(struct hist_browser *browser)
hist_browser__update_nr_entries(browser);
browser->b.nr_entries = hist_browser__nr_entries(browser);
hist_browser__refresh_dimensions(browser);
hist_browser__refresh_dimensions(&browser->b);
ui_browser__reset_index(&browser->b);
}
@ -346,7 +377,7 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
"Or reduce the sampling frequency.");
}
static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
static int hist_browser__run(struct hist_browser *browser,
struct hist_browser_timer *hbt)
{
int key;
@ -356,8 +387,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
browser->b.entries = &browser->hists->entries;
browser->b.nr_entries = hist_browser__nr_entries(browser);
hist_browser__refresh_dimensions(browser);
hists__browser_title(browser->hists, title, sizeof(title), ev_name);
hists__browser_title(browser->hists, title, sizeof(title));
if (ui_browser__show(&browser->b, title,
"Press '?' for help on key bindings") < 0)
@ -384,7 +414,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
ui_browser__warn_lost_events(&browser->b);
}
hists__browser_title(browser->hists, title, sizeof(title), ev_name);
hists__browser_title(browser->hists, title, sizeof(title));
ui_browser__show_title(&browser->b, title);
continue;
}
@ -393,10 +423,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
struct hist_entry *h = rb_entry(browser->b.top,
struct hist_entry, rb_node);
ui_helpline__pop();
ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
seq++, browser->b.nr_entries,
browser->hists->nr_entries,
browser->b.height,
browser->b.rows,
browser->b.index,
browser->b.top_idx,
h->row_offset, h->nr_rows);
@ -410,6 +440,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
/* Expand the whole world. */
hist_browser__set_folding(browser, true);
break;
case 'H':
browser->show_headers = !browser->show_headers;
hist_browser__update_rows(browser);
break;
case K_ENTER:
if (hist_browser__toggle_fold(browser))
break;
@ -509,13 +543,13 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
}
ui_browser__set_color(&browser->b, color);
ui_browser__gotorc(&browser->b, row, 0);
hist_browser__gotorc(browser, row, 0);
slsmg_write_nstring(" ", offset + extra_offset);
slsmg_printf("%c ", folded_sign);
slsmg_write_nstring(str, width);
free(alloc_str);
if (++row == browser->b.height)
if (++row == browser->b.rows)
goto out;
do_next:
if (folded_sign == '+')
@ -528,7 +562,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
new_level, row, row_offset,
is_current_entry);
}
if (row == browser->b.height)
if (row == browser->b.rows)
goto out;
node = next;
}
@ -568,13 +602,13 @@ static int hist_browser__show_callchain_node(struct hist_browser *browser,
s = callchain_list__sym_name(chain, bf, sizeof(bf),
browser->show_dso);
ui_browser__gotorc(&browser->b, row, 0);
hist_browser__gotorc(browser, row, 0);
ui_browser__set_color(&browser->b, color);
slsmg_write_nstring(" ", offset);
slsmg_printf("%c ", folded_sign);
slsmg_write_nstring(s, width - 2);
if (++row == browser->b.height)
if (++row == browser->b.rows)
goto out;
}
@ -603,7 +637,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
row += hist_browser__show_callchain_node(browser, node, level,
row, row_offset,
is_current_entry);
if (row == browser->b.height)
if (row == browser->b.rows)
break;
}
@ -733,7 +767,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.ptr = &arg,
};
ui_browser__gotorc(&browser->b, row, 0);
hist_browser__gotorc(browser, row, 0);
perf_hpp__for_each_format(fmt) {
if (perf_hpp__should_skip(fmt))
@ -777,7 +811,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
} else
--row_offset;
if (folded_sign == '-' && row != browser->b.height) {
if (folded_sign == '-' && row != browser->b.rows) {
printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
1, row, &row_offset,
&current_entry);
@ -788,6 +822,56 @@ static int hist_browser__show_entry(struct hist_browser *browser,
return printed;
}
static int advance_hpp_check(struct perf_hpp *hpp, int inc)
{
advance_hpp(hpp, inc);
return hpp->size <= 0;
}
static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
{
struct perf_hpp dummy_hpp = {
.buf = buf,
.size = size,
};
struct perf_hpp_fmt *fmt;
size_t ret = 0;
if (symbol_conf.use_callchain) {
ret = scnprintf(buf, size, " ");
if (advance_hpp_check(&dummy_hpp, ret))
return ret;
}
perf_hpp__for_each_format(fmt) {
if (perf_hpp__should_skip(fmt))
continue;
/* We need to add the length of the columns header. */
perf_hpp__reset_width(fmt, hists);
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
if (advance_hpp_check(&dummy_hpp, ret))
break;
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
if (advance_hpp_check(&dummy_hpp, ret))
break;
}
return ret;
}
static void hist_browser__show_headers(struct hist_browser *browser)
{
char headers[1024];
hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
ui_browser__gotorc(&browser->b, 0, 0);
ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
slsmg_write_nstring(headers, browser->b.width + 1);
}
static void ui_browser__hists_init_top(struct ui_browser *browser)
{
if (browser->top == NULL) {
@ -801,9 +885,15 @@ static void ui_browser__hists_init_top(struct ui_browser *browser)
static unsigned int hist_browser__refresh(struct ui_browser *browser)
{
unsigned row = 0;
u16 header_offset = 0;
struct rb_node *nd;
struct hist_browser *hb = container_of(browser, struct hist_browser, b);
if (hb->show_headers) {
hist_browser__show_headers(hb);
header_offset = 1;
}
ui_browser__hists_init_top(browser);
for (nd = browser->top; nd; nd = rb_next(nd)) {
@ -818,11 +908,11 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
continue;
row += hist_browser__show_entry(hb, h, row);
if (row == browser->height)
if (row == browser->rows)
break;
}
return row;
return row + header_offset;
}
static struct rb_node *hists__filter_entries(struct rb_node *nd,
@ -1191,8 +1281,10 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
if (browser) {
browser->hists = hists;
browser->b.refresh = hist_browser__refresh;
browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
browser->b.seek = ui_browser__hists_seek;
browser->b.use_navkeypressed = true;
browser->show_headers = symbol_conf.show_hist_headers;
}
return browser;
@ -1213,8 +1305,7 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser
return browser->he_selection->thread;
}
static int hists__browser_title(struct hists *hists, char *bf, size_t size,
const char *ev_name)
static int hists__browser_title(struct hists *hists, char *bf, size_t size)
{
char unit;
int printed;
@ -1223,6 +1314,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = hists->stats.total_period;
struct perf_evsel *evsel = hists_to_evsel(hists);
const char *ev_name = perf_evsel__name(evsel);
char buf[512];
size_t buflen = sizeof(buf);
@ -1390,7 +1482,7 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)
}
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
const char *helpline, const char *ev_name,
const char *helpline,
bool left_exits,
struct hist_browser_timer *hbt,
float min_pcnt,
@ -1422,6 +1514,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"d Zoom into current DSO\n" \
"E Expand all callchains\n" \
"F Toggle percentage of filtered entries\n" \
"H Display column headers\n" \
/* help messages are sorted by lexical order of the hotkey */
const char report_help[] = HIST_BROWSER_HELP_COMMON
@ -1465,7 +1558,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
nr_options = 0;
key = hist_browser__run(browser, ev_name, hbt);
key = hist_browser__run(browser, hbt);
if (browser->he_selection != NULL) {
thread = hist_browser__selected_thread(browser);
@ -1843,7 +1936,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
{
struct perf_evlist *evlist = menu->b.priv;
struct perf_evsel *pos;
const char *ev_name, *title = "Available samples";
const char *title = "Available samples";
int delay_secs = hbt ? hbt->refresh : 0;
int key;
@ -1876,9 +1969,8 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
*/
if (hbt)
hbt->timer(hbt->arg);
ev_name = perf_evsel__name(pos);
key = perf_evsel__hists_browse(pos, nr_events, help,
ev_name, true, hbt,
true, hbt,
menu->min_pcnt,
menu->env);
ui_browser__show_title(&menu->b, title);
@ -1982,10 +2074,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
single_entry:
if (nr_entries == 1) {
struct perf_evsel *first = perf_evlist__first(evlist);
const char *ev_name = perf_evsel__name(first);
return perf_evsel__hists_browse(first, nr_entries, help,
ev_name, false, hbt, min_pcnt,
false, hbt, min_pcnt,
env);
}

View File

@ -479,7 +479,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
if (h->ms.map == NULL && verbose > 1) {
__map_groups__fprintf_maps(h->thread->mg,
MAP__FUNCTION, verbose, fp);
MAP__FUNCTION, fp);
fprintf(fp, "%.10s end\n", graph_dotted_line);
}
}

View File

@ -626,7 +626,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample)
{
if (!symbol_conf.use_callchain)
if (!symbol_conf.use_callchain || sample->callchain == NULL)
return 0;
return callchain_append(he->callchain, &callchain_cursor, sample->period);
}

View File

@ -176,4 +176,17 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
dest->first = src->curr;
dest->nr -= src->pos;
}
#ifdef HAVE_SKIP_CALLCHAIN_IDX
extern int arch_skip_callchain_idx(struct machine *machine,
struct thread *thread, struct ip_callchain *chain);
#else
static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused,
struct thread *thread __maybe_unused,
struct ip_callchain *chain __maybe_unused)
{
return -1;
}
#endif
#endif /* __PERF_CALLCHAIN_H */

57
tools/perf/util/cloexec.c Normal file
View File

@ -0,0 +1,57 @@
#include "util.h"
#include "../perf.h"
#include "cloexec.h"
#include "asm/bug.h"
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
static int perf_flag_probe(void)
{
/* use 'safest' configuration as used in perf_evsel__fallback() */
struct perf_event_attr attr = {
.type = PERF_COUNT_SW_CPU_CLOCK,
.config = PERF_COUNT_SW_CPU_CLOCK,
};
int fd;
int err;
/* check cloexec flag */
fd = sys_perf_event_open(&attr, 0, -1, -1,
PERF_FLAG_FD_CLOEXEC);
err = errno;
if (fd >= 0) {
close(fd);
return 1;
}
WARN_ONCE(err != EINVAL,
"perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
err, strerror(err));
/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
err = errno;
if (WARN_ONCE(fd < 0,
"perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
err, strerror(err)))
return -1;
close(fd);
return 0;
}
unsigned long perf_event_open_cloexec_flag(void)
{
static bool probed;
if (!probed) {
if (perf_flag_probe() <= 0)
flag = 0;
probed = true;
}
return flag;
}

Some files were not shown because too many files have changed in this diff Show More