Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Kernel side updates: - Fix and enhance poll support (Jiri Olsa) - Re-enable inheritance optimization (Jiri Olsa) - Enhance Intel memory events support (Stephane Eranian) - Refactor the Intel uncore driver to be more maintainable (Zheng Yan) - Enhance and fix Intel CPU and uncore PMU drivers (Peter Zijlstra, Andi Kleen) - [ plus various smaller fixes/cleanups ] User visible tooling updates: - Add +field argument support for --field option, so that one can add fields to the default list of fields to show, ie now one can just do: perf report --fields +pid And the pid will appear in addition to the default fields (Jiri Olsa) - Add +field argument support for --sort option (Jiri Olsa) - Honour -w in the report tools (report, top), allowing to specify the widths for the histogram entries columns (Namhyung Kim) - Properly show submicrosecond times in 'perf kvm stat' (Christian Borntraeger) - Add beautifier for mremap flags param in 'trace' (Alex Snast) - perf script: Allow callchains if any event samples them - Don't truncate Intel style addresses in 'annotate' (Alex Converse) - Allow profiling when kptr_restrict == 1 for non root users, kernel samples will just remain unresolved (Andi Kleen) - Allow configuring default options for callchains in config file (Namhyung Kim) - Support operations for shared futexes. (Davidlohr Bueso) - "perf kvm stat report" improvements by Alexander Yarygin: - Save pid string in opts.target.pid - Enable the target.system_wide flag - Unify the title bar output - [ plus lots of other fixes and small improvements. ] Tooling infrastructure changes: - Refactor unit and scale function parameters for PMU parsing routines (Matt Fleming) - Improve DSO long names lookup with rbtree, resulting in great speedup for workloads with lots of DSOs (Waiman Long) - We were not handling POLLHUP notifications for event file descriptors Fix it by filtering entries in the events file descriptor array after poll() returns, refcounting mmaps so that when the last fd pointing to a perf mmap goes away we do the unmap (Arnaldo Carvalho de Melo) - Intel PT prep work, from Adrian Hunter, including: - Let a user specify a PMU event without any config terms - Add perf-with-kcore script - Let default config be defined for a PMU - Add perf_pmu__scan_file() - Add a 'perf test' for tracking with sched_switch - Add 'flush' callback to scripting API - Use ring buffer consume method to look like other tools (Arnaldo Carvalho de Melo) - hists browser (used in top and report) refactorings, getting rid of unused variables and reducing source code size by handling similar cases in a fewer functions (Namhyung Kim). - Replace thread unsafe strerror() with strerror_r() accross the whole tools/perf/ tree (Masami Hiramatsu) - Rename ordered_samples to ordered_events and allow setting a queue size for ordering events (Jiri Olsa) - [ plus lots of fixes, cleanups and other improvements ]" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (198 commits) perf/x86: Tone down kernel messages when the PMU check fails in a virtual environment perf/x86/intel/uncore: Fix minor race in box set up perf record: Fix error message for --filter option not coming after tracepoint perf tools: Fix build breakage on arm64 targets perf symbols: Improve DSO long names lookup speed with rbtree perf symbols: Encapsulate dsos list head into struct dsos perf bench futex: Sanitize -q option in requeue perf bench futex: Support operations for shared futexes perf trace: Fix mmap return address truncation to 32-bit perf tools: Refactor unit and scale function parameters perf tools: Fix line number in the config file error message perf tools: Convert {record,top}.call-graph option to call-graph.record-mode perf tools: Introduce perf_callchain_config() perf callchain: Move some parser functions to callchain.c perf tools: Move callchain config from record_opts to callchain_param perf hists browser: Fix callchain print bug on TUI perf tools: Use ACCESS_ONCE() instead of volatile cast perf tools: Modify error code for when perf_session__new() fails perf tools: Fix perf record as non root with kptr_restrict == 1 perf stat: Fix --per-core on multi socket systems ...
This commit is contained in:
commit
9d9420f120
@ -51,6 +51,14 @@
|
||||
ARCH_PERFMON_EVENTSEL_EDGE | \
|
||||
ARCH_PERFMON_EVENTSEL_INV | \
|
||||
ARCH_PERFMON_EVENTSEL_CMASK)
|
||||
#define X86_ALL_EVENT_FLAGS \
|
||||
(ARCH_PERFMON_EVENTSEL_EDGE | \
|
||||
ARCH_PERFMON_EVENTSEL_INV | \
|
||||
ARCH_PERFMON_EVENTSEL_CMASK | \
|
||||
ARCH_PERFMON_EVENTSEL_ANY | \
|
||||
ARCH_PERFMON_EVENTSEL_PIN_CONTROL | \
|
||||
HSW_IN_TX | \
|
||||
HSW_IN_TX_CHECKPOINTED)
|
||||
#define AMD64_RAW_EVENT_MASK \
|
||||
(X86_RAW_EVENT_MASK | \
|
||||
AMD64_EVENTSEL_EVENT)
|
||||
|
@ -39,7 +39,9 @@ obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd_iommu.o
|
||||
endif
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o perf_event_intel_rapl.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o perf_event_intel_uncore_snb.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore_snbep.o perf_event_intel_uncore_nhmex.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_rapl.o
|
||||
endif
|
||||
|
||||
|
||||
|
@ -243,7 +243,8 @@ static bool check_hw_exists(void)
|
||||
|
||||
msr_fail:
|
||||
printk(KERN_CONT "Broken PMU hardware detected, using software events only.\n");
|
||||
printk(KERN_ERR "Failed to access perfctr msr (MSR %x is %Lx)\n", reg, val_new);
|
||||
printk(boot_cpu_has(X86_FEATURE_HYPERVISOR) ? KERN_INFO : KERN_ERR
|
||||
"Failed to access perfctr msr (MSR %x is %Lx)\n", reg, val_new);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -387,7 +388,7 @@ int x86_pmu_hw_config(struct perf_event *event)
|
||||
precise++;
|
||||
|
||||
/* Support for IP fixup */
|
||||
if (x86_pmu.lbr_nr)
|
||||
if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2)
|
||||
precise++;
|
||||
}
|
||||
|
||||
@ -443,6 +444,12 @@ int x86_pmu_hw_config(struct perf_event *event)
|
||||
if (event->attr.type == PERF_TYPE_RAW)
|
||||
event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK;
|
||||
|
||||
if (event->attr.sample_period && x86_pmu.limit_period) {
|
||||
if (x86_pmu.limit_period(event, event->attr.sample_period) >
|
||||
event->attr.sample_period)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return x86_setup_perfctr(event);
|
||||
}
|
||||
|
||||
@ -980,6 +987,9 @@ int x86_perf_event_set_period(struct perf_event *event)
|
||||
if (left > x86_pmu.max_period)
|
||||
left = x86_pmu.max_period;
|
||||
|
||||
if (x86_pmu.limit_period)
|
||||
left = x86_pmu.limit_period(event, left);
|
||||
|
||||
per_cpu(pmc_prev_left[idx], smp_processor_id()) = left;
|
||||
|
||||
/*
|
||||
|
@ -67,8 +67,10 @@ struct event_constraint {
|
||||
*/
|
||||
#define PERF_X86_EVENT_PEBS_LDLAT 0x1 /* ld+ldlat data address sampling */
|
||||
#define PERF_X86_EVENT_PEBS_ST 0x2 /* st data address sampling */
|
||||
#define PERF_X86_EVENT_PEBS_ST_HSW 0x4 /* haswell style st data sampling */
|
||||
#define PERF_X86_EVENT_PEBS_ST_HSW 0x4 /* haswell style datala, store */
|
||||
#define PERF_X86_EVENT_COMMITTED 0x8 /* event passed commit_txn */
|
||||
#define PERF_X86_EVENT_PEBS_LD_HSW 0x10 /* haswell style datala, load */
|
||||
#define PERF_X86_EVENT_PEBS_NA_HSW 0x20 /* haswell style datala, unknown */
|
||||
|
||||
struct amd_nb {
|
||||
int nb_id; /* NorthBridge id */
|
||||
@ -252,18 +254,52 @@ struct cpu_hw_events {
|
||||
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK)
|
||||
|
||||
#define INTEL_PLD_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT)
|
||||
|
||||
#define INTEL_PST_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST)
|
||||
|
||||
/* DataLA version of store sampling without extra enable bit. */
|
||||
#define INTEL_PST_HSW_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \
|
||||
/* Event constraint, but match on all event flags too. */
|
||||
#define INTEL_FLAGS_EVENT_CONSTRAINT(c, n) \
|
||||
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS)
|
||||
|
||||
/* Check only flags, but allow all event/umask */
|
||||
#define INTEL_ALL_EVENT_CONSTRAINT(code, n) \
|
||||
EVENT_CONSTRAINT(code, n, X86_ALL_EVENT_FLAGS)
|
||||
|
||||
/* Check flags and event code, and set the HSW store flag */
|
||||
#define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_ST(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
|
||||
/* Check flags and event code, and set the HSW load flag */
|
||||
#define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW store flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW load flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW N/A flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|INTEL_ARCH_EVENT_MASK, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_NA_HSW)
|
||||
|
||||
|
||||
/*
|
||||
* We define the end marker as having a weight of -1
|
||||
* to enable blacklisting of events using a counter bitmask
|
||||
@ -409,6 +445,7 @@ struct x86_pmu {
|
||||
struct x86_pmu_quirk *quirks;
|
||||
int perfctr_second_write;
|
||||
bool late_ack;
|
||||
unsigned (*limit_period)(struct perf_event *event, unsigned l);
|
||||
|
||||
/*
|
||||
* sysfs attrs
|
||||
|
@ -220,6 +220,15 @@ static struct event_constraint intel_hsw_event_constraints[] = {
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
static struct event_constraint intel_bdw_event_constraints[] = {
|
||||
FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */
|
||||
FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */
|
||||
FIXED_EVENT_CONSTRAINT(0x0300, 2), /* CPU_CLK_UNHALTED.REF */
|
||||
INTEL_UEVENT_CONSTRAINT(0x148, 0x4), /* L1D_PEND_MISS.PENDING */
|
||||
INTEL_EVENT_CONSTRAINT(0xa3, 0x4), /* CYCLE_ACTIVITY.* */
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
static u64 intel_pmu_event_map(int hw_event)
|
||||
{
|
||||
return intel_perfmon_event_map[hw_event];
|
||||
@ -415,6 +424,126 @@ static __initconst const u64 snb_hw_cache_event_ids
|
||||
|
||||
};
|
||||
|
||||
static __initconst const u64 hsw_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
{
|
||||
[ C(L1D ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
[ C(RESULT_MISS) ] = 0x151, /* L1D.REPLACEMENT */
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
},
|
||||
[ C(L1I ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x280, /* ICACHE.MISSES */
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
},
|
||||
[ C(LL ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
/* OFFCORE_RESPONSE:ALL_DATA_RD|ALL_CODE_RD */
|
||||
[ C(RESULT_ACCESS) ] = 0x1b7,
|
||||
/* OFFCORE_RESPONSE:ALL_DATA_RD|ALL_CODE_RD|SUPPLIER_NONE|
|
||||
L3_MISS|ANY_SNOOP */
|
||||
[ C(RESULT_MISS) ] = 0x1b7,
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x1b7, /* OFFCORE_RESPONSE:ALL_RFO */
|
||||
/* OFFCORE_RESPONSE:ALL_RFO|SUPPLIER_NONE|L3_MISS|ANY_SNOOP */
|
||||
[ C(RESULT_MISS) ] = 0x1b7,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
},
|
||||
[ C(DTLB) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
[ C(RESULT_MISS) ] = 0x108, /* DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK */
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
[ C(RESULT_MISS) ] = 0x149, /* DTLB_STORE_MISSES.MISS_CAUSES_A_WALK */
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
},
|
||||
[ C(ITLB) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x6085, /* ITLB_MISSES.STLB_HIT */
|
||||
[ C(RESULT_MISS) ] = 0x185, /* ITLB_MISSES.MISS_CAUSES_A_WALK */
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
},
|
||||
[ C(BPU ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0xc4, /* BR_INST_RETIRED.ALL_BRANCHES */
|
||||
[ C(RESULT_MISS) ] = 0xc5, /* BR_MISP_RETIRED.ALL_BRANCHES */
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static __initconst const u64 hsw_hw_cache_extra_regs
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
{
|
||||
[ C(LL ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
/* OFFCORE_RESPONSE:ALL_DATA_RD|ALL_CODE_RD */
|
||||
[ C(RESULT_ACCESS) ] = 0x2d5,
|
||||
/* OFFCORE_RESPONSE:ALL_DATA_RD|ALL_CODE_RD|SUPPLIER_NONE|
|
||||
L3_MISS|ANY_SNOOP */
|
||||
[ C(RESULT_MISS) ] = 0x3fbc0202d5ull,
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x122, /* OFFCORE_RESPONSE:ALL_RFO */
|
||||
/* OFFCORE_RESPONSE:ALL_RFO|SUPPLIER_NONE|L3_MISS|ANY_SNOOP */
|
||||
[ C(RESULT_MISS) ] = 0x3fbc020122ull,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = 0x0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static __initconst const u64 westmere_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
@ -1905,6 +2034,24 @@ hsw_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event)
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadwell:
|
||||
* The INST_RETIRED.ALL period always needs to have lowest
|
||||
* 6bits cleared (BDM57). It shall not use a period smaller
|
||||
* than 100 (BDM11). We combine the two to enforce
|
||||
* a min-period of 128.
|
||||
*/
|
||||
static unsigned bdw_limit_period(struct perf_event *event, unsigned left)
|
||||
{
|
||||
if ((event->hw.config & INTEL_ARCH_EVENT_MASK) ==
|
||||
X86_CONFIG(.event=0xc0, .umask=0x01)) {
|
||||
if (left < 128)
|
||||
left = 128;
|
||||
left &= ~0x3fu;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
PMU_FORMAT_ATTR(event, "config:0-7" );
|
||||
PMU_FORMAT_ATTR(umask, "config:8-15" );
|
||||
PMU_FORMAT_ATTR(edge, "config:18" );
|
||||
@ -2367,15 +2514,15 @@ __init int intel_pmu_init(void)
|
||||
* Install the hw-cache-events table:
|
||||
*/
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 14: /* 65 nm core solo/duo, "Yonah" */
|
||||
case 14: /* 65nm Core "Yonah" */
|
||||
pr_cont("Core events, ");
|
||||
break;
|
||||
|
||||
case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */
|
||||
case 15: /* 65nm Core2 "Merom" */
|
||||
x86_add_quirk(intel_clovertown_quirk);
|
||||
case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */
|
||||
case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */
|
||||
case 29: /* six-core 45 nm xeon "Dunnington" */
|
||||
case 22: /* 65nm Core2 "Merom-L" */
|
||||
case 23: /* 45nm Core2 "Penryn" */
|
||||
case 29: /* 45nm Core2 "Dunnington (MP) */
|
||||
memcpy(hw_cache_event_ids, core2_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
@ -2386,9 +2533,9 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Core2 events, ");
|
||||
break;
|
||||
|
||||
case 26: /* 45 nm nehalem, "Bloomfield" */
|
||||
case 30: /* 45 nm nehalem, "Lynnfield" */
|
||||
case 46: /* 45 nm nehalem-ex, "Beckton" */
|
||||
case 30: /* 45nm Nehalem */
|
||||
case 26: /* 45nm Nehalem-EP */
|
||||
case 46: /* 45nm Nehalem-EX */
|
||||
memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs,
|
||||
@ -2415,11 +2562,11 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Nehalem events, ");
|
||||
break;
|
||||
|
||||
case 28: /* Atom */
|
||||
case 38: /* Lincroft */
|
||||
case 39: /* Penwell */
|
||||
case 53: /* Cloverview */
|
||||
case 54: /* Cedarview */
|
||||
case 28: /* 45nm Atom "Pineview" */
|
||||
case 38: /* 45nm Atom "Lincroft" */
|
||||
case 39: /* 32nm Atom "Penwell" */
|
||||
case 53: /* 32nm Atom "Cloverview" */
|
||||
case 54: /* 32nm Atom "Cedarview" */
|
||||
memcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
@ -2430,8 +2577,8 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Atom events, ");
|
||||
break;
|
||||
|
||||
case 55: /* Atom 22nm "Silvermont" */
|
||||
case 77: /* Avoton "Silvermont" */
|
||||
case 55: /* 22nm Atom "Silvermont" */
|
||||
case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
|
||||
memcpy(hw_cache_event_ids, slm_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, slm_hw_cache_extra_regs,
|
||||
@ -2446,9 +2593,9 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Silvermont events, ");
|
||||
break;
|
||||
|
||||
case 37: /* 32 nm nehalem, "Clarkdale" */
|
||||
case 44: /* 32 nm nehalem, "Gulftown" */
|
||||
case 47: /* 32 nm Xeon E7 */
|
||||
case 37: /* 32nm Westmere */
|
||||
case 44: /* 32nm Westmere-EP */
|
||||
case 47: /* 32nm Westmere-EX */
|
||||
memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs,
|
||||
@ -2474,8 +2621,8 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Westmere events, ");
|
||||
break;
|
||||
|
||||
case 42: /* SandyBridge */
|
||||
case 45: /* SandyBridge, "Romely-EP" */
|
||||
case 42: /* 32nm SandyBridge */
|
||||
case 45: /* 32nm SandyBridge-E/EN/EP */
|
||||
x86_add_quirk(intel_sandybridge_quirk);
|
||||
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
@ -2506,8 +2653,9 @@ __init int intel_pmu_init(void)
|
||||
|
||||
pr_cont("SandyBridge events, ");
|
||||
break;
|
||||
case 58: /* IvyBridge */
|
||||
case 62: /* IvyBridge EP */
|
||||
|
||||
case 58: /* 22nm IvyBridge */
|
||||
case 62: /* 22nm IvyBridge-EP/EX */
|
||||
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
/* dTLB-load-misses on IVB is different than SNB */
|
||||
@ -2539,20 +2687,19 @@ __init int intel_pmu_init(void)
|
||||
break;
|
||||
|
||||
|
||||
case 60: /* Haswell Client */
|
||||
case 70:
|
||||
case 71:
|
||||
case 63:
|
||||
case 69:
|
||||
case 60: /* 22nm Haswell Core */
|
||||
case 63: /* 22nm Haswell Server */
|
||||
case 69: /* 22nm Haswell ULT */
|
||||
case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */
|
||||
x86_pmu.late_ack = true;
|
||||
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, sizeof(hw_cache_extra_regs));
|
||||
memcpy(hw_cache_event_ids, hsw_hw_cache_event_ids, sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, hsw_hw_cache_extra_regs, sizeof(hw_cache_extra_regs));
|
||||
|
||||
intel_pmu_lbr_init_snb();
|
||||
|
||||
x86_pmu.event_constraints = intel_hsw_event_constraints;
|
||||
x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints;
|
||||
x86_pmu.extra_regs = intel_snb_extra_regs;
|
||||
x86_pmu.extra_regs = intel_snbep_extra_regs;
|
||||
x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
|
||||
/* all extra regs are per-cpu when HT is on */
|
||||
x86_pmu.er_flags |= ERF_HAS_RSP_1;
|
||||
@ -2565,6 +2712,28 @@ __init int intel_pmu_init(void)
|
||||
pr_cont("Haswell events, ");
|
||||
break;
|
||||
|
||||
case 61: /* 14nm Broadwell Core-M */
|
||||
x86_pmu.late_ack = true;
|
||||
memcpy(hw_cache_event_ids, hsw_hw_cache_event_ids, sizeof(hw_cache_event_ids));
|
||||
memcpy(hw_cache_extra_regs, hsw_hw_cache_extra_regs, sizeof(hw_cache_extra_regs));
|
||||
|
||||
intel_pmu_lbr_init_snb();
|
||||
|
||||
x86_pmu.event_constraints = intel_bdw_event_constraints;
|
||||
x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints;
|
||||
x86_pmu.extra_regs = intel_snbep_extra_regs;
|
||||
x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
|
||||
/* all extra regs are per-cpu when HT is on */
|
||||
x86_pmu.er_flags |= ERF_HAS_RSP_1;
|
||||
x86_pmu.er_flags |= ERF_NO_HT_SHARING;
|
||||
|
||||
x86_pmu.hw_config = hsw_hw_config;
|
||||
x86_pmu.get_event_constraints = hsw_get_event_constraints;
|
||||
x86_pmu.cpu_events = hsw_events_attrs;
|
||||
x86_pmu.limit_period = bdw_limit_period;
|
||||
pr_cont("Broadwell events, ");
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (x86_pmu.version) {
|
||||
case 1:
|
||||
|
@ -108,14 +108,16 @@ static u64 precise_store_data(u64 status)
|
||||
return val;
|
||||
}
|
||||
|
||||
static u64 precise_store_data_hsw(struct perf_event *event, u64 status)
|
||||
static u64 precise_datala_hsw(struct perf_event *event, u64 status)
|
||||
{
|
||||
union perf_mem_data_src dse;
|
||||
u64 cfg = event->hw.config & INTEL_ARCH_EVENT_MASK;
|
||||
|
||||
dse.val = 0;
|
||||
dse.mem_op = PERF_MEM_OP_STORE;
|
||||
dse.mem_lvl = PERF_MEM_LVL_NA;
|
||||
dse.val = PERF_MEM_NA;
|
||||
|
||||
if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
dse.mem_op = PERF_MEM_OP_STORE;
|
||||
else if (event->hw.flags & PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
dse.mem_op = PERF_MEM_OP_LOAD;
|
||||
|
||||
/*
|
||||
* L1 info only valid for following events:
|
||||
@ -125,15 +127,12 @@ static u64 precise_store_data_hsw(struct perf_event *event, u64 status)
|
||||
* MEM_UOPS_RETIRED.SPLIT_STORES
|
||||
* MEM_UOPS_RETIRED.ALL_STORES
|
||||
*/
|
||||
if (cfg != 0x12d0 && cfg != 0x22d0 && cfg != 0x42d0 && cfg != 0x82d0)
|
||||
return dse.mem_lvl;
|
||||
|
||||
if (status & 1)
|
||||
dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_HIT;
|
||||
else
|
||||
dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_MISS;
|
||||
|
||||
/* Nothing else supported. Sorry. */
|
||||
if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW) {
|
||||
if (status & 1)
|
||||
dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_HIT;
|
||||
else
|
||||
dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_MISS;
|
||||
}
|
||||
return dse.val;
|
||||
}
|
||||
|
||||
@ -569,28 +568,10 @@ struct event_constraint intel_atom_pebs_event_constraints[] = {
|
||||
};
|
||||
|
||||
struct event_constraint intel_slm_pebs_event_constraints[] = {
|
||||
INTEL_UEVENT_CONSTRAINT(0x0103, 0x1), /* REHABQ.LD_BLOCK_ST_FORWARD_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x0803, 0x1), /* REHABQ.LD_SPLITS_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x0204, 0x1), /* MEM_UOPS_RETIRED.L2_HIT_LOADS_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x0404, 0x1), /* MEM_UOPS_RETIRED.L2_MISS_LOADS_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x0804, 0x1), /* MEM_UOPS_RETIRED.DTLB_MISS_LOADS_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x2004, 0x1), /* MEM_UOPS_RETIRED.HITM_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x00c4, 0x1), /* BR_INST_RETIRED.ALL_BRANCHES_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x7ec4, 0x1), /* BR_INST_RETIRED.JCC_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xbfc4, 0x1), /* BR_INST_RETIRED.FAR_BRANCH_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xebc4, 0x1), /* BR_INST_RETIRED.NON_RETURN_IND_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xf7c4, 0x1), /* BR_INST_RETIRED.RETURN_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xf9c4, 0x1), /* BR_INST_RETIRED.CALL_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xfbc4, 0x1), /* BR_INST_RETIRED.IND_CALL_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xfdc4, 0x1), /* BR_INST_RETIRED.REL_CALL_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xfec4, 0x1), /* BR_INST_RETIRED.TAKEN_JCC_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_MISP_RETIRED.ALL_BRANCHES_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x7ec5, 0x1), /* BR_INST_MISP_RETIRED.JCC_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xebc5, 0x1), /* BR_INST_MISP_RETIRED.NON_RETURN_IND_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xf7c5, 0x1), /* BR_INST_MISP_RETIRED.RETURN_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xfbc5, 0x1), /* BR_INST_MISP_RETIRED.IND_CALL_PS */
|
||||
INTEL_UEVENT_CONSTRAINT(0xfec5, 0x1), /* BR_INST_MISP_RETIRED.TAKEN_JCC_PS */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0x1),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
@ -626,68 +607,44 @@ struct event_constraint intel_westmere_pebs_event_constraints[] = {
|
||||
|
||||
struct event_constraint intel_snb_pebs_event_constraints[] = {
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
|
||||
INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */
|
||||
INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */
|
||||
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
|
||||
INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02d4, 0xf), /* MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
struct event_constraint intel_ivb_pebs_event_constraints[] = {
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
|
||||
INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */
|
||||
INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */
|
||||
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
|
||||
INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
|
||||
INTEL_EVENT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
struct event_constraint intel_hsw_pebs_event_constraints[] = {
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
|
||||
INTEL_PST_HSW_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
|
||||
INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01c5, 0xf), /* BR_MISP_RETIRED.CONDITIONAL */
|
||||
INTEL_UEVENT_CONSTRAINT(0x04c5, 0xf), /* BR_MISP_RETIRED.ALL_BRANCHES */
|
||||
INTEL_UEVENT_CONSTRAINT(0x20c5, 0xf), /* BR_MISP_RETIRED.NEAR_TAKEN */
|
||||
INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.* */
|
||||
/* MEM_UOPS_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x11d0, 0xf),
|
||||
/* MEM_UOPS_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_UEVENT_CONSTRAINT(0x12d0, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */
|
||||
/* MEM_UOPS_RETIRED.SPLIT_STORES */
|
||||
INTEL_UEVENT_CONSTRAINT(0x42d0, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
INTEL_PST_HSW_CONSTRAINT(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01d1, 0xf), /* MEM_LOAD_UOPS_RETIRED.L1_HIT */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02d1, 0xf), /* MEM_LOAD_UOPS_RETIRED.L2_HIT */
|
||||
INTEL_UEVENT_CONSTRAINT(0x04d1, 0xf), /* MEM_LOAD_UOPS_RETIRED.L3_HIT */
|
||||
/* MEM_LOAD_UOPS_RETIRED.HIT_LFB */
|
||||
INTEL_UEVENT_CONSTRAINT(0x40d1, 0xf),
|
||||
/* MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_MISS */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01d2, 0xf),
|
||||
/* MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT */
|
||||
INTEL_UEVENT_CONSTRAINT(0x02d2, 0xf),
|
||||
/* MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM */
|
||||
INTEL_UEVENT_CONSTRAINT(0x01d3, 0xf),
|
||||
INTEL_UEVENT_CONSTRAINT(0x04c8, 0xf), /* HLE_RETIRED.Abort */
|
||||
INTEL_UEVENT_CONSTRAINT(0x04c9, 0xf), /* RTM_RETIRED.Abort */
|
||||
|
||||
INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd2, 0xf), /* MEM_LOAD_UOPS_L3_HIT_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd3, 0xf), /* MEM_LOAD_UOPS_L3_MISS_RETIRED.* */
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
@ -864,6 +821,10 @@ static inline u64 intel_hsw_transaction(struct pebs_record_hsw *pebs)
|
||||
static void __intel_pmu_pebs_event(struct perf_event *event,
|
||||
struct pt_regs *iregs, void *__pebs)
|
||||
{
|
||||
#define PERF_X86_EVENT_PEBS_HSW_PREC \
|
||||
(PERF_X86_EVENT_PEBS_ST_HSW | \
|
||||
PERF_X86_EVENT_PEBS_LD_HSW | \
|
||||
PERF_X86_EVENT_PEBS_NA_HSW)
|
||||
/*
|
||||
* We cast to the biggest pebs_record but are careful not to
|
||||
* unconditionally access the 'extra' entries.
|
||||
@ -873,42 +834,40 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs regs;
|
||||
u64 sample_type;
|
||||
int fll, fst;
|
||||
int fll, fst, dsrc;
|
||||
int fl = event->hw.flags;
|
||||
|
||||
if (!intel_pmu_save_and_restart(event))
|
||||
return;
|
||||
|
||||
fll = event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT;
|
||||
fst = event->hw.flags & (PERF_X86_EVENT_PEBS_ST |
|
||||
PERF_X86_EVENT_PEBS_ST_HSW);
|
||||
sample_type = event->attr.sample_type;
|
||||
dsrc = sample_type & PERF_SAMPLE_DATA_SRC;
|
||||
|
||||
fll = fl & PERF_X86_EVENT_PEBS_LDLAT;
|
||||
fst = fl & (PERF_X86_EVENT_PEBS_ST | PERF_X86_EVENT_PEBS_HSW_PREC);
|
||||
|
||||
perf_sample_data_init(&data, 0, event->hw.last_period);
|
||||
|
||||
data.period = event->hw.last_period;
|
||||
sample_type = event->attr.sample_type;
|
||||
|
||||
/*
|
||||
* if PEBS-LL or PreciseStore
|
||||
* Use latency for weight (only avail with PEBS-LL)
|
||||
*/
|
||||
if (fll || fst) {
|
||||
/*
|
||||
* Use latency for weight (only avail with PEBS-LL)
|
||||
*/
|
||||
if (fll && (sample_type & PERF_SAMPLE_WEIGHT))
|
||||
data.weight = pebs->lat;
|
||||
if (fll && (sample_type & PERF_SAMPLE_WEIGHT))
|
||||
data.weight = pebs->lat;
|
||||
|
||||
/*
|
||||
* data.data_src encodes the data source
|
||||
*/
|
||||
if (sample_type & PERF_SAMPLE_DATA_SRC) {
|
||||
if (fll)
|
||||
data.data_src.val = load_latency_data(pebs->dse);
|
||||
else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
data.data_src.val =
|
||||
precise_store_data_hsw(event, pebs->dse);
|
||||
else
|
||||
data.data_src.val = precise_store_data(pebs->dse);
|
||||
}
|
||||
/*
|
||||
* data.data_src encodes the data source
|
||||
*/
|
||||
if (dsrc) {
|
||||
u64 val = PERF_MEM_NA;
|
||||
if (fll)
|
||||
val = load_latency_data(pebs->dse);
|
||||
else if (fst && (fl & PERF_X86_EVENT_PEBS_HSW_PREC))
|
||||
val = precise_datala_hsw(event, pebs->dse);
|
||||
else if (fst)
|
||||
val = precise_store_data(pebs->dse);
|
||||
data.data_src.val = val;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -935,16 +894,16 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
|
||||
else
|
||||
regs.flags &= ~PERF_EFLAGS_EXACT;
|
||||
|
||||
if ((event->attr.sample_type & PERF_SAMPLE_ADDR) &&
|
||||
if ((sample_type & PERF_SAMPLE_ADDR) &&
|
||||
x86_pmu.intel_cap.pebs_format >= 1)
|
||||
data.addr = pebs->dla;
|
||||
|
||||
if (x86_pmu.intel_cap.pebs_format >= 2) {
|
||||
/* Only set the TSX weight when no memory weight. */
|
||||
if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && !fll)
|
||||
if ((sample_type & PERF_SAMPLE_WEIGHT) && !fll)
|
||||
data.weight = intel_hsw_weight(pebs);
|
||||
|
||||
if (event->attr.sample_type & PERF_SAMPLE_TRANSACTION)
|
||||
if (sample_type & PERF_SAMPLE_TRANSACTION)
|
||||
data.txn = intel_hsw_transaction(pebs);
|
||||
}
|
||||
|
||||
@ -1055,7 +1014,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
|
||||
* BTS, PEBS probe and setup
|
||||
*/
|
||||
|
||||
void intel_ds_init(void)
|
||||
void __init intel_ds_init(void)
|
||||
{
|
||||
/*
|
||||
* No support for 32bit formats
|
||||
|
@ -697,7 +697,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
|
||||
};
|
||||
|
||||
/* core */
|
||||
void intel_pmu_lbr_init_core(void)
|
||||
void __init intel_pmu_lbr_init_core(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 4;
|
||||
x86_pmu.lbr_tos = MSR_LBR_TOS;
|
||||
@ -712,7 +712,7 @@ void intel_pmu_lbr_init_core(void)
|
||||
}
|
||||
|
||||
/* nehalem/westmere */
|
||||
void intel_pmu_lbr_init_nhm(void)
|
||||
void __init intel_pmu_lbr_init_nhm(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 16;
|
||||
x86_pmu.lbr_tos = MSR_LBR_TOS;
|
||||
@ -733,7 +733,7 @@ void intel_pmu_lbr_init_nhm(void)
|
||||
}
|
||||
|
||||
/* sandy bridge */
|
||||
void intel_pmu_lbr_init_snb(void)
|
||||
void __init intel_pmu_lbr_init_snb(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 16;
|
||||
x86_pmu.lbr_tos = MSR_LBR_TOS;
|
||||
@ -753,7 +753,7 @@ void intel_pmu_lbr_init_snb(void)
|
||||
}
|
||||
|
||||
/* atom */
|
||||
void intel_pmu_lbr_init_atom(void)
|
||||
void __init intel_pmu_lbr_init_atom(void)
|
||||
{
|
||||
/*
|
||||
* only models starting at stepping 10 seems
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,395 +24,6 @@
|
||||
|
||||
#define UNCORE_EVENT_CONSTRAINT(c, n) EVENT_CONSTRAINT(c, n, 0xff)
|
||||
|
||||
/* SNB event control */
|
||||
#define SNB_UNC_CTL_EV_SEL_MASK 0x000000ff
|
||||
#define SNB_UNC_CTL_UMASK_MASK 0x0000ff00
|
||||
#define SNB_UNC_CTL_EDGE_DET (1 << 18)
|
||||
#define SNB_UNC_CTL_EN (1 << 22)
|
||||
#define SNB_UNC_CTL_INVERT (1 << 23)
|
||||
#define SNB_UNC_CTL_CMASK_MASK 0x1f000000
|
||||
#define NHM_UNC_CTL_CMASK_MASK 0xff000000
|
||||
#define NHM_UNC_FIXED_CTR_CTL_EN (1 << 0)
|
||||
|
||||
#define SNB_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
|
||||
SNB_UNC_CTL_UMASK_MASK | \
|
||||
SNB_UNC_CTL_EDGE_DET | \
|
||||
SNB_UNC_CTL_INVERT | \
|
||||
SNB_UNC_CTL_CMASK_MASK)
|
||||
|
||||
#define NHM_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
|
||||
SNB_UNC_CTL_UMASK_MASK | \
|
||||
SNB_UNC_CTL_EDGE_DET | \
|
||||
SNB_UNC_CTL_INVERT | \
|
||||
NHM_UNC_CTL_CMASK_MASK)
|
||||
|
||||
/* SNB global control register */
|
||||
#define SNB_UNC_PERF_GLOBAL_CTL 0x391
|
||||
#define SNB_UNC_FIXED_CTR_CTRL 0x394
|
||||
#define SNB_UNC_FIXED_CTR 0x395
|
||||
|
||||
/* SNB uncore global control */
|
||||
#define SNB_UNC_GLOBAL_CTL_CORE_ALL ((1 << 4) - 1)
|
||||
#define SNB_UNC_GLOBAL_CTL_EN (1 << 29)
|
||||
|
||||
/* SNB Cbo register */
|
||||
#define SNB_UNC_CBO_0_PERFEVTSEL0 0x700
|
||||
#define SNB_UNC_CBO_0_PER_CTR0 0x706
|
||||
#define SNB_UNC_CBO_MSR_OFFSET 0x10
|
||||
|
||||
/* NHM global control register */
|
||||
#define NHM_UNC_PERF_GLOBAL_CTL 0x391
|
||||
#define NHM_UNC_FIXED_CTR 0x394
|
||||
#define NHM_UNC_FIXED_CTR_CTRL 0x395
|
||||
|
||||
/* NHM uncore global control */
|
||||
#define NHM_UNC_GLOBAL_CTL_EN_PC_ALL ((1ULL << 8) - 1)
|
||||
#define NHM_UNC_GLOBAL_CTL_EN_FC (1ULL << 32)
|
||||
|
||||
/* NHM uncore register */
|
||||
#define NHM_UNC_PERFEVTSEL0 0x3c0
|
||||
#define NHM_UNC_UNCORE_PMC0 0x3b0
|
||||
|
||||
/* SNB-EP Box level control */
|
||||
#define SNBEP_PMON_BOX_CTL_RST_CTRL (1 << 0)
|
||||
#define SNBEP_PMON_BOX_CTL_RST_CTRS (1 << 1)
|
||||
#define SNBEP_PMON_BOX_CTL_FRZ (1 << 8)
|
||||
#define SNBEP_PMON_BOX_CTL_FRZ_EN (1 << 16)
|
||||
#define SNBEP_PMON_BOX_CTL_INT (SNBEP_PMON_BOX_CTL_RST_CTRL | \
|
||||
SNBEP_PMON_BOX_CTL_RST_CTRS | \
|
||||
SNBEP_PMON_BOX_CTL_FRZ_EN)
|
||||
/* SNB-EP event control */
|
||||
#define SNBEP_PMON_CTL_EV_SEL_MASK 0x000000ff
|
||||
#define SNBEP_PMON_CTL_UMASK_MASK 0x0000ff00
|
||||
#define SNBEP_PMON_CTL_RST (1 << 17)
|
||||
#define SNBEP_PMON_CTL_EDGE_DET (1 << 18)
|
||||
#define SNBEP_PMON_CTL_EV_SEL_EXT (1 << 21)
|
||||
#define SNBEP_PMON_CTL_EN (1 << 22)
|
||||
#define SNBEP_PMON_CTL_INVERT (1 << 23)
|
||||
#define SNBEP_PMON_CTL_TRESH_MASK 0xff000000
|
||||
#define SNBEP_PMON_RAW_EVENT_MASK (SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_UMASK_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_PMON_CTL_INVERT | \
|
||||
SNBEP_PMON_CTL_TRESH_MASK)
|
||||
|
||||
/* SNB-EP Ubox event control */
|
||||
#define SNBEP_U_MSR_PMON_CTL_TRESH_MASK 0x1f000000
|
||||
#define SNBEP_U_MSR_PMON_RAW_EVENT_MASK \
|
||||
(SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_UMASK_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_PMON_CTL_INVERT | \
|
||||
SNBEP_U_MSR_PMON_CTL_TRESH_MASK)
|
||||
|
||||
#define SNBEP_CBO_PMON_CTL_TID_EN (1 << 19)
|
||||
#define SNBEP_CBO_MSR_PMON_RAW_EVENT_MASK (SNBEP_PMON_RAW_EVENT_MASK | \
|
||||
SNBEP_CBO_PMON_CTL_TID_EN)
|
||||
|
||||
/* SNB-EP PCU event control */
|
||||
#define SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK 0x0000c000
|
||||
#define SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK 0x1f000000
|
||||
#define SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT (1 << 30)
|
||||
#define SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET (1 << 31)
|
||||
#define SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK \
|
||||
(SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_PMON_CTL_EV_SEL_EXT | \
|
||||
SNBEP_PMON_CTL_INVERT | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET)
|
||||
|
||||
#define SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK \
|
||||
(SNBEP_PMON_RAW_EVENT_MASK | \
|
||||
SNBEP_PMON_CTL_EV_SEL_EXT)
|
||||
|
||||
/* SNB-EP pci control register */
|
||||
#define SNBEP_PCI_PMON_BOX_CTL 0xf4
|
||||
#define SNBEP_PCI_PMON_CTL0 0xd8
|
||||
/* SNB-EP pci counter register */
|
||||
#define SNBEP_PCI_PMON_CTR0 0xa0
|
||||
|
||||
/* SNB-EP home agent register */
|
||||
#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH0 0x40
|
||||
#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH1 0x44
|
||||
#define SNBEP_HA_PCI_PMON_BOX_OPCODEMATCH 0x48
|
||||
/* SNB-EP memory controller register */
|
||||
#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTL 0xf0
|
||||
#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTR 0xd0
|
||||
/* SNB-EP QPI register */
|
||||
#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH0 0x228
|
||||
#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH1 0x22c
|
||||
#define SNBEP_Q_Py_PCI_PMON_PKT_MASK0 0x238
|
||||
#define SNBEP_Q_Py_PCI_PMON_PKT_MASK1 0x23c
|
||||
|
||||
/* SNB-EP Ubox register */
|
||||
#define SNBEP_U_MSR_PMON_CTR0 0xc16
|
||||
#define SNBEP_U_MSR_PMON_CTL0 0xc10
|
||||
|
||||
#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTL 0xc08
|
||||
#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTR 0xc09
|
||||
|
||||
/* SNB-EP Cbo register */
|
||||
#define SNBEP_C0_MSR_PMON_CTR0 0xd16
|
||||
#define SNBEP_C0_MSR_PMON_CTL0 0xd10
|
||||
#define SNBEP_C0_MSR_PMON_BOX_CTL 0xd04
|
||||
#define SNBEP_C0_MSR_PMON_BOX_FILTER 0xd14
|
||||
#define SNBEP_CBO_MSR_OFFSET 0x20
|
||||
|
||||
#define SNBEP_CB0_MSR_PMON_BOX_FILTER_TID 0x1f
|
||||
#define SNBEP_CB0_MSR_PMON_BOX_FILTER_NID 0x3fc00
|
||||
#define SNBEP_CB0_MSR_PMON_BOX_FILTER_STATE 0x7c0000
|
||||
#define SNBEP_CB0_MSR_PMON_BOX_FILTER_OPC 0xff800000
|
||||
|
||||
#define SNBEP_CBO_EVENT_EXTRA_REG(e, m, i) { \
|
||||
.event = (e), \
|
||||
.msr = SNBEP_C0_MSR_PMON_BOX_FILTER, \
|
||||
.config_mask = (m), \
|
||||
.idx = (i) \
|
||||
}
|
||||
|
||||
/* SNB-EP PCU register */
|
||||
#define SNBEP_PCU_MSR_PMON_CTR0 0xc36
|
||||
#define SNBEP_PCU_MSR_PMON_CTL0 0xc30
|
||||
#define SNBEP_PCU_MSR_PMON_BOX_CTL 0xc24
|
||||
#define SNBEP_PCU_MSR_PMON_BOX_FILTER 0xc34
|
||||
#define SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK 0xffffffff
|
||||
#define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc
|
||||
#define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd
|
||||
|
||||
/* IVT event control */
|
||||
#define IVT_PMON_BOX_CTL_INT (SNBEP_PMON_BOX_CTL_RST_CTRL | \
|
||||
SNBEP_PMON_BOX_CTL_RST_CTRS)
|
||||
#define IVT_PMON_RAW_EVENT_MASK (SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_UMASK_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_PMON_CTL_TRESH_MASK)
|
||||
/* IVT Ubox */
|
||||
#define IVT_U_MSR_PMON_GLOBAL_CTL 0xc00
|
||||
#define IVT_U_PMON_GLOBAL_FRZ_ALL (1 << 31)
|
||||
#define IVT_U_PMON_GLOBAL_UNFRZ_ALL (1 << 29)
|
||||
|
||||
#define IVT_U_MSR_PMON_RAW_EVENT_MASK \
|
||||
(SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_UMASK_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_U_MSR_PMON_CTL_TRESH_MASK)
|
||||
/* IVT Cbo */
|
||||
#define IVT_CBO_MSR_PMON_RAW_EVENT_MASK (IVT_PMON_RAW_EVENT_MASK | \
|
||||
SNBEP_CBO_PMON_CTL_TID_EN)
|
||||
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_TID (0x1fULL << 0)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_LINK (0xfULL << 5)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_STATE (0x3fULL << 17)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_NID (0xffffULL << 32)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_OPC (0x1ffULL << 52)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_C6 (0x1ULL << 61)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_NC (0x1ULL << 62)
|
||||
#define IVT_CB0_MSR_PMON_BOX_FILTER_IOSC (0x1ULL << 63)
|
||||
|
||||
/* IVT home agent */
|
||||
#define IVT_HA_PCI_PMON_CTL_Q_OCC_RST (1 << 16)
|
||||
#define IVT_HA_PCI_PMON_RAW_EVENT_MASK \
|
||||
(IVT_PMON_RAW_EVENT_MASK | \
|
||||
IVT_HA_PCI_PMON_CTL_Q_OCC_RST)
|
||||
/* IVT PCU */
|
||||
#define IVT_PCU_MSR_PMON_RAW_EVENT_MASK \
|
||||
(SNBEP_PMON_CTL_EV_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_EV_SEL_EXT | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \
|
||||
SNBEP_PMON_CTL_EDGE_DET | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \
|
||||
SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET)
|
||||
/* IVT QPI */
|
||||
#define IVT_QPI_PCI_PMON_RAW_EVENT_MASK \
|
||||
(IVT_PMON_RAW_EVENT_MASK | \
|
||||
SNBEP_PMON_CTL_EV_SEL_EXT)
|
||||
|
||||
/* NHM-EX event control */
|
||||
#define NHMEX_PMON_CTL_EV_SEL_MASK 0x000000ff
|
||||
#define NHMEX_PMON_CTL_UMASK_MASK 0x0000ff00
|
||||
#define NHMEX_PMON_CTL_EN_BIT0 (1 << 0)
|
||||
#define NHMEX_PMON_CTL_EDGE_DET (1 << 18)
|
||||
#define NHMEX_PMON_CTL_PMI_EN (1 << 20)
|
||||
#define NHMEX_PMON_CTL_EN_BIT22 (1 << 22)
|
||||
#define NHMEX_PMON_CTL_INVERT (1 << 23)
|
||||
#define NHMEX_PMON_CTL_TRESH_MASK 0xff000000
|
||||
#define NHMEX_PMON_RAW_EVENT_MASK (NHMEX_PMON_CTL_EV_SEL_MASK | \
|
||||
NHMEX_PMON_CTL_UMASK_MASK | \
|
||||
NHMEX_PMON_CTL_EDGE_DET | \
|
||||
NHMEX_PMON_CTL_INVERT | \
|
||||
NHMEX_PMON_CTL_TRESH_MASK)
|
||||
|
||||
/* NHM-EX Ubox */
|
||||
#define NHMEX_U_MSR_PMON_GLOBAL_CTL 0xc00
|
||||
#define NHMEX_U_MSR_PMON_CTR 0xc11
|
||||
#define NHMEX_U_MSR_PMON_EV_SEL 0xc10
|
||||
|
||||
#define NHMEX_U_PMON_GLOBAL_EN (1 << 0)
|
||||
#define NHMEX_U_PMON_GLOBAL_PMI_CORE_SEL 0x0000001e
|
||||
#define NHMEX_U_PMON_GLOBAL_EN_ALL (1 << 28)
|
||||
#define NHMEX_U_PMON_GLOBAL_RST_ALL (1 << 29)
|
||||
#define NHMEX_U_PMON_GLOBAL_FRZ_ALL (1 << 31)
|
||||
|
||||
#define NHMEX_U_PMON_RAW_EVENT_MASK \
|
||||
(NHMEX_PMON_CTL_EV_SEL_MASK | \
|
||||
NHMEX_PMON_CTL_EDGE_DET)
|
||||
|
||||
/* NHM-EX Cbox */
|
||||
#define NHMEX_C0_MSR_PMON_GLOBAL_CTL 0xd00
|
||||
#define NHMEX_C0_MSR_PMON_CTR0 0xd11
|
||||
#define NHMEX_C0_MSR_PMON_EV_SEL0 0xd10
|
||||
#define NHMEX_C_MSR_OFFSET 0x20
|
||||
|
||||
/* NHM-EX Bbox */
|
||||
#define NHMEX_B0_MSR_PMON_GLOBAL_CTL 0xc20
|
||||
#define NHMEX_B0_MSR_PMON_CTR0 0xc31
|
||||
#define NHMEX_B0_MSR_PMON_CTL0 0xc30
|
||||
#define NHMEX_B_MSR_OFFSET 0x40
|
||||
#define NHMEX_B0_MSR_MATCH 0xe45
|
||||
#define NHMEX_B0_MSR_MASK 0xe46
|
||||
#define NHMEX_B1_MSR_MATCH 0xe4d
|
||||
#define NHMEX_B1_MSR_MASK 0xe4e
|
||||
|
||||
#define NHMEX_B_PMON_CTL_EN (1 << 0)
|
||||
#define NHMEX_B_PMON_CTL_EV_SEL_SHIFT 1
|
||||
#define NHMEX_B_PMON_CTL_EV_SEL_MASK \
|
||||
(0x1f << NHMEX_B_PMON_CTL_EV_SEL_SHIFT)
|
||||
#define NHMEX_B_PMON_CTR_SHIFT 6
|
||||
#define NHMEX_B_PMON_CTR_MASK \
|
||||
(0x3 << NHMEX_B_PMON_CTR_SHIFT)
|
||||
#define NHMEX_B_PMON_RAW_EVENT_MASK \
|
||||
(NHMEX_B_PMON_CTL_EV_SEL_MASK | \
|
||||
NHMEX_B_PMON_CTR_MASK)
|
||||
|
||||
/* NHM-EX Sbox */
|
||||
#define NHMEX_S0_MSR_PMON_GLOBAL_CTL 0xc40
|
||||
#define NHMEX_S0_MSR_PMON_CTR0 0xc51
|
||||
#define NHMEX_S0_MSR_PMON_CTL0 0xc50
|
||||
#define NHMEX_S_MSR_OFFSET 0x80
|
||||
#define NHMEX_S0_MSR_MM_CFG 0xe48
|
||||
#define NHMEX_S0_MSR_MATCH 0xe49
|
||||
#define NHMEX_S0_MSR_MASK 0xe4a
|
||||
#define NHMEX_S1_MSR_MM_CFG 0xe58
|
||||
#define NHMEX_S1_MSR_MATCH 0xe59
|
||||
#define NHMEX_S1_MSR_MASK 0xe5a
|
||||
|
||||
#define NHMEX_S_PMON_MM_CFG_EN (0x1ULL << 63)
|
||||
#define NHMEX_S_EVENT_TO_R_PROG_EV 0
|
||||
|
||||
/* NHM-EX Mbox */
|
||||
#define NHMEX_M0_MSR_GLOBAL_CTL 0xca0
|
||||
#define NHMEX_M0_MSR_PMU_DSP 0xca5
|
||||
#define NHMEX_M0_MSR_PMU_ISS 0xca6
|
||||
#define NHMEX_M0_MSR_PMU_MAP 0xca7
|
||||
#define NHMEX_M0_MSR_PMU_MSC_THR 0xca8
|
||||
#define NHMEX_M0_MSR_PMU_PGT 0xca9
|
||||
#define NHMEX_M0_MSR_PMU_PLD 0xcaa
|
||||
#define NHMEX_M0_MSR_PMU_ZDP_CTL_FVC 0xcab
|
||||
#define NHMEX_M0_MSR_PMU_CTL0 0xcb0
|
||||
#define NHMEX_M0_MSR_PMU_CNT0 0xcb1
|
||||
#define NHMEX_M_MSR_OFFSET 0x40
|
||||
#define NHMEX_M0_MSR_PMU_MM_CFG 0xe54
|
||||
#define NHMEX_M1_MSR_PMU_MM_CFG 0xe5c
|
||||
|
||||
#define NHMEX_M_PMON_MM_CFG_EN (1ULL << 63)
|
||||
#define NHMEX_M_PMON_ADDR_MATCH_MASK 0x3ffffffffULL
|
||||
#define NHMEX_M_PMON_ADDR_MASK_MASK 0x7ffffffULL
|
||||
#define NHMEX_M_PMON_ADDR_MASK_SHIFT 34
|
||||
|
||||
#define NHMEX_M_PMON_CTL_EN (1 << 0)
|
||||
#define NHMEX_M_PMON_CTL_PMI_EN (1 << 1)
|
||||
#define NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT 2
|
||||
#define NHMEX_M_PMON_CTL_COUNT_MODE_MASK \
|
||||
(0x3 << NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT)
|
||||
#define NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT 4
|
||||
#define NHMEX_M_PMON_CTL_STORAGE_MODE_MASK \
|
||||
(0x3 << NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT)
|
||||
#define NHMEX_M_PMON_CTL_WRAP_MODE (1 << 6)
|
||||
#define NHMEX_M_PMON_CTL_FLAG_MODE (1 << 7)
|
||||
#define NHMEX_M_PMON_CTL_INC_SEL_SHIFT 9
|
||||
#define NHMEX_M_PMON_CTL_INC_SEL_MASK \
|
||||
(0x1f << NHMEX_M_PMON_CTL_INC_SEL_SHIFT)
|
||||
#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT 19
|
||||
#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK \
|
||||
(0x7 << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT)
|
||||
#define NHMEX_M_PMON_RAW_EVENT_MASK \
|
||||
(NHMEX_M_PMON_CTL_COUNT_MODE_MASK | \
|
||||
NHMEX_M_PMON_CTL_STORAGE_MODE_MASK | \
|
||||
NHMEX_M_PMON_CTL_WRAP_MODE | \
|
||||
NHMEX_M_PMON_CTL_FLAG_MODE | \
|
||||
NHMEX_M_PMON_CTL_INC_SEL_MASK | \
|
||||
NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK)
|
||||
|
||||
#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 11) - 1) | (1 << 23))
|
||||
#define NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7ULL << (11 + 3 * (n)))
|
||||
|
||||
#define WSMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 12) - 1) | (1 << 24))
|
||||
#define WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7ULL << (12 + 3 * (n)))
|
||||
|
||||
/*
|
||||
* use the 9~13 bits to select event If the 7th bit is not set,
|
||||
* otherwise use the 19~21 bits to select event.
|
||||
*/
|
||||
#define MBOX_INC_SEL(x) ((x) << NHMEX_M_PMON_CTL_INC_SEL_SHIFT)
|
||||
#define MBOX_SET_FLAG_SEL(x) (((x) << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT) | \
|
||||
NHMEX_M_PMON_CTL_FLAG_MODE)
|
||||
#define MBOX_INC_SEL_MASK (NHMEX_M_PMON_CTL_INC_SEL_MASK | \
|
||||
NHMEX_M_PMON_CTL_FLAG_MODE)
|
||||
#define MBOX_SET_FLAG_SEL_MASK (NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK | \
|
||||
NHMEX_M_PMON_CTL_FLAG_MODE)
|
||||
#define MBOX_INC_SEL_EXTAR_REG(c, r) \
|
||||
EVENT_EXTRA_REG(MBOX_INC_SEL(c), NHMEX_M0_MSR_PMU_##r, \
|
||||
MBOX_INC_SEL_MASK, (u64)-1, NHMEX_M_##r)
|
||||
#define MBOX_SET_FLAG_SEL_EXTRA_REG(c, r) \
|
||||
EVENT_EXTRA_REG(MBOX_SET_FLAG_SEL(c), NHMEX_M0_MSR_PMU_##r, \
|
||||
MBOX_SET_FLAG_SEL_MASK, \
|
||||
(u64)-1, NHMEX_M_##r)
|
||||
|
||||
/* NHM-EX Rbox */
|
||||
#define NHMEX_R_MSR_GLOBAL_CTL 0xe00
|
||||
#define NHMEX_R_MSR_PMON_CTL0 0xe10
|
||||
#define NHMEX_R_MSR_PMON_CNT0 0xe11
|
||||
#define NHMEX_R_MSR_OFFSET 0x20
|
||||
|
||||
#define NHMEX_R_MSR_PORTN_QLX_CFG(n) \
|
||||
((n) < 4 ? (0xe0c + (n)) : (0xe2c + (n) - 4))
|
||||
#define NHMEX_R_MSR_PORTN_IPERF_CFG0(n) (0xe04 + (n))
|
||||
#define NHMEX_R_MSR_PORTN_IPERF_CFG1(n) (0xe24 + (n))
|
||||
#define NHMEX_R_MSR_PORTN_XBR_OFFSET(n) \
|
||||
(((n) < 4 ? 0 : 0x10) + (n) * 4)
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) \
|
||||
(0xe60 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n))
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(n) \
|
||||
(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 1)
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET1_MASK(n) \
|
||||
(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 2)
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) \
|
||||
(0xe70 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n))
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(n) \
|
||||
(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 1)
|
||||
#define NHMEX_R_MSR_PORTN_XBR_SET2_MASK(n) \
|
||||
(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 2)
|
||||
|
||||
#define NHMEX_R_PMON_CTL_EN (1 << 0)
|
||||
#define NHMEX_R_PMON_CTL_EV_SEL_SHIFT 1
|
||||
#define NHMEX_R_PMON_CTL_EV_SEL_MASK \
|
||||
(0x1f << NHMEX_R_PMON_CTL_EV_SEL_SHIFT)
|
||||
#define NHMEX_R_PMON_CTL_PMI_EN (1 << 6)
|
||||
#define NHMEX_R_PMON_RAW_EVENT_MASK NHMEX_R_PMON_CTL_EV_SEL_MASK
|
||||
|
||||
/* NHM-EX Wbox */
|
||||
#define NHMEX_W_MSR_GLOBAL_CTL 0xc80
|
||||
#define NHMEX_W_MSR_PMON_CNT0 0xc90
|
||||
#define NHMEX_W_MSR_PMON_EVT_SEL0 0xc91
|
||||
#define NHMEX_W_MSR_PMON_FIXED_CTR 0x394
|
||||
#define NHMEX_W_MSR_PMON_FIXED_CTL 0x395
|
||||
|
||||
#define NHMEX_W_PMON_GLOBAL_FIXED_EN (1ULL << 31)
|
||||
|
||||
struct intel_uncore_ops;
|
||||
struct intel_uncore_pmu;
|
||||
struct intel_uncore_box;
|
||||
@ -505,6 +116,9 @@ struct uncore_event_desc {
|
||||
const char *config;
|
||||
};
|
||||
|
||||
ssize_t uncore_event_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
|
||||
#define INTEL_UNCORE_EVENT_DESC(_name, _config) \
|
||||
{ \
|
||||
.attr = __ATTR(_name, 0444, uncore_event_show, NULL), \
|
||||
@ -522,15 +136,6 @@ static ssize_t __uncore_##_var##_show(struct kobject *kobj, \
|
||||
static struct kobj_attribute format_attr_##_var = \
|
||||
__ATTR(_name, 0444, __uncore_##_var##_show, NULL)
|
||||
|
||||
|
||||
static ssize_t uncore_event_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct uncore_event_desc *event =
|
||||
container_of(attr, struct uncore_event_desc, attr);
|
||||
return sprintf(buf, "%s", event->config);
|
||||
}
|
||||
|
||||
static inline unsigned uncore_pci_box_ctl(struct intel_uncore_box *box)
|
||||
{
|
||||
return box->pmu->type->box_ctl;
|
||||
@ -694,3 +299,41 @@ static inline bool uncore_box_is_fake(struct intel_uncore_box *box)
|
||||
{
|
||||
return (box->phys_id < 0);
|
||||
}
|
||||
|
||||
struct intel_uncore_pmu *uncore_event_to_pmu(struct perf_event *event);
|
||||
struct intel_uncore_box *uncore_pmu_to_box(struct intel_uncore_pmu *pmu, int cpu);
|
||||
struct intel_uncore_box *uncore_event_to_box(struct perf_event *event);
|
||||
u64 uncore_msr_read_counter(struct intel_uncore_box *box, struct perf_event *event);
|
||||
void uncore_pmu_start_hrtimer(struct intel_uncore_box *box);
|
||||
void uncore_pmu_cancel_hrtimer(struct intel_uncore_box *box);
|
||||
void uncore_pmu_event_read(struct perf_event *event);
|
||||
void uncore_perf_event_update(struct intel_uncore_box *box, struct perf_event *event);
|
||||
struct event_constraint *
|
||||
uncore_get_constraint(struct intel_uncore_box *box, struct perf_event *event);
|
||||
void uncore_put_constraint(struct intel_uncore_box *box, struct perf_event *event);
|
||||
u64 uncore_shared_reg_config(struct intel_uncore_box *box, int idx);
|
||||
|
||||
extern struct intel_uncore_type **uncore_msr_uncores;
|
||||
extern struct intel_uncore_type **uncore_pci_uncores;
|
||||
extern struct pci_driver *uncore_pci_driver;
|
||||
extern int uncore_pcibus_to_physid[256];
|
||||
extern struct pci_dev *uncore_extra_pci_dev[UNCORE_SOCKET_MAX][UNCORE_EXTRA_PCI_DEV_MAX];
|
||||
extern struct event_constraint uncore_constraint_empty;
|
||||
|
||||
/* perf_event_intel_uncore_snb.c */
|
||||
int snb_uncore_pci_init(void);
|
||||
int ivb_uncore_pci_init(void);
|
||||
int hsw_uncore_pci_init(void);
|
||||
void snb_uncore_cpu_init(void);
|
||||
void nhm_uncore_cpu_init(void);
|
||||
|
||||
/* perf_event_intel_uncore_snbep.c */
|
||||
int snbep_uncore_pci_init(void);
|
||||
void snbep_uncore_cpu_init(void);
|
||||
int ivbep_uncore_pci_init(void);
|
||||
void ivbep_uncore_cpu_init(void);
|
||||
int hswep_uncore_pci_init(void);
|
||||
void hswep_uncore_cpu_init(void);
|
||||
|
||||
/* perf_event_intel_uncore_nhmex.c */
|
||||
void nhmex_uncore_cpu_init(void);
|
||||
|
1221
arch/x86/kernel/cpu/perf_event_intel_uncore_nhmex.c
Normal file
1221
arch/x86/kernel/cpu/perf_event_intel_uncore_nhmex.c
Normal file
File diff suppressed because it is too large
Load Diff
636
arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
Normal file
636
arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
Normal file
@ -0,0 +1,636 @@
|
||||
/* Nehalem/SandBridge/Haswell uncore support */
|
||||
#include "perf_event_intel_uncore.h"
|
||||
|
||||
/* SNB event control */
|
||||
#define SNB_UNC_CTL_EV_SEL_MASK 0x000000ff
|
||||
#define SNB_UNC_CTL_UMASK_MASK 0x0000ff00
|
||||
#define SNB_UNC_CTL_EDGE_DET (1 << 18)
|
||||
#define SNB_UNC_CTL_EN (1 << 22)
|
||||
#define SNB_UNC_CTL_INVERT (1 << 23)
|
||||
#define SNB_UNC_CTL_CMASK_MASK 0x1f000000
|
||||
#define NHM_UNC_CTL_CMASK_MASK 0xff000000
|
||||
#define NHM_UNC_FIXED_CTR_CTL_EN (1 << 0)
|
||||
|
||||
#define SNB_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
|
||||
SNB_UNC_CTL_UMASK_MASK | \
|
||||
SNB_UNC_CTL_EDGE_DET | \
|
||||
SNB_UNC_CTL_INVERT | \
|
||||
SNB_UNC_CTL_CMASK_MASK)
|
||||
|
||||
#define NHM_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
|
||||
SNB_UNC_CTL_UMASK_MASK | \
|
||||
SNB_UNC_CTL_EDGE_DET | \
|
||||
SNB_UNC_CTL_INVERT | \
|
||||
NHM_UNC_CTL_CMASK_MASK)
|
||||
|
||||
/* SNB global control register */
|
||||
#define SNB_UNC_PERF_GLOBAL_CTL 0x391
|
||||
#define SNB_UNC_FIXED_CTR_CTRL 0x394
|
||||
#define SNB_UNC_FIXED_CTR 0x395
|
||||
|
||||
/* SNB uncore global control */
|
||||
#define SNB_UNC_GLOBAL_CTL_CORE_ALL ((1 << 4) - 1)
|
||||
#define SNB_UNC_GLOBAL_CTL_EN (1 << 29)
|
||||
|
||||
/* SNB Cbo register */
|
||||
#define SNB_UNC_CBO_0_PERFEVTSEL0 0x700
|
||||
#define SNB_UNC_CBO_0_PER_CTR0 0x706
|
||||
#define SNB_UNC_CBO_MSR_OFFSET 0x10
|
||||
|
||||
/* NHM global control register */
|
||||
#define NHM_UNC_PERF_GLOBAL_CTL 0x391
|
||||
#define NHM_UNC_FIXED_CTR 0x394
|
||||
#define NHM_UNC_FIXED_CTR_CTRL 0x395
|
||||
|
||||
/* NHM uncore global control */
|
||||
#define NHM_UNC_GLOBAL_CTL_EN_PC_ALL ((1ULL << 8) - 1)
|
||||
#define NHM_UNC_GLOBAL_CTL_EN_FC (1ULL << 32)
|
||||
|
||||
/* NHM uncore register */
|
||||
#define NHM_UNC_PERFEVTSEL0 0x3c0
|
||||
#define NHM_UNC_UNCORE_PMC0 0x3b0
|
||||
|
||||
DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7");
|
||||
DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15");
|
||||
DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18");
|
||||
DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23");
|
||||
DEFINE_UNCORE_FORMAT_ATTR(cmask5, cmask, "config:24-28");
|
||||
DEFINE_UNCORE_FORMAT_ATTR(cmask8, cmask, "config:24-31");
|
||||
|
||||
/* Sandy Bridge uncore support */
|
||||
static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
||||
wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
||||
else
|
||||
wrmsrl(hwc->config_base, SNB_UNC_CTL_EN);
|
||||
}
|
||||
|
||||
static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{
|
||||
wrmsrl(event->hw.config_base, 0);
|
||||
}
|
||||
|
||||
static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||
{
|
||||
if (box->pmu->pmu_idx == 0) {
|
||||
wrmsrl(SNB_UNC_PERF_GLOBAL_CTL,
|
||||
SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct uncore_event_desc snb_uncore_events[] = {
|
||||
INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
static struct attribute *snb_uncore_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask5.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group snb_uncore_format_group = {
|
||||
.name = "format",
|
||||
.attrs = snb_uncore_formats_attr,
|
||||
};
|
||||
|
||||
static struct intel_uncore_ops snb_uncore_msr_ops = {
|
||||
.init_box = snb_uncore_msr_init_box,
|
||||
.disable_event = snb_uncore_msr_disable_event,
|
||||
.enable_event = snb_uncore_msr_enable_event,
|
||||
.read_counter = uncore_msr_read_counter,
|
||||
};
|
||||
|
||||
static struct event_constraint snb_uncore_cbox_constraints[] = {
|
||||
UNCORE_EVENT_CONSTRAINT(0x80, 0x1),
|
||||
UNCORE_EVENT_CONSTRAINT(0x83, 0x1),
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
static struct intel_uncore_type snb_uncore_cbox = {
|
||||
.name = "cbox",
|
||||
.num_counters = 2,
|
||||
.num_boxes = 4,
|
||||
.perf_ctr_bits = 44,
|
||||
.fixed_ctr_bits = 48,
|
||||
.perf_ctr = SNB_UNC_CBO_0_PER_CTR0,
|
||||
.event_ctl = SNB_UNC_CBO_0_PERFEVTSEL0,
|
||||
.fixed_ctr = SNB_UNC_FIXED_CTR,
|
||||
.fixed_ctl = SNB_UNC_FIXED_CTR_CTRL,
|
||||
.single_fixed = 1,
|
||||
.event_mask = SNB_UNC_RAW_EVENT_MASK,
|
||||
.msr_offset = SNB_UNC_CBO_MSR_OFFSET,
|
||||
.constraints = snb_uncore_cbox_constraints,
|
||||
.ops = &snb_uncore_msr_ops,
|
||||
.format_group = &snb_uncore_format_group,
|
||||
.event_descs = snb_uncore_events,
|
||||
};
|
||||
|
||||
static struct intel_uncore_type *snb_msr_uncores[] = {
|
||||
&snb_uncore_cbox,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void snb_uncore_cpu_init(void)
|
||||
{
|
||||
uncore_msr_uncores = snb_msr_uncores;
|
||||
if (snb_uncore_cbox.num_boxes > boot_cpu_data.x86_max_cores)
|
||||
snb_uncore_cbox.num_boxes = boot_cpu_data.x86_max_cores;
|
||||
}
|
||||
|
||||
enum {
|
||||
SNB_PCI_UNCORE_IMC,
|
||||
};
|
||||
|
||||
static struct uncore_event_desc snb_uncore_imc_events[] = {
|
||||
INTEL_UNCORE_EVENT_DESC(data_reads, "event=0x01"),
|
||||
INTEL_UNCORE_EVENT_DESC(data_reads.scale, "6.103515625e-5"),
|
||||
INTEL_UNCORE_EVENT_DESC(data_reads.unit, "MiB"),
|
||||
|
||||
INTEL_UNCORE_EVENT_DESC(data_writes, "event=0x02"),
|
||||
INTEL_UNCORE_EVENT_DESC(data_writes.scale, "6.103515625e-5"),
|
||||
INTEL_UNCORE_EVENT_DESC(data_writes.unit, "MiB"),
|
||||
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
#define SNB_UNCORE_PCI_IMC_EVENT_MASK 0xff
|
||||
#define SNB_UNCORE_PCI_IMC_BAR_OFFSET 0x48
|
||||
|
||||
/* page size multiple covering all config regs */
|
||||
#define SNB_UNCORE_PCI_IMC_MAP_SIZE 0x6000
|
||||
|
||||
#define SNB_UNCORE_PCI_IMC_DATA_READS 0x1
|
||||
#define SNB_UNCORE_PCI_IMC_DATA_READS_BASE 0x5050
|
||||
#define SNB_UNCORE_PCI_IMC_DATA_WRITES 0x2
|
||||
#define SNB_UNCORE_PCI_IMC_DATA_WRITES_BASE 0x5054
|
||||
#define SNB_UNCORE_PCI_IMC_CTR_BASE SNB_UNCORE_PCI_IMC_DATA_READS_BASE
|
||||
|
||||
static struct attribute *snb_uncore_imc_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group snb_uncore_imc_format_group = {
|
||||
.name = "format",
|
||||
.attrs = snb_uncore_imc_formats_attr,
|
||||
};
|
||||
|
||||
static void snb_uncore_imc_init_box(struct intel_uncore_box *box)
|
||||
{
|
||||
struct pci_dev *pdev = box->pci_dev;
|
||||
int where = SNB_UNCORE_PCI_IMC_BAR_OFFSET;
|
||||
resource_size_t addr;
|
||||
u32 pci_dword;
|
||||
|
||||
pci_read_config_dword(pdev, where, &pci_dword);
|
||||
addr = pci_dword;
|
||||
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
pci_read_config_dword(pdev, where + 4, &pci_dword);
|
||||
addr |= ((resource_size_t)pci_dword << 32);
|
||||
#endif
|
||||
|
||||
addr &= ~(PAGE_SIZE - 1);
|
||||
|
||||
box->io_addr = ioremap(addr, SNB_UNCORE_PCI_IMC_MAP_SIZE);
|
||||
box->hrtimer_duration = UNCORE_SNB_IMC_HRTIMER_INTERVAL;
|
||||
}
|
||||
|
||||
static void snb_uncore_imc_enable_box(struct intel_uncore_box *box)
|
||||
{}
|
||||
|
||||
static void snb_uncore_imc_disable_box(struct intel_uncore_box *box)
|
||||
{}
|
||||
|
||||
static void snb_uncore_imc_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{}
|
||||
|
||||
static void snb_uncore_imc_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{}
|
||||
|
||||
static u64 snb_uncore_imc_read_counter(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
return (u64)*(unsigned int *)(box->io_addr + hwc->event_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* custom event_init() function because we define our own fixed, free
|
||||
* running counters, so we do not want to conflict with generic uncore
|
||||
* logic. Also simplifies processing
|
||||
*/
|
||||
static int snb_uncore_imc_event_init(struct perf_event *event)
|
||||
{
|
||||
struct intel_uncore_pmu *pmu;
|
||||
struct intel_uncore_box *box;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
u64 cfg = event->attr.config & SNB_UNCORE_PCI_IMC_EVENT_MASK;
|
||||
int idx, base;
|
||||
|
||||
if (event->attr.type != event->pmu->type)
|
||||
return -ENOENT;
|
||||
|
||||
pmu = uncore_event_to_pmu(event);
|
||||
/* no device found for this pmu */
|
||||
if (pmu->func_id < 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* Sampling not supported yet */
|
||||
if (hwc->sample_period)
|
||||
return -EINVAL;
|
||||
|
||||
/* unsupported modes and filters */
|
||||
if (event->attr.exclude_user ||
|
||||
event->attr.exclude_kernel ||
|
||||
event->attr.exclude_hv ||
|
||||
event->attr.exclude_idle ||
|
||||
event->attr.exclude_host ||
|
||||
event->attr.exclude_guest ||
|
||||
event->attr.sample_period) /* no sampling */
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Place all uncore events for a particular physical package
|
||||
* onto a single cpu
|
||||
*/
|
||||
if (event->cpu < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* check only supported bits are set */
|
||||
if (event->attr.config & ~SNB_UNCORE_PCI_IMC_EVENT_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
box = uncore_pmu_to_box(pmu, event->cpu);
|
||||
if (!box || box->cpu < 0)
|
||||
return -EINVAL;
|
||||
|
||||
event->cpu = box->cpu;
|
||||
|
||||
event->hw.idx = -1;
|
||||
event->hw.last_tag = ~0ULL;
|
||||
event->hw.extra_reg.idx = EXTRA_REG_NONE;
|
||||
event->hw.branch_reg.idx = EXTRA_REG_NONE;
|
||||
/*
|
||||
* check event is known (whitelist, determines counter)
|
||||
*/
|
||||
switch (cfg) {
|
||||
case SNB_UNCORE_PCI_IMC_DATA_READS:
|
||||
base = SNB_UNCORE_PCI_IMC_DATA_READS_BASE;
|
||||
idx = UNCORE_PMC_IDX_FIXED;
|
||||
break;
|
||||
case SNB_UNCORE_PCI_IMC_DATA_WRITES:
|
||||
base = SNB_UNCORE_PCI_IMC_DATA_WRITES_BASE;
|
||||
idx = UNCORE_PMC_IDX_FIXED + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* must be done before validate_group */
|
||||
event->hw.event_base = base;
|
||||
event->hw.config = cfg;
|
||||
event->hw.idx = idx;
|
||||
|
||||
/* no group validation needed, we have free running counters */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snb_uncore_imc_hw_config(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snb_uncore_imc_event_start(struct perf_event *event, int flags)
|
||||
{
|
||||
struct intel_uncore_box *box = uncore_event_to_box(event);
|
||||
u64 count;
|
||||
|
||||
if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
|
||||
return;
|
||||
|
||||
event->hw.state = 0;
|
||||
box->n_active++;
|
||||
|
||||
list_add_tail(&event->active_entry, &box->active_list);
|
||||
|
||||
count = snb_uncore_imc_read_counter(box, event);
|
||||
local64_set(&event->hw.prev_count, count);
|
||||
|
||||
if (box->n_active == 1)
|
||||
uncore_pmu_start_hrtimer(box);
|
||||
}
|
||||
|
||||
static void snb_uncore_imc_event_stop(struct perf_event *event, int flags)
|
||||
{
|
||||
struct intel_uncore_box *box = uncore_event_to_box(event);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
if (!(hwc->state & PERF_HES_STOPPED)) {
|
||||
box->n_active--;
|
||||
|
||||
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
|
||||
hwc->state |= PERF_HES_STOPPED;
|
||||
|
||||
list_del(&event->active_entry);
|
||||
|
||||
if (box->n_active == 0)
|
||||
uncore_pmu_cancel_hrtimer(box);
|
||||
}
|
||||
|
||||
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
||||
/*
|
||||
* Drain the remaining delta count out of a event
|
||||
* that we are disabling:
|
||||
*/
|
||||
uncore_perf_event_update(box, event);
|
||||
hwc->state |= PERF_HES_UPTODATE;
|
||||
}
|
||||
}
|
||||
|
||||
static int snb_uncore_imc_event_add(struct perf_event *event, int flags)
|
||||
{
|
||||
struct intel_uncore_box *box = uncore_event_to_box(event);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
if (!box)
|
||||
return -ENODEV;
|
||||
|
||||
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
|
||||
if (!(flags & PERF_EF_START))
|
||||
hwc->state |= PERF_HES_ARCH;
|
||||
|
||||
snb_uncore_imc_event_start(event, 0);
|
||||
|
||||
box->n_events++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snb_uncore_imc_event_del(struct perf_event *event, int flags)
|
||||
{
|
||||
struct intel_uncore_box *box = uncore_event_to_box(event);
|
||||
int i;
|
||||
|
||||
snb_uncore_imc_event_stop(event, PERF_EF_UPDATE);
|
||||
|
||||
for (i = 0; i < box->n_events; i++) {
|
||||
if (event == box->event_list[i]) {
|
||||
--box->n_events;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int snb_pci2phy_map_init(int devid)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
int bus;
|
||||
|
||||
dev = pci_get_device(PCI_VENDOR_ID_INTEL, devid, dev);
|
||||
if (!dev)
|
||||
return -ENOTTY;
|
||||
|
||||
bus = dev->bus->number;
|
||||
|
||||
uncore_pcibus_to_physid[bus] = 0;
|
||||
|
||||
pci_dev_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pmu snb_uncore_imc_pmu = {
|
||||
.task_ctx_nr = perf_invalid_context,
|
||||
.event_init = snb_uncore_imc_event_init,
|
||||
.add = snb_uncore_imc_event_add,
|
||||
.del = snb_uncore_imc_event_del,
|
||||
.start = snb_uncore_imc_event_start,
|
||||
.stop = snb_uncore_imc_event_stop,
|
||||
.read = uncore_pmu_event_read,
|
||||
};
|
||||
|
||||
static struct intel_uncore_ops snb_uncore_imc_ops = {
|
||||
.init_box = snb_uncore_imc_init_box,
|
||||
.enable_box = snb_uncore_imc_enable_box,
|
||||
.disable_box = snb_uncore_imc_disable_box,
|
||||
.disable_event = snb_uncore_imc_disable_event,
|
||||
.enable_event = snb_uncore_imc_enable_event,
|
||||
.hw_config = snb_uncore_imc_hw_config,
|
||||
.read_counter = snb_uncore_imc_read_counter,
|
||||
};
|
||||
|
||||
static struct intel_uncore_type snb_uncore_imc = {
|
||||
.name = "imc",
|
||||
.num_counters = 2,
|
||||
.num_boxes = 1,
|
||||
.fixed_ctr_bits = 32,
|
||||
.fixed_ctr = SNB_UNCORE_PCI_IMC_CTR_BASE,
|
||||
.event_descs = snb_uncore_imc_events,
|
||||
.format_group = &snb_uncore_imc_format_group,
|
||||
.perf_ctr = SNB_UNCORE_PCI_IMC_DATA_READS_BASE,
|
||||
.event_mask = SNB_UNCORE_PCI_IMC_EVENT_MASK,
|
||||
.ops = &snb_uncore_imc_ops,
|
||||
.pmu = &snb_uncore_imc_pmu,
|
||||
};
|
||||
|
||||
static struct intel_uncore_type *snb_pci_uncores[] = {
|
||||
[SNB_PCI_UNCORE_IMC] = &snb_uncore_imc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct pci_device_id snb_uncore_pci_ids[] = {
|
||||
{ /* IMC */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SNB_IMC),
|
||||
.driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
|
||||
},
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
static const struct pci_device_id ivb_uncore_pci_ids[] = {
|
||||
{ /* IMC */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_IMC),
|
||||
.driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
|
||||
},
|
||||
{ /* IMC */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_E3_IMC),
|
||||
.driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
|
||||
},
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
static const struct pci_device_id hsw_uncore_pci_ids[] = {
|
||||
{ /* IMC */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HSW_IMC),
|
||||
.driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
|
||||
},
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
static struct pci_driver snb_uncore_pci_driver = {
|
||||
.name = "snb_uncore",
|
||||
.id_table = snb_uncore_pci_ids,
|
||||
};
|
||||
|
||||
static struct pci_driver ivb_uncore_pci_driver = {
|
||||
.name = "ivb_uncore",
|
||||
.id_table = ivb_uncore_pci_ids,
|
||||
};
|
||||
|
||||
static struct pci_driver hsw_uncore_pci_driver = {
|
||||
.name = "hsw_uncore",
|
||||
.id_table = hsw_uncore_pci_ids,
|
||||
};
|
||||
|
||||
struct imc_uncore_pci_dev {
|
||||
__u32 pci_id;
|
||||
struct pci_driver *driver;
|
||||
};
|
||||
#define IMC_DEV(a, d) \
|
||||
{ .pci_id = PCI_DEVICE_ID_INTEL_##a, .driver = (d) }
|
||||
|
||||
static const struct imc_uncore_pci_dev desktop_imc_pci_ids[] = {
|
||||
IMC_DEV(SNB_IMC, &snb_uncore_pci_driver),
|
||||
IMC_DEV(IVB_IMC, &ivb_uncore_pci_driver), /* 3rd Gen Core processor */
|
||||
IMC_DEV(IVB_E3_IMC, &ivb_uncore_pci_driver), /* Xeon E3-1200 v2/3rd Gen Core processor */
|
||||
IMC_DEV(HSW_IMC, &hsw_uncore_pci_driver), /* 4th Gen Core Processor */
|
||||
{ /* end marker */ }
|
||||
};
|
||||
|
||||
|
||||
#define for_each_imc_pci_id(x, t) \
|
||||
for (x = (t); (x)->pci_id; x++)
|
||||
|
||||
static struct pci_driver *imc_uncore_find_dev(void)
|
||||
{
|
||||
const struct imc_uncore_pci_dev *p;
|
||||
int ret;
|
||||
|
||||
for_each_imc_pci_id(p, desktop_imc_pci_ids) {
|
||||
ret = snb_pci2phy_map_init(p->pci_id);
|
||||
if (ret == 0)
|
||||
return p->driver;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int imc_uncore_pci_init(void)
|
||||
{
|
||||
struct pci_driver *imc_drv = imc_uncore_find_dev();
|
||||
|
||||
if (!imc_drv)
|
||||
return -ENODEV;
|
||||
|
||||
uncore_pci_uncores = snb_pci_uncores;
|
||||
uncore_pci_driver = imc_drv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snb_uncore_pci_init(void)
|
||||
{
|
||||
return imc_uncore_pci_init();
|
||||
}
|
||||
|
||||
int ivb_uncore_pci_init(void)
|
||||
{
|
||||
return imc_uncore_pci_init();
|
||||
}
|
||||
int hsw_uncore_pci_init(void)
|
||||
{
|
||||
return imc_uncore_pci_init();
|
||||
}
|
||||
|
||||
/* end of Sandy Bridge uncore support */
|
||||
|
||||
/* Nehalem uncore support */
|
||||
static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||
{
|
||||
wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0);
|
||||
}
|
||||
|
||||
static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||
{
|
||||
wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC);
|
||||
}
|
||||
|
||||
static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
||||
wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
||||
else
|
||||
wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN);
|
||||
}
|
||||
|
||||
static struct attribute *nhm_uncore_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask8.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group nhm_uncore_format_group = {
|
||||
.name = "format",
|
||||
.attrs = nhm_uncore_formats_attr,
|
||||
};
|
||||
|
||||
static struct uncore_event_desc nhm_uncore_events[] = {
|
||||
INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
|
||||
INTEL_UNCORE_EVENT_DESC(qmc_writes_full_any, "event=0x2f,umask=0x0f"),
|
||||
INTEL_UNCORE_EVENT_DESC(qmc_normal_reads_any, "event=0x2c,umask=0x0f"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_reads, "event=0x20,umask=0x01"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_writes, "event=0x20,umask=0x02"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_remote_reads, "event=0x20,umask=0x04"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_remote_writes, "event=0x20,umask=0x08"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_local_reads, "event=0x20,umask=0x10"),
|
||||
INTEL_UNCORE_EVENT_DESC(qhl_request_local_writes, "event=0x20,umask=0x20"),
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
static struct intel_uncore_ops nhm_uncore_msr_ops = {
|
||||
.disable_box = nhm_uncore_msr_disable_box,
|
||||
.enable_box = nhm_uncore_msr_enable_box,
|
||||
.disable_event = snb_uncore_msr_disable_event,
|
||||
.enable_event = nhm_uncore_msr_enable_event,
|
||||
.read_counter = uncore_msr_read_counter,
|
||||
};
|
||||
|
||||
static struct intel_uncore_type nhm_uncore = {
|
||||
.name = "",
|
||||
.num_counters = 8,
|
||||
.num_boxes = 1,
|
||||
.perf_ctr_bits = 48,
|
||||
.fixed_ctr_bits = 48,
|
||||
.event_ctl = NHM_UNC_PERFEVTSEL0,
|
||||
.perf_ctr = NHM_UNC_UNCORE_PMC0,
|
||||
.fixed_ctr = NHM_UNC_FIXED_CTR,
|
||||
.fixed_ctl = NHM_UNC_FIXED_CTR_CTRL,
|
||||
.event_mask = NHM_UNC_RAW_EVENT_MASK,
|
||||
.event_descs = nhm_uncore_events,
|
||||
.ops = &nhm_uncore_msr_ops,
|
||||
.format_group = &nhm_uncore_format_group,
|
||||
};
|
||||
|
||||
static struct intel_uncore_type *nhm_msr_uncores[] = {
|
||||
&nhm_uncore,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void nhm_uncore_cpu_init(void)
|
||||
{
|
||||
uncore_msr_uncores = nhm_msr_uncores;
|
||||
}
|
||||
|
||||
/* end of Nehalem uncore support */
|
2258
arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
Normal file
2258
arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,28 +8,28 @@
|
||||
* Copyright (C) 2011-2012 Peter Zijlstra <pzijlstr@redhat.com>
|
||||
*
|
||||
* Jump labels provide an interface to generate dynamic branches using
|
||||
* self-modifying code. Assuming toolchain and architecture support the result
|
||||
* of a "if (static_key_false(&key))" statement is a unconditional branch (which
|
||||
* self-modifying code. Assuming toolchain and architecture support, the result
|
||||
* of a "if (static_key_false(&key))" statement is an unconditional branch (which
|
||||
* defaults to false - and the true block is placed out of line).
|
||||
*
|
||||
* However at runtime we can change the branch target using
|
||||
* static_key_slow_{inc,dec}(). These function as a 'reference' count on the key
|
||||
* object and for as long as there are references all branches referring to
|
||||
* object, and for as long as there are references all branches referring to
|
||||
* that particular key will point to the (out of line) true block.
|
||||
*
|
||||
* Since this relies on modifying code the static_key_slow_{inc,dec}() functions
|
||||
* Since this relies on modifying code, the static_key_slow_{inc,dec}() functions
|
||||
* must be considered absolute slow paths (machine wide synchronization etc.).
|
||||
* OTOH, since the affected branches are unconditional their runtime overhead
|
||||
* OTOH, since the affected branches are unconditional, their runtime overhead
|
||||
* will be absolutely minimal, esp. in the default (off) case where the total
|
||||
* effect is a single NOP of appropriate size. The on case will patch in a jump
|
||||
* to the out-of-line block.
|
||||
*
|
||||
* When the control is directly exposed to userspace it is prudent to delay the
|
||||
* When the control is directly exposed to userspace, it is prudent to delay the
|
||||
* decrement to avoid high frequency code modifications which can (and do)
|
||||
* cause significant performance degradation. Struct static_key_deferred and
|
||||
* static_key_slow_dec_deferred() provide for this.
|
||||
*
|
||||
* Lacking toolchain and or architecture support, it falls back to a simple
|
||||
* Lacking toolchain and or architecture support, jump labels fall back to a simple
|
||||
* conditional branch.
|
||||
*
|
||||
* struct static_key my_key = STATIC_KEY_INIT_TRUE;
|
||||
@ -43,8 +43,7 @@
|
||||
*
|
||||
* Not initializing the key (static data is initialized to 0s anyway) is the
|
||||
* same as using STATIC_KEY_INIT_FALSE.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
@ -2538,6 +2538,7 @@
|
||||
#define PCI_DEVICE_ID_INTEL_EESSC 0x0008
|
||||
#define PCI_DEVICE_ID_INTEL_SNB_IMC 0x0100
|
||||
#define PCI_DEVICE_ID_INTEL_IVB_IMC 0x0154
|
||||
#define PCI_DEVICE_ID_INTEL_IVB_E3_IMC 0x0150
|
||||
#define PCI_DEVICE_ID_INTEL_HSW_IMC 0x0c00
|
||||
#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320
|
||||
#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321
|
||||
|
@ -52,6 +52,7 @@ struct perf_guest_info_callbacks {
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/perf_regs.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/local.h>
|
||||
|
||||
struct perf_callchain_entry {
|
||||
@ -268,6 +269,7 @@ struct pmu {
|
||||
* enum perf_event_active_state - the states of a event
|
||||
*/
|
||||
enum perf_event_active_state {
|
||||
PERF_EVENT_STATE_EXIT = -3,
|
||||
PERF_EVENT_STATE_ERROR = -2,
|
||||
PERF_EVENT_STATE_OFF = -1,
|
||||
PERF_EVENT_STATE_INACTIVE = 0,
|
||||
@ -507,6 +509,9 @@ struct perf_event_context {
|
||||
int nr_cgroups; /* cgroup evts */
|
||||
int nr_branch_stack; /* branch_stack evt */
|
||||
struct rcu_head rcu_head;
|
||||
|
||||
struct delayed_work orphans_remove;
|
||||
bool orphans_remove_sched;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -604,6 +609,13 @@ struct perf_sample_data {
|
||||
u64 txn;
|
||||
};
|
||||
|
||||
/* default value for data source */
|
||||
#define PERF_MEM_NA (PERF_MEM_S(OP, NA) |\
|
||||
PERF_MEM_S(LVL, NA) |\
|
||||
PERF_MEM_S(SNOOP, NA) |\
|
||||
PERF_MEM_S(LOCK, NA) |\
|
||||
PERF_MEM_S(TLB, NA))
|
||||
|
||||
static inline void perf_sample_data_init(struct perf_sample_data *data,
|
||||
u64 addr, u64 period)
|
||||
{
|
||||
@ -616,7 +628,7 @@ static inline void perf_sample_data_init(struct perf_sample_data *data,
|
||||
data->regs_user.regs = NULL;
|
||||
data->stack_user_size = 0;
|
||||
data->weight = 0;
|
||||
data->data_src.val = 0;
|
||||
data->data_src.val = PERF_MEM_NA;
|
||||
data->txn = 0;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ static void release_callchain_buffers(void)
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
entries = callchain_cpus_entries;
|
||||
rcu_assign_pointer(callchain_cpus_entries, NULL);
|
||||
RCU_INIT_POINTER(callchain_cpus_entries, NULL);
|
||||
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,8 @@
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
static struct workqueue_struct *perf_wq;
|
||||
|
||||
struct remote_function_call {
|
||||
struct task_struct *p;
|
||||
int (*func)(void *info);
|
||||
@ -120,6 +122,13 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
#define EVENT_OWNER_KERNEL ((void *) -1)
|
||||
|
||||
static bool is_kernel_event(struct perf_event *event)
|
||||
{
|
||||
return event->owner == EVENT_OWNER_KERNEL;
|
||||
}
|
||||
|
||||
#define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\
|
||||
PERF_FLAG_FD_OUTPUT |\
|
||||
PERF_FLAG_PID_CGROUP |\
|
||||
@ -1370,6 +1379,45 @@ out:
|
||||
perf_event__header_size(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* User event without the task.
|
||||
*/
|
||||
static bool is_orphaned_event(struct perf_event *event)
|
||||
{
|
||||
return event && !is_kernel_event(event) && !event->owner;
|
||||
}
|
||||
|
||||
/*
|
||||
* Event has a parent but parent's task finished and it's
|
||||
* alive only because of children holding refference.
|
||||
*/
|
||||
static bool is_orphaned_child(struct perf_event *event)
|
||||
{
|
||||
return is_orphaned_event(event->parent);
|
||||
}
|
||||
|
||||
static void orphans_remove_work(struct work_struct *work);
|
||||
|
||||
static void schedule_orphans_remove(struct perf_event_context *ctx)
|
||||
{
|
||||
if (!ctx->task || ctx->orphans_remove_sched || !perf_wq)
|
||||
return;
|
||||
|
||||
if (queue_delayed_work(perf_wq, &ctx->orphans_remove, 1)) {
|
||||
get_ctx(ctx);
|
||||
ctx->orphans_remove_sched = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init perf_workqueue_init(void)
|
||||
{
|
||||
perf_wq = create_singlethread_workqueue("perf");
|
||||
WARN(!perf_wq, "failed to create perf workqueue\n");
|
||||
return perf_wq ? 0 : -1;
|
||||
}
|
||||
|
||||
core_initcall(perf_workqueue_init);
|
||||
|
||||
static inline int
|
||||
event_filter_match(struct perf_event *event)
|
||||
{
|
||||
@ -1419,6 +1467,9 @@ event_sched_out(struct perf_event *event,
|
||||
if (event->attr.exclusive || !cpuctx->active_oncpu)
|
||||
cpuctx->exclusive = 0;
|
||||
|
||||
if (is_orphaned_child(event))
|
||||
schedule_orphans_remove(ctx);
|
||||
|
||||
perf_pmu_enable(event->pmu);
|
||||
}
|
||||
|
||||
@ -1726,6 +1777,9 @@ event_sched_in(struct perf_event *event,
|
||||
if (event->attr.exclusive)
|
||||
cpuctx->exclusive = 1;
|
||||
|
||||
if (is_orphaned_child(event))
|
||||
schedule_orphans_remove(ctx);
|
||||
|
||||
out:
|
||||
perf_pmu_enable(event->pmu);
|
||||
|
||||
@ -2326,7 +2380,7 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
|
||||
next_parent = rcu_dereference(next_ctx->parent_ctx);
|
||||
|
||||
/* If neither context have a parent context; they cannot be clones. */
|
||||
if (!parent || !next_parent)
|
||||
if (!parent && !next_parent)
|
||||
goto unlock;
|
||||
|
||||
if (next_parent == ctx || next_ctx == parent || next_parent == parent) {
|
||||
@ -3073,6 +3127,7 @@ static void __perf_event_init_context(struct perf_event_context *ctx)
|
||||
INIT_LIST_HEAD(&ctx->flexible_groups);
|
||||
INIT_LIST_HEAD(&ctx->event_list);
|
||||
atomic_set(&ctx->refcount, 1);
|
||||
INIT_DELAYED_WORK(&ctx->orphans_remove, orphans_remove_work);
|
||||
}
|
||||
|
||||
static struct perf_event_context *
|
||||
@ -3318,16 +3373,12 @@ static void free_event(struct perf_event *event)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the last reference to the file is gone.
|
||||
* Remove user event from the owner task.
|
||||
*/
|
||||
static void put_event(struct perf_event *event)
|
||||
static void perf_remove_from_owner(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
struct task_struct *owner;
|
||||
|
||||
if (!atomic_long_dec_and_test(&event->refcount))
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
owner = ACCESS_ONCE(event->owner);
|
||||
/*
|
||||
@ -3360,6 +3411,20 @@ static void put_event(struct perf_event *event)
|
||||
mutex_unlock(&owner->perf_event_mutex);
|
||||
put_task_struct(owner);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the last reference to the file is gone.
|
||||
*/
|
||||
static void put_event(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
|
||||
if (!atomic_long_dec_and_test(&event->refcount))
|
||||
return;
|
||||
|
||||
if (!is_kernel_event(event))
|
||||
perf_remove_from_owner(event);
|
||||
|
||||
WARN_ON_ONCE(ctx->parent_ctx);
|
||||
/*
|
||||
@ -3394,6 +3459,42 @@ static int perf_release(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all orphanes events from the context.
|
||||
*/
|
||||
static void orphans_remove_work(struct work_struct *work)
|
||||
{
|
||||
struct perf_event_context *ctx;
|
||||
struct perf_event *event, *tmp;
|
||||
|
||||
ctx = container_of(work, struct perf_event_context,
|
||||
orphans_remove.work);
|
||||
|
||||
mutex_lock(&ctx->mutex);
|
||||
list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry) {
|
||||
struct perf_event *parent_event = event->parent;
|
||||
|
||||
if (!is_orphaned_child(event))
|
||||
continue;
|
||||
|
||||
perf_remove_from_context(event, true);
|
||||
|
||||
mutex_lock(&parent_event->child_mutex);
|
||||
list_del_init(&event->child_list);
|
||||
mutex_unlock(&parent_event->child_mutex);
|
||||
|
||||
free_event(event);
|
||||
put_event(parent_event);
|
||||
}
|
||||
|
||||
raw_spin_lock_irq(&ctx->lock);
|
||||
ctx->orphans_remove_sched = false;
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
mutex_unlock(&ctx->mutex);
|
||||
|
||||
put_ctx(ctx);
|
||||
}
|
||||
|
||||
u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
|
||||
{
|
||||
struct perf_event *child;
|
||||
@ -3491,6 +3592,19 @@ static int perf_event_read_one(struct perf_event *event,
|
||||
return n * sizeof(u64);
|
||||
}
|
||||
|
||||
static bool is_event_hup(struct perf_event *event)
|
||||
{
|
||||
bool no_children;
|
||||
|
||||
if (event->state != PERF_EVENT_STATE_EXIT)
|
||||
return false;
|
||||
|
||||
mutex_lock(&event->child_mutex);
|
||||
no_children = list_empty(&event->child_list);
|
||||
mutex_unlock(&event->child_mutex);
|
||||
return no_children;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the performance event - simple non blocking version for now
|
||||
*/
|
||||
@ -3532,7 +3646,12 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct perf_event *event = file->private_data;
|
||||
struct ring_buffer *rb;
|
||||
unsigned int events = POLL_HUP;
|
||||
unsigned int events = POLLHUP;
|
||||
|
||||
poll_wait(file, &event->waitq, wait);
|
||||
|
||||
if (is_event_hup(event))
|
||||
return events;
|
||||
|
||||
/*
|
||||
* Pin the event->rb by taking event->mmap_mutex; otherwise
|
||||
@ -3543,9 +3662,6 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
|
||||
if (rb)
|
||||
events = atomic_xchg(&rb->poll, 0);
|
||||
mutex_unlock(&event->mmap_mutex);
|
||||
|
||||
poll_wait(file, &event->waitq, wait);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
@ -5809,7 +5925,7 @@ static void swevent_hlist_release(struct swevent_htable *swhash)
|
||||
if (!hlist)
|
||||
return;
|
||||
|
||||
rcu_assign_pointer(swhash->swevent_hlist, NULL);
|
||||
RCU_INIT_POINTER(swhash->swevent_hlist, NULL);
|
||||
kfree_rcu(hlist, rcu_head);
|
||||
}
|
||||
|
||||
@ -7392,6 +7508,9 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Mark owner so we could distinguish it from user events. */
|
||||
event->owner = EVENT_OWNER_KERNEL;
|
||||
|
||||
account_event(event);
|
||||
|
||||
ctx = find_get_context(event->pmu, task, cpu);
|
||||
@ -7478,6 +7597,12 @@ static void sync_child_event(struct perf_event *child_event,
|
||||
list_del_init(&child_event->child_list);
|
||||
mutex_unlock(&parent_event->child_mutex);
|
||||
|
||||
/*
|
||||
* Make sure user/parent get notified, that we just
|
||||
* lost one event.
|
||||
*/
|
||||
perf_event_wakeup(parent_event);
|
||||
|
||||
/*
|
||||
* Release the parent event, if this was the last
|
||||
* reference to it.
|
||||
@ -7512,6 +7637,9 @@ __perf_event_exit_task(struct perf_event *child_event,
|
||||
if (child_event->parent) {
|
||||
sync_child_event(child_event, child);
|
||||
free_event(child_event);
|
||||
} else {
|
||||
child_event->state = PERF_EVENT_STATE_EXIT;
|
||||
perf_event_wakeup(child_event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7695,6 +7823,7 @@ inherit_event(struct perf_event *parent_event,
|
||||
struct perf_event *group_leader,
|
||||
struct perf_event_context *child_ctx)
|
||||
{
|
||||
enum perf_event_active_state parent_state = parent_event->state;
|
||||
struct perf_event *child_event;
|
||||
unsigned long flags;
|
||||
|
||||
@ -7715,7 +7844,8 @@ inherit_event(struct perf_event *parent_event,
|
||||
if (IS_ERR(child_event))
|
||||
return child_event;
|
||||
|
||||
if (!atomic_long_inc_not_zero(&parent_event->refcount)) {
|
||||
if (is_orphaned_event(parent_event) ||
|
||||
!atomic_long_inc_not_zero(&parent_event->refcount)) {
|
||||
free_event(child_event);
|
||||
return NULL;
|
||||
}
|
||||
@ -7727,7 +7857,7 @@ inherit_event(struct perf_event *parent_event,
|
||||
* not its attr.disabled bit. We hold the parent's mutex,
|
||||
* so we won't race with perf_event_{en, dis}able_family.
|
||||
*/
|
||||
if (parent_event->state >= PERF_EVENT_STATE_INACTIVE)
|
||||
if (parent_state >= PERF_EVENT_STATE_INACTIVE)
|
||||
child_event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
else
|
||||
child_event->state = PERF_EVENT_STATE_OFF;
|
||||
|
@ -10,9 +10,14 @@ LIB_OBJS=
|
||||
|
||||
LIB_H += fs/debugfs.h
|
||||
LIB_H += fs/fs.h
|
||||
# See comment below about piggybacking...
|
||||
LIB_H += fd/array.h
|
||||
|
||||
LIB_OBJS += $(OUTPUT)fs/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)fs/fs.o
|
||||
# XXX piggybacking here, need to introduce libapikfd, or rename this
|
||||
# to plain libapik.a and make it have it all api goodies
|
||||
LIB_OBJS += $(OUTPUT)fd/array.o
|
||||
|
||||
LIBFILE = libapikfs.a
|
||||
|
||||
@ -29,7 +34,7 @@ $(LIBFILE): $(LIB_OBJS)
|
||||
$(LIB_OBJS): $(LIB_H)
|
||||
|
||||
libapi_dirs:
|
||||
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/
|
||||
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fd $(OUTPUT)fs
|
||||
|
||||
$(OUTPUT)%.o: %.c libapi_dirs
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
|
127
tools/lib/api/fd/array.c
Normal file
127
tools/lib/api/fd/array.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
|
||||
*
|
||||
* Released under the GPL v2. (and only v2, not any later version)
|
||||
*/
|
||||
#include "array.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void fdarray__init(struct fdarray *fda, int nr_autogrow)
|
||||
{
|
||||
fda->entries = NULL;
|
||||
fda->priv = NULL;
|
||||
fda->nr = fda->nr_alloc = 0;
|
||||
fda->nr_autogrow = nr_autogrow;
|
||||
}
|
||||
|
||||
int fdarray__grow(struct fdarray *fda, int nr)
|
||||
{
|
||||
void *priv;
|
||||
int nr_alloc = fda->nr_alloc + nr;
|
||||
size_t psize = sizeof(fda->priv[0]) * nr_alloc;
|
||||
size_t size = sizeof(struct pollfd) * nr_alloc;
|
||||
struct pollfd *entries = realloc(fda->entries, size);
|
||||
|
||||
if (entries == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = realloc(fda->priv, psize);
|
||||
if (priv == NULL) {
|
||||
free(entries);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fda->nr_alloc = nr_alloc;
|
||||
fda->entries = entries;
|
||||
fda->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow)
|
||||
{
|
||||
struct fdarray *fda = calloc(1, sizeof(*fda));
|
||||
|
||||
if (fda != NULL) {
|
||||
if (fdarray__grow(fda, nr_alloc)) {
|
||||
free(fda);
|
||||
fda = NULL;
|
||||
} else {
|
||||
fda->nr_autogrow = nr_autogrow;
|
||||
}
|
||||
}
|
||||
|
||||
return fda;
|
||||
}
|
||||
|
||||
void fdarray__exit(struct fdarray *fda)
|
||||
{
|
||||
free(fda->entries);
|
||||
free(fda->priv);
|
||||
fdarray__init(fda, 0);
|
||||
}
|
||||
|
||||
void fdarray__delete(struct fdarray *fda)
|
||||
{
|
||||
fdarray__exit(fda);
|
||||
free(fda);
|
||||
}
|
||||
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents)
|
||||
{
|
||||
int pos = fda->nr;
|
||||
|
||||
if (fda->nr == fda->nr_alloc &&
|
||||
fdarray__grow(fda, fda->nr_autogrow) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fda->entries[fda->nr].fd = fd;
|
||||
fda->entries[fda->nr].events = revents;
|
||||
fda->nr++;
|
||||
return pos;
|
||||
}
|
||||
|
||||
int fdarray__filter(struct fdarray *fda, short revents,
|
||||
void (*entry_destructor)(struct fdarray *fda, int fd))
|
||||
{
|
||||
int fd, nr = 0;
|
||||
|
||||
if (fda->nr == 0)
|
||||
return 0;
|
||||
|
||||
for (fd = 0; fd < fda->nr; ++fd) {
|
||||
if (fda->entries[fd].revents & revents) {
|
||||
if (entry_destructor)
|
||||
entry_destructor(fda, fd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd != nr) {
|
||||
fda->entries[nr] = fda->entries[fd];
|
||||
fda->priv[nr] = fda->priv[fd];
|
||||
}
|
||||
|
||||
++nr;
|
||||
}
|
||||
|
||||
return fda->nr = nr;
|
||||
}
|
||||
|
||||
int fdarray__poll(struct fdarray *fda, int timeout)
|
||||
{
|
||||
return poll(fda->entries, fda->nr, timeout);
|
||||
}
|
||||
|
||||
int fdarray__fprintf(struct fdarray *fda, FILE *fp)
|
||||
{
|
||||
int fd, printed = fprintf(fp, "%d [ ", fda->nr);
|
||||
|
||||
for (fd = 0; fd < fda->nr; ++fd)
|
||||
printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd);
|
||||
|
||||
return printed + fprintf(fp, " ]");
|
||||
}
|
46
tools/lib/api/fd/array.h
Normal file
46
tools/lib/api/fd/array.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef __API_FD_ARRAY__
|
||||
#define __API_FD_ARRAY__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct pollfd;
|
||||
|
||||
/**
|
||||
* struct fdarray: Array of file descriptors
|
||||
*
|
||||
* @priv: Per array entry priv area, users should access just its contents,
|
||||
* not set it to anything, as it is kept in synch with @entries, being
|
||||
* realloc'ed, * for instance, in fdarray__{grow,filter}.
|
||||
*
|
||||
* I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
|
||||
* but doing 'fda->priv = malloc(M)' is not allowed.
|
||||
*/
|
||||
struct fdarray {
|
||||
int nr;
|
||||
int nr_alloc;
|
||||
int nr_autogrow;
|
||||
struct pollfd *entries;
|
||||
union {
|
||||
int idx;
|
||||
} *priv;
|
||||
};
|
||||
|
||||
void fdarray__init(struct fdarray *fda, int nr_autogrow);
|
||||
void fdarray__exit(struct fdarray *fda);
|
||||
|
||||
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
|
||||
void fdarray__delete(struct fdarray *fda);
|
||||
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents);
|
||||
int fdarray__poll(struct fdarray *fda, int timeout);
|
||||
int fdarray__filter(struct fdarray *fda, short revents,
|
||||
void (*entry_destructor)(struct fdarray *fda, int fd));
|
||||
int fdarray__grow(struct fdarray *fda, int extra);
|
||||
int fdarray__fprintf(struct fdarray *fda, FILE *fp);
|
||||
|
||||
static inline int fdarray__available_entries(struct fdarray *fda)
|
||||
{
|
||||
return fda->nr_alloc - fda->nr;
|
||||
}
|
||||
|
||||
#endif /* __API_FD_ARRAY__ */
|
1
tools/perf/.gitignore
vendored
1
tools/perf/.gitignore
vendored
@ -15,6 +15,7 @@ perf.data
|
||||
perf.data.old
|
||||
output.svg
|
||||
perf-archive
|
||||
perf-with-kcore
|
||||
tags
|
||||
TAGS
|
||||
cscope*
|
||||
|
@ -104,6 +104,9 @@ OPTIONS
|
||||
Specify path to the executable or shared library file for user
|
||||
space tracing. Can also be used with --funcs option.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbols.
|
||||
|
||||
In absence of -m/-x options, perf probe checks if the first argument after
|
||||
the options is an absolute path name. If its an absolute path, perf probe
|
||||
uses it as a target module/target user space binary to probe.
|
||||
|
@ -147,7 +147,7 @@ OPTIONS
|
||||
-w::
|
||||
--column-widths=<width[,width...]>::
|
||||
Force each column width to the provided list, for large terminal
|
||||
readability.
|
||||
readability. 0 means no limit (default behavior).
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
@ -276,6 +276,9 @@ OPTIONS
|
||||
Demangle symbol names to human readable form. It's enabled by default,
|
||||
disable with --no-demangle.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbol names to human readable form (for C++ kernels).
|
||||
|
||||
--mem-mode::
|
||||
Use the data addresses of samples in addition to instruction addresses
|
||||
to build the histograms. To generate meaningful output, the perf.data
|
||||
|
@ -98,6 +98,9 @@ Default is to monitor all CPUS.
|
||||
--hide_user_symbols::
|
||||
Hide user symbols.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbols.
|
||||
|
||||
-D::
|
||||
--dump-symtab::
|
||||
Dump the symbol table used for profiling.
|
||||
@ -193,6 +196,12 @@ Default is to monitor all CPUS.
|
||||
sum of shown entries will be always 100%. "absolute" means it retains
|
||||
the original value before and after the filter is applied.
|
||||
|
||||
-w::
|
||||
--column-widths=<width[,width...]>::
|
||||
Force each column width to the provided list, for large terminal
|
||||
readability. 0 means no limit (default behavior).
|
||||
|
||||
|
||||
INTERACTIVE PROMPTING KEYS
|
||||
--------------------------
|
||||
|
||||
|
@ -126,6 +126,7 @@ PYRF_OBJS =
|
||||
SCRIPT_SH =
|
||||
|
||||
SCRIPT_SH += perf-archive.sh
|
||||
SCRIPT_SH += perf-with-kcore.sh
|
||||
|
||||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
@ -263,6 +264,7 @@ LIB_H += util/xyarray.h
|
||||
LIB_H += util/header.h
|
||||
LIB_H += util/help.h
|
||||
LIB_H += util/session.h
|
||||
LIB_H += util/ordered-events.h
|
||||
LIB_H += util/strbuf.h
|
||||
LIB_H += util/strlist.h
|
||||
LIB_H += util/strfilter.h
|
||||
@ -347,6 +349,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o
|
||||
LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/ordered-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/comm.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread_map.o
|
||||
@ -399,6 +402,7 @@ LIB_OBJS += $(OUTPUT)tests/perf-record.o
|
||||
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
|
||||
LIB_OBJS += $(OUTPUT)tests/fdarray.o
|
||||
LIB_OBJS += $(OUTPUT)tests/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_common.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_link.o
|
||||
@ -423,6 +427,7 @@ endif
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
|
||||
LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
|
||||
LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
@ -765,7 +770,7 @@ $(LIBTRACEEVENT)-clean:
|
||||
install-traceevent-plugins: $(LIBTRACEEVENT)
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins
|
||||
|
||||
LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch])
|
||||
LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch])
|
||||
|
||||
# if subdir is set, we've been called from above so target has been built
|
||||
# already
|
||||
@ -875,6 +880,8 @@ install-bin: all install-gtk
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-archive) \
|
||||
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-with-kcore) \
|
||||
$(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
ifndef NO_LIBPERL
|
||||
$(call QUIET_INSTALL, perl-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
@ -920,7 +927,7 @@ config-clean:
|
||||
@$(MAKE) -C config/feature-checks clean >/dev/null
|
||||
|
||||
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
|
||||
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
#define STACK_SIZE 8192
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <asm/perf_regs.h>
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_ARM64_MAX
|
||||
|
||||
#define PERF_REG_IP PERF_REG_ARM64_PC
|
||||
#define PERF_REG_SP PERF_REG_ARM64_SP
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
|
@ -12,6 +12,11 @@ const char *const arm_triplets[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const arm64_triplets[] = {
|
||||
"aarch64-linux-android-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const powerpc_triplets[] = {
|
||||
"powerpc-unknown-linux-gnu-",
|
||||
"powerpc64-unknown-linux-gnu-",
|
||||
@ -105,6 +110,8 @@ static const char *normalize_arch(char *arch)
|
||||
return "x86";
|
||||
if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5))
|
||||
return "sparc";
|
||||
if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64"))
|
||||
return "arm64";
|
||||
if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110"))
|
||||
return "arm";
|
||||
if (!strncmp(arch, "s390", 4))
|
||||
@ -159,6 +166,8 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
|
||||
if (!strcmp(arch, "arm"))
|
||||
path_list = arm_triplets;
|
||||
else if (!strcmp(arch, "arm64"))
|
||||
path_list = arm64_triplets;
|
||||
else if (!strcmp(arch, "powerpc"))
|
||||
path_list = powerpc_triplets;
|
||||
else if (!strcmp(arch, "sh"))
|
||||
|
@ -1,6 +1,6 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
/*
|
||||
* When saving the callchain on Power, the kernel conservatively saves
|
||||
|
@ -26,6 +26,7 @@ static unsigned int nsecs = 10;
|
||||
/* amount of futexes per thread */
|
||||
static unsigned int nfutexes = 1024;
|
||||
static bool fshared = false, done = false, silent = false;
|
||||
static int futex_flag = 0;
|
||||
|
||||
struct timeval start, end, runtime;
|
||||
static pthread_mutex_t thread_lock;
|
||||
@ -75,8 +76,7 @@ static void *workerfn(void *arg)
|
||||
* such as internal waitqueue handling, thus enlarging
|
||||
* the critical region protected by hb->lock.
|
||||
*/
|
||||
ret = futex_wait(&w->futex[i], 1234, NULL,
|
||||
fshared ? 0 : FUTEX_PRIVATE_FLAG);
|
||||
ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
|
||||
if (!silent &&
|
||||
(!ret || errno != EAGAIN || errno != EWOULDBLOCK))
|
||||
warn("Non-expected futex return call");
|
||||
@ -135,6 +135,9 @@ int bench_futex_hash(int argc, const char **argv,
|
||||
if (!worker)
|
||||
goto errmem;
|
||||
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
|
||||
getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs);
|
||||
|
||||
|
@ -30,16 +30,18 @@ static u_int32_t futex1 = 0, futex2 = 0;
|
||||
static unsigned int nrequeue = 1;
|
||||
|
||||
static pthread_t *worker;
|
||||
static bool done = 0, silent = 0;
|
||||
static bool done = false, silent = false, fshared = false;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
static struct stats requeuetime_stats, requeued_stats;
|
||||
static unsigned int ncpus, threads_starting, nthreads = 0;
|
||||
static int futex_flag = 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_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -70,7 +72,7 @@ static void *workerfn(void *arg __maybe_unused)
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG);
|
||||
futex_wait(&futex1, 0, NULL, futex_flag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -127,9 +129,12 @@ int bench_futex_requeue(int argc, const char **argv,
|
||||
if (!worker)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
|
||||
printf("Run summary [PID %d]: Requeuing %d threads (from %p to %p), "
|
||||
"%d at a time.\n\n",
|
||||
getpid(), nthreads, &futex1, &futex2, nrequeue);
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), "
|
||||
"%d at a time.\n\n", getpid(), nthreads,
|
||||
fshared ? "shared":"private", &futex1, &futex2, nrequeue);
|
||||
|
||||
init_stats(&requeued_stats);
|
||||
init_stats(&requeuetime_stats);
|
||||
@ -156,16 +161,20 @@ int bench_futex_requeue(int argc, const char **argv,
|
||||
|
||||
/* Ok, all threads are patiently blocked, start requeueing */
|
||||
gettimeofday(&start, NULL);
|
||||
for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue)
|
||||
for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) {
|
||||
/*
|
||||
* Do not wakeup any tasks blocked on futex1, allowing
|
||||
* us to really measure futex_wait functionality.
|
||||
*/
|
||||
futex_cmp_requeue(&futex1, 0, &futex2, 0, nrequeue,
|
||||
FUTEX_PRIVATE_FLAG);
|
||||
futex_cmp_requeue(&futex1, 0, &futex2, 0,
|
||||
nrequeue, futex_flag);
|
||||
}
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
|
||||
if (nrequeued > nthreads)
|
||||
nrequeued = nthreads;
|
||||
|
||||
update_stats(&requeued_stats, nrequeued);
|
||||
update_stats(&requeuetime_stats, runtime.tv_usec);
|
||||
|
||||
@ -175,7 +184,7 @@ int bench_futex_requeue(int argc, const char **argv,
|
||||
}
|
||||
|
||||
/* everybody should be blocked on futex2, wake'em up */
|
||||
nrequeued = futex_wake(&futex2, nthreads, FUTEX_PRIVATE_FLAG);
|
||||
nrequeued = futex_wake(&futex2, nthreads, futex_flag);
|
||||
if (nthreads != nrequeued)
|
||||
warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads);
|
||||
|
||||
@ -184,7 +193,6 @@ int bench_futex_requeue(int argc, const char **argv,
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_join");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* cleanup & report results */
|
||||
|
@ -31,16 +31,18 @@ static u_int32_t futex1 = 0;
|
||||
static unsigned int nwakes = 1;
|
||||
|
||||
pthread_t *worker;
|
||||
static bool done = false, silent = false;
|
||||
static bool done = false, silent = false, fshared = false;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
static struct stats waketime_stats, wakeup_stats;
|
||||
static unsigned int ncpus, threads_starting, nthreads = 0;
|
||||
static int futex_flag = 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_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -58,7 +60,7 @@ static void *workerfn(void *arg __maybe_unused)
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG);
|
||||
futex_wait(&futex1, 0, NULL, futex_flag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -130,9 +132,12 @@ int bench_futex_wake(int argc, const char **argv,
|
||||
if (!worker)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
|
||||
printf("Run summary [PID %d]: blocking on %d threads (at futex %p), "
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
|
||||
"waking up %d at a time.\n\n",
|
||||
getpid(), nthreads, &futex1, nwakes);
|
||||
getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes);
|
||||
|
||||
init_stats(&wakeup_stats);
|
||||
init_stats(&waketime_stats);
|
||||
@ -160,7 +165,7 @@ int bench_futex_wake(int argc, const char **argv,
|
||||
/* Ok, all threads are patiently blocked, start waking folks up */
|
||||
gettimeofday(&start, NULL);
|
||||
while (nwoken != nthreads)
|
||||
nwoken += futex_wake(&futex1, nwakes, FUTEX_PRIVATE_FLAG);
|
||||
nwoken += futex_wake(&futex1, nwakes, futex_flag);
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
|
@ -36,7 +36,8 @@
|
||||
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
bool force, use_tui, use_stdio, use_gtk;
|
||||
struct perf_session *session;
|
||||
bool use_tui, use_stdio, use_gtk;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
bool skip_missing;
|
||||
@ -188,18 +189,9 @@ find_next:
|
||||
static int __cmd_annotate(struct perf_annotate *ann)
|
||||
{
|
||||
int ret;
|
||||
struct perf_session *session;
|
||||
struct perf_session *session = ann->session;
|
||||
struct perf_evsel *pos;
|
||||
u64 total_nr_samples;
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = ann->force,
|
||||
};
|
||||
|
||||
session = perf_session__new(&file, false, &ann->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
machines__set_symbol_filter(&session->machines, symbol__annotate_init);
|
||||
|
||||
@ -207,22 +199,22 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
ret = perf_session__cpu_bitmap(session, ann->cpu_list,
|
||||
ann->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&session->header.env);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session, &ann->tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
goto out;
|
||||
|
||||
if (dump_trace) {
|
||||
perf_session__fprintf_nr_events(session, stdout);
|
||||
goto out_delete;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
@ -250,8 +242,8 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
}
|
||||
|
||||
if (total_nr_samples == 0) {
|
||||
ui__error("The %s file has no samples!\n", file.path);
|
||||
goto out_delete;
|
||||
ui__error("The %s file has no samples!\n", session->file->path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (use_browser == 2) {
|
||||
@ -261,24 +253,12 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
"perf_gtk__show_annotations");
|
||||
if (show_annotations == NULL) {
|
||||
ui__error("GTK browser not found!\n");
|
||||
goto out_delete;
|
||||
goto out;
|
||||
}
|
||||
show_annotations();
|
||||
}
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
* Speed up the exit process, for large files this can
|
||||
* take quite a while.
|
||||
*
|
||||
* XXX Enable this when using valgrind or if we ever
|
||||
* librarize this command.
|
||||
*
|
||||
* Also experiment with obstacks to see how much speed
|
||||
* up we'll get here.
|
||||
*
|
||||
* perf_session__delete(session);
|
||||
*/
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -297,10 +277,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
@ -308,7 +292,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
|
||||
"symbol to annotate"),
|
||||
OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
@ -341,6 +325,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"Show event group information together"),
|
||||
OPT_END()
|
||||
};
|
||||
int ret;
|
||||
|
||||
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
||||
|
||||
@ -353,11 +338,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
setup_browser(true);
|
||||
|
||||
annotate.session = perf_session__new(&file, false, &annotate.tool);
|
||||
if (annotate.session == NULL)
|
||||
return -1;
|
||||
|
||||
symbol_conf.priv_size = sizeof(struct annotation);
|
||||
symbol_conf.try_vmlinux_path = true;
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
ret = symbol__init(&annotate.session->header.env);
|
||||
if (ret < 0)
|
||||
goto out_delete;
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(annotate_usage, options);
|
||||
@ -373,5 +363,20 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
annotate.sym_hist_filter = argv[0];
|
||||
}
|
||||
|
||||
return __cmd_annotate(&annotate);
|
||||
ret = __cmd_annotate(&annotate);
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
* Speed up the exit process, for large files this can
|
||||
* take quite a while.
|
||||
*
|
||||
* XXX Enable this when using valgrind or if we ever
|
||||
* librarize this command.
|
||||
*
|
||||
* Also experiment with obstacks to see how much speed
|
||||
* up we'll get here.
|
||||
*
|
||||
* perf_session__delete(session);
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
@ -246,20 +246,9 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp)
|
||||
static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
|
||||
{
|
||||
struct perf_data_file file = {
|
||||
.path = filename,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = force,
|
||||
};
|
||||
struct perf_session *session = perf_session__new(&file, false, NULL);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
|
||||
perf_session__delete(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -302,6 +291,12 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
*missing_filename = NULL,
|
||||
*update_name_list_str = NULL,
|
||||
*kcore_filename;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
struct perf_data_file file = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
struct perf_session *session = NULL;
|
||||
|
||||
const struct option buildid_cache_options[] = {
|
||||
OPT_STRING('a', "add", &add_name_list_str,
|
||||
@ -326,8 +321,17 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
argc = parse_options(argc, argv, buildid_cache_options,
|
||||
buildid_cache_usage, 0);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
if (missing_filename) {
|
||||
file.path = missing_filename;
|
||||
file.force = force;
|
||||
|
||||
session = perf_session__new(&file, false, NULL);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (symbol__init(session ? &session->header.env : NULL) < 0)
|
||||
goto out;
|
||||
|
||||
setup_pager();
|
||||
|
||||
@ -344,7 +348,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't add %s: %s\n",
|
||||
pos->s, strerror(errno));
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
@ -362,7 +366,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't remove %s: %s\n",
|
||||
pos->s, strerror(errno));
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
@ -370,7 +374,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
}
|
||||
|
||||
if (missing_filename)
|
||||
ret = build_id_cache__fprintf_missing(missing_filename, force, stdout);
|
||||
ret = build_id_cache__fprintf_missing(session, stdout);
|
||||
|
||||
if (update_name_list_str) {
|
||||
list = strlist__new(true, update_name_list_str);
|
||||
@ -383,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't update %s: %s\n",
|
||||
pos->s, strerror(errno));
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
@ -394,5 +398,9 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
build_id_cache__add_kcore(kcore_filename, debugdir, force))
|
||||
pr_warning("Couldn't add %s\n", kcore_filename);
|
||||
|
||||
out:
|
||||
if (session)
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ static struct perf_tool tool = {
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
};
|
||||
|
||||
@ -683,7 +683,7 @@ static int __cmd_diff(void)
|
||||
d->session = perf_session__new(&d->file, false, &tool);
|
||||
if (!d->session) {
|
||||
pr_err("Failed to open %s\n", d->file.path);
|
||||
ret = -ENOMEM;
|
||||
ret = -1;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
@ -1143,7 +1143,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
argc = parse_options(argc, argv, options, diff_usage, 0);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
if (symbol__init(NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (data_init(argc, argv) < 0)
|
||||
|
@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
||||
|
||||
session = perf_session__new(&file, 0, NULL);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
evlist__for_each(session->evlist, pos)
|
||||
perf_evsel__fprintf(pos, details, stdout);
|
||||
|
@ -103,6 +103,8 @@ static int check_emacsclient_version(void)
|
||||
|
||||
static void exec_woman_emacs(const char *path, const char *page)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (!check_emacsclient_version()) {
|
||||
/* This works only with emacsclient version >= 22. */
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
@ -111,16 +113,19 @@ static void exec_woman_emacs(const char *path, const char *page)
|
||||
path = "emacsclient";
|
||||
strbuf_addf(&man_page, "(woman \"%s\")", page);
|
||||
execlp(path, "emacsclient", "-e", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
warning("failed to exec '%s': %s", path,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_konqueror(const char *path, const char *page)
|
||||
{
|
||||
const char *display = getenv("DISPLAY");
|
||||
|
||||
if (display && *display) {
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
const char *filename = "kfmclient";
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
/* It's simpler to launch konqueror using kfmclient. */
|
||||
if (path) {
|
||||
@ -139,24 +144,31 @@ static void exec_man_konqueror(const char *path, const char *page)
|
||||
path = "kfmclient";
|
||||
strbuf_addf(&man_page, "man:%s(1)", page);
|
||||
execlp(path, filename, "newTab", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
warning("failed to exec '%s': %s", path,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_man(const char *path, const char *page)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (!path)
|
||||
path = "man";
|
||||
execlp(path, "man", page, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
warning("failed to exec '%s': %s", path,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
static void exec_man_cmd(const char *cmd, const char *page)
|
||||
{
|
||||
struct strbuf shell_cmd = STRBUF_INIT;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
|
||||
execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
|
||||
warning("failed to exec '%s': %s", cmd, strerror(errno));
|
||||
warning("failed to exec '%s': %s", cmd,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
static void add_man_viewer(const char *name)
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
struct perf_inject {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
bool build_ids;
|
||||
bool sched_stat;
|
||||
const char *input_name;
|
||||
@ -340,12 +341,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
|
||||
|
||||
static int __cmd_inject(struct perf_inject *inject)
|
||||
{
|
||||
struct perf_session *session;
|
||||
int ret = -EINVAL;
|
||||
struct perf_data_file file = {
|
||||
.path = inject->input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
struct perf_session *session = inject->session;
|
||||
struct perf_data_file *file_out = &inject->output;
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
@ -357,16 +354,12 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
inject->tool.tracing_data = perf_event__repipe_tracing_data;
|
||||
}
|
||||
|
||||
session = perf_session__new(&file, true, &inject->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (inject->build_ids) {
|
||||
inject->tool.sample = perf_event__inject_buildid;
|
||||
} else if (inject->sched_stat) {
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
inject->tool.ordered_samples = true;
|
||||
inject->tool.ordered_events = true;
|
||||
|
||||
evlist__for_each(session->evlist, evsel) {
|
||||
const char *name = perf_evsel__name(evsel);
|
||||
@ -396,8 +389,6 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
perf_session__write_header(session, session->evlist, file_out->fd, true);
|
||||
}
|
||||
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -427,6 +418,11 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.mode = PERF_DATA_MODE_WRITE,
|
||||
},
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
int ret;
|
||||
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
|
||||
"Inject build-ids into the output stream"),
|
||||
@ -461,8 +457,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
file.path = inject.input_name;
|
||||
inject.session = perf_session__new(&file, true, &inject.tool);
|
||||
if (inject.session == NULL)
|
||||
return -1;
|
||||
|
||||
return __cmd_inject(&inject);
|
||||
if (symbol__init(&inject.session->header.env) < 0)
|
||||
return -1;
|
||||
|
||||
ret = __cmd_inject(&inject);
|
||||
|
||||
perf_session__delete(inject.session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -256,7 +256,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static struct perf_tool perf_kmem = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.ordered_events = true,
|
||||
};
|
||||
|
||||
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
|
||||
@ -403,10 +405,9 @@ static void sort_result(void)
|
||||
__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
|
||||
}
|
||||
|
||||
static int __cmd_kmem(void)
|
||||
static int __cmd_kmem(struct perf_session *session)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session;
|
||||
const struct perf_evsel_str_handler kmem_tracepoints[] = {
|
||||
{ "kmem:kmalloc", perf_evsel__process_alloc_event, },
|
||||
{ "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, },
|
||||
@ -415,34 +416,22 @@ static int __cmd_kmem(void)
|
||||
{ "kmem:kfree", perf_evsel__process_free_event, },
|
||||
{ "kmem:kmem_cache_free", perf_evsel__process_free_event, },
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
|
||||
session = perf_session__new(&file, false, &perf_kmem);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (perf_session__create_kernel_maps(session) < 0)
|
||||
goto out_delete;
|
||||
|
||||
if (!perf_session__has_traces(session, "kmem record"))
|
||||
goto out_delete;
|
||||
goto out;
|
||||
|
||||
if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
|
||||
pr_err("Initializing perf session tracepoint handlers failed\n");
|
||||
return -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
setup_pager();
|
||||
err = perf_session__process_events(session, &perf_kmem);
|
||||
if (err != 0)
|
||||
goto out_delete;
|
||||
goto out;
|
||||
sort_result();
|
||||
print_result(session);
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -689,29 +678,46 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
struct perf_session *session;
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
int ret = -1;
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, kmem_options,
|
||||
kmem_subcommands, kmem_usage, 0);
|
||||
|
||||
if (!argc)
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
||||
symbol__init();
|
||||
|
||||
if (!strncmp(argv[0], "rec", 3)) {
|
||||
symbol__init(NULL);
|
||||
return __cmd_record(argc, argv);
|
||||
} else if (!strcmp(argv[0], "stat")) {
|
||||
}
|
||||
|
||||
session = perf_session__new(&file, false, &perf_kmem);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
if (!strcmp(argv[0], "stat")) {
|
||||
if (cpu__setup_cpunode_map())
|
||||
return -1;
|
||||
goto out_delete;
|
||||
|
||||
if (list_empty(&caller_sort))
|
||||
setup_sorting(&caller_sort, default_sort_order);
|
||||
if (list_empty(&alloc_sort))
|
||||
setup_sorting(&alloc_sort, default_sort_order);
|
||||
|
||||
return __cmd_kmem();
|
||||
ret = __cmd_kmem(session);
|
||||
} else
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
||||
return 0;
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -543,14 +543,12 @@ static void print_vcpu_info(struct perf_kvm_stat *kvm)
|
||||
|
||||
pr_info("Analyze events for ");
|
||||
|
||||
if (kvm->live) {
|
||||
if (kvm->opts.target.system_wide)
|
||||
pr_info("all VMs, ");
|
||||
else if (kvm->opts.target.pid)
|
||||
pr_info("pid(s) %s, ", kvm->opts.target.pid);
|
||||
else
|
||||
pr_info("dazed and confused on what is monitored, ");
|
||||
}
|
||||
if (kvm->opts.target.system_wide)
|
||||
pr_info("all VMs, ");
|
||||
else if (kvm->opts.target.pid)
|
||||
pr_info("pid(s) %s, ", kvm->opts.target.pid);
|
||||
else
|
||||
pr_info("dazed and confused on what is monitored, ");
|
||||
|
||||
if (vcpu == -1)
|
||||
pr_info("all VCPUs:\n\n");
|
||||
@ -592,8 +590,8 @@ static void print_result(struct perf_kvm_stat *kvm)
|
||||
pr_info("%9s ", "Samples%");
|
||||
|
||||
pr_info("%9s ", "Time%");
|
||||
pr_info("%10s ", "Min Time");
|
||||
pr_info("%10s ", "Max Time");
|
||||
pr_info("%11s ", "Min Time");
|
||||
pr_info("%11s ", "Max Time");
|
||||
pr_info("%16s ", "Avg time");
|
||||
pr_info("\n\n");
|
||||
|
||||
@ -610,8 +608,8 @@ static void print_result(struct perf_kvm_stat *kvm)
|
||||
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);
|
||||
pr_info("%8" PRIu64 "us ", min / 1000);
|
||||
pr_info("%8" PRIu64 "us ", max / 1000);
|
||||
pr_info("%9.2fus ", (double)min / 1e3);
|
||||
pr_info("%9.2fus ", (double)max / 1e3);
|
||||
pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3,
|
||||
kvm_event_rel_stddev(vcpu, event));
|
||||
pr_info("\n");
|
||||
@ -732,7 +730,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_session_queue_event(kvm->session, event, &sample, 0);
|
||||
err = perf_session_queue_event(kvm->session, event, &kvm->tool, &sample, 0);
|
||||
/*
|
||||
* FIXME: Here we can't consume the event, as perf_session_queue_event will
|
||||
* point to it, and it'll get possibly overwritten by the kernel.
|
||||
@ -785,7 +783,7 @@ static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm)
|
||||
|
||||
/* flush queue after each round in which we processed events */
|
||||
if (ntotal) {
|
||||
kvm->session->ordered_samples.next_flush = flush_time;
|
||||
kvm->session->ordered_events.next_flush = flush_time;
|
||||
err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session);
|
||||
if (err) {
|
||||
if (kvm->lost_events)
|
||||
@ -885,15 +883,11 @@ static int fd_set_nonblock(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save)
|
||||
static int perf_kvm__handle_stdin(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
tcsetattr(0, TCSANOW, tc_now);
|
||||
c = getc(stdin);
|
||||
tcsetattr(0, TCSAFLUSH, tc_save);
|
||||
|
||||
if (c == 'q')
|
||||
return 1;
|
||||
|
||||
@ -904,7 +898,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
|
||||
{
|
||||
struct pollfd *pollfds = NULL;
|
||||
int nr_fds, nr_stdin, ret, err = -EINVAL;
|
||||
struct termios tc, save;
|
||||
struct termios save;
|
||||
|
||||
/* live flag must be set first */
|
||||
kvm->live = true;
|
||||
@ -919,26 +913,14 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_term_quiet_input(&save);
|
||||
init_kvm_event_record(kvm);
|
||||
|
||||
tcgetattr(0, &save);
|
||||
tc = save;
|
||||
tc.c_lflag &= ~(ICANON | ECHO);
|
||||
tc.c_cc[VMIN] = 0;
|
||||
tc.c_cc[VTIME] = 0;
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
|
||||
/* copy pollfds -- need to add timerfd and stdin */
|
||||
nr_fds = kvm->evlist->nr_fds;
|
||||
pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2));
|
||||
if (!pollfds) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memcpy(pollfds, kvm->evlist->pollfd,
|
||||
sizeof(struct pollfd) * kvm->evlist->nr_fds);
|
||||
/* use pollfds -- need to add timerfd and stdin */
|
||||
nr_fds = kvm->evlist->pollfd.nr;
|
||||
|
||||
/* add timer fd */
|
||||
if (perf_kvm__timerfd_create(kvm) < 0) {
|
||||
@ -946,17 +928,21 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
pollfds[nr_fds].fd = kvm->timerfd;
|
||||
pollfds[nr_fds].events = POLLIN;
|
||||
if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd))
|
||||
goto out;
|
||||
|
||||
nr_fds++;
|
||||
|
||||
pollfds[nr_fds].fd = fileno(stdin);
|
||||
pollfds[nr_fds].events = POLLIN;
|
||||
if (perf_evlist__add_pollfd(kvm->evlist, fileno(stdin)))
|
||||
goto out;
|
||||
|
||||
nr_stdin = nr_fds;
|
||||
nr_fds++;
|
||||
if (fd_set_nonblock(fileno(stdin)) != 0)
|
||||
goto out;
|
||||
|
||||
pollfds = kvm->evlist->pollfd.entries;
|
||||
|
||||
/* everything is good - enable the events and process */
|
||||
perf_evlist__enable(kvm->evlist);
|
||||
|
||||
@ -972,7 +958,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
|
||||
goto out;
|
||||
|
||||
if (pollfds[nr_stdin].revents & POLLIN)
|
||||
done = perf_kvm__handle_stdin(&tc, &save);
|
||||
done = perf_kvm__handle_stdin();
|
||||
|
||||
if (!rc && !done)
|
||||
err = poll(pollfds, nr_fds, 100);
|
||||
@ -989,7 +975,7 @@ out:
|
||||
if (kvm->timerfd >= 0)
|
||||
close(kvm->timerfd);
|
||||
|
||||
free(pollfds);
|
||||
tcsetattr(0, TCSAFLUSH, &save);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -998,6 +984,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
|
||||
int err, rc = -1;
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evlist *evlist = kvm->evlist;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
perf_evlist__config(evlist, &kvm->opts);
|
||||
|
||||
@ -1034,12 +1021,14 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
printf("Couldn't create the events: %s\n", strerror(errno));
|
||||
printf("Couldn't create the events: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) {
|
||||
ui__error("Failed to mmap the events: %s\n", strerror(errno));
|
||||
ui__error("Failed to mmap the events: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
perf_evlist__close(evlist);
|
||||
goto out;
|
||||
}
|
||||
@ -1058,7 +1047,7 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||
struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = kvm->file_name,
|
||||
@ -1069,9 +1058,11 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||
kvm->session = perf_session__new(&file, false, &kvm->tool);
|
||||
if (!kvm->session) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
symbol__init(&kvm->session->header.env);
|
||||
|
||||
if (!perf_session__has_traces(kvm->session, "kvm record"))
|
||||
return -EINVAL;
|
||||
|
||||
@ -1088,8 +1079,8 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||
|
||||
static int parse_target_str(struct perf_kvm_stat *kvm)
|
||||
{
|
||||
if (kvm->pid_str) {
|
||||
kvm->pid_list = intlist__new(kvm->pid_str);
|
||||
if (kvm->opts.target.pid) {
|
||||
kvm->pid_list = intlist__new(kvm->opts.target.pid);
|
||||
if (kvm->pid_list == NULL) {
|
||||
pr_err("Error parsing process id string\n");
|
||||
return -EINVAL;
|
||||
@ -1191,7 +1182,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
|
||||
"key for sorting: sample(sort by samples number)"
|
||||
" time (sort by avg time)"),
|
||||
OPT_STRING('p', "pid", &kvm->pid_str, "pid",
|
||||
OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid",
|
||||
"analyze events only for given process id(s)"),
|
||||
OPT_END()
|
||||
};
|
||||
@ -1201,8 +1192,6 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
NULL
|
||||
};
|
||||
|
||||
symbol__init();
|
||||
|
||||
if (argc) {
|
||||
argc = parse_options(argc, argv,
|
||||
kvm_events_report_options,
|
||||
@ -1212,6 +1201,9 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
kvm_events_report_options);
|
||||
}
|
||||
|
||||
if (!kvm->opts.target.pid)
|
||||
kvm->opts.target.system_wide = true;
|
||||
|
||||
return kvm_events_report_vcpu(kvm);
|
||||
}
|
||||
|
||||
@ -1311,7 +1303,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
kvm->tool.exit = perf_event__process_exit;
|
||||
kvm->tool.fork = perf_event__process_fork;
|
||||
kvm->tool.lost = process_lost_event;
|
||||
kvm->tool.ordered_samples = true;
|
||||
kvm->tool.ordered_events = true;
|
||||
perf_tool__fill_defaults(&kvm->tool);
|
||||
|
||||
/* set defaults */
|
||||
@ -1322,7 +1314,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
kvm->opts.target.uid_str = NULL;
|
||||
kvm->opts.target.uid = UINT_MAX;
|
||||
|
||||
symbol__init();
|
||||
symbol__init(NULL);
|
||||
disable_buildid_cache();
|
||||
|
||||
use_browser = 0;
|
||||
@ -1369,7 +1361,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
*/
|
||||
kvm->session = perf_session__new(&file, false, &kvm->tool);
|
||||
if (kvm->session == NULL) {
|
||||
err = -ENOMEM;
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
kvm->session->evlist = kvm->evlist;
|
||||
|
@ -852,7 +852,7 @@ static int __cmd_report(bool display_info)
|
||||
struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
@ -862,9 +862,11 @@ static int __cmd_report(bool display_info)
|
||||
session = perf_session__new(&file, false, &eops);
|
||||
if (!session) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
if (!perf_session__has_traces(session, "lock record"))
|
||||
goto out_delete;
|
||||
|
||||
@ -974,7 +976,6 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
|
||||
symbol__init();
|
||||
for (i = 0; i < LOCKHASH_SIZE; i++)
|
||||
INIT_LIST_HEAD(lockhash_table + i);
|
||||
|
||||
|
@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem)
|
||||
&mem->tool);
|
||||
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
if (mem->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, mem->cpu_list,
|
||||
@ -133,7 +133,7 @@ static int report_raw_events(struct perf_mem *mem)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
if (symbol__init(&session->header.env) < 0)
|
||||
return -1;
|
||||
|
||||
printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
|
||||
@ -194,7 +194,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
};
|
||||
|
@ -290,8 +290,11 @@ static void cleanup_params(void)
|
||||
|
||||
static void pr_err_with_code(const char *msg, int err)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
pr_err("%s", msg);
|
||||
pr_debug(" Reason: %s (Code: %d)", strerror(-err), err);
|
||||
pr_debug(" Reason: %s (Code: %d)",
|
||||
strerror_r(-err, sbuf, sizeof(sbuf)), err);
|
||||
pr_err("\n");
|
||||
}
|
||||
|
||||
@ -373,6 +376,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"target executable name or path", opt_set_target),
|
||||
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
||||
"Disable symbol demangling"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
OPT_END()
|
||||
};
|
||||
int ret;
|
||||
@ -467,7 +472,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
|
||||
ret = show_line_range(¶ms.line_range, params.target);
|
||||
ret = show_line_range(¶ms.line_range, params.target,
|
||||
params.uprobes);
|
||||
if (ret < 0)
|
||||
pr_err_with_code(" Error: Failed to show lines.", ret);
|
||||
return ret;
|
||||
|
@ -65,8 +65,9 @@ static int process_synthesized_event(struct perf_tool *tool,
|
||||
return record__write(rec, event, event->header.size);
|
||||
}
|
||||
|
||||
static int record__mmap_read(struct record *rec, struct perf_mmap *md)
|
||||
static int record__mmap_read(struct record *rec, int idx)
|
||||
{
|
||||
struct perf_mmap *md = &rec->evlist->mmap[idx];
|
||||
unsigned int head = perf_mmap__read_head(md);
|
||||
unsigned int old = md->prev;
|
||||
unsigned char *data = md->base + page_size;
|
||||
@ -102,8 +103,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md)
|
||||
}
|
||||
|
||||
md->prev = old;
|
||||
perf_mmap__write_tail(md, old);
|
||||
|
||||
perf_evlist__mmap_consume(rec->evlist, idx);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
@ -161,7 +161,7 @@ try_again:
|
||||
|
||||
if (perf_evlist__apply_filters(evlist)) {
|
||||
error("failed to set filter with %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
strerror_r(errno, msg, sizeof(msg)));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -175,7 +175,8 @@ try_again:
|
||||
"(current value: %u)\n", opts->mmap_pages);
|
||||
rc = -errno;
|
||||
} else {
|
||||
pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
|
||||
pr_err("failed to mmap with %d (%s)\n", errno,
|
||||
strerror_r(errno, msg, sizeof(msg)));
|
||||
rc = -errno;
|
||||
}
|
||||
goto out;
|
||||
@ -244,7 +245,7 @@ static int record__mmap_read_all(struct record *rec)
|
||||
|
||||
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
|
||||
if (rec->evlist->mmap[i].base) {
|
||||
if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
|
||||
if (record__mmap_read(rec, i) != 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -307,7 +308,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
struct record_opts *opts = &rec->opts;
|
||||
struct perf_data_file *file = &rec->file;
|
||||
struct perf_session *session;
|
||||
bool disabled = false;
|
||||
bool disabled = false, draining = false;
|
||||
|
||||
rec->progname = argv[0];
|
||||
|
||||
@ -456,9 +457,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (hits == rec->samples) {
|
||||
if (done)
|
||||
if (done || draining)
|
||||
break;
|
||||
err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
|
||||
err = perf_evlist__poll(rec->evlist, -1);
|
||||
/*
|
||||
* Propagate error, only if there's any. Ignore positive
|
||||
* number of returned events and interrupt error.
|
||||
@ -466,6 +467,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
if (err > 0 || (err < 0 && errno == EINTR))
|
||||
err = 0;
|
||||
waking++;
|
||||
|
||||
if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0)
|
||||
draining = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -480,7 +484,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (forks && workload_exec_errno) {
|
||||
char msg[512];
|
||||
char msg[STRERR_BUFSIZE];
|
||||
const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
|
||||
pr_err("Workload failed: %s\n", emsg);
|
||||
err = -1;
|
||||
@ -620,145 +624,56 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
static int get_stack_size(char *str, unsigned long *_size)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long size;
|
||||
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
|
||||
|
||||
size = strtoul(str, &endptr, 0);
|
||||
|
||||
do {
|
||||
if (*endptr)
|
||||
break;
|
||||
|
||||
size = round_up(size, sizeof(u64));
|
||||
if (!size || size > max_size)
|
||||
break;
|
||||
|
||||
*_size = size;
|
||||
return 0;
|
||||
|
||||
} while (0);
|
||||
|
||||
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
|
||||
max_size, str);
|
||||
return -1;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
|
||||
int record_parse_callchain(const char *arg, struct record_opts *opts)
|
||||
{
|
||||
char *tok, *name, *saveptr = NULL;
|
||||
char *buf;
|
||||
int ret = -1;
|
||||
|
||||
/* We need buffer that we know we can write to. */
|
||||
buf = malloc(strlen(arg) + 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(buf, arg);
|
||||
|
||||
tok = strtok_r((char *)buf, ",", &saveptr);
|
||||
name = tok ? : (char *)buf;
|
||||
|
||||
do {
|
||||
/* Framepointer style */
|
||||
if (!strncmp(name, "fp", sizeof("fp"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
opts->call_graph = CALLCHAIN_FP;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for -g fp\n");
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
/* Dwarf style */
|
||||
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
opts->call_graph = CALLCHAIN_DWARF;
|
||||
opts->stack_dump_size = default_stack_dump_size;
|
||||
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
if (tok) {
|
||||
unsigned long size = 0;
|
||||
|
||||
ret = get_stack_size(tok, &size);
|
||||
opts->stack_dump_size = size;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
} else {
|
||||
pr_err("callchain: Unknown --call-graph option "
|
||||
"value: %s\n", arg);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void callchain_debug(struct record_opts *opts)
|
||||
static void callchain_debug(void)
|
||||
{
|
||||
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" };
|
||||
|
||||
pr_debug("callchain: type %s\n", str[opts->call_graph]);
|
||||
pr_debug("callchain: type %s\n", str[callchain_param.record_mode]);
|
||||
|
||||
if (opts->call_graph == CALLCHAIN_DWARF)
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
opts->stack_dump_size);
|
||||
callchain_param.dump_size);
|
||||
}
|
||||
|
||||
int record_parse_callchain_opt(const struct option *opt,
|
||||
int record_parse_callchain_opt(const struct option *opt __maybe_unused,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
struct record_opts *opts = opt->value;
|
||||
int ret;
|
||||
|
||||
opts->call_graph_enabled = !unset;
|
||||
callchain_param.enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
opts->call_graph = CALLCHAIN_NONE;
|
||||
callchain_param.record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = record_parse_callchain(arg, opts);
|
||||
ret = parse_callchain_record_opt(arg);
|
||||
if (!ret)
|
||||
callchain_debug(opts);
|
||||
callchain_debug();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int record_callchain_opt(const struct option *opt,
|
||||
int record_callchain_opt(const struct option *opt __maybe_unused,
|
||||
const char *arg __maybe_unused,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct record_opts *opts = opt->value;
|
||||
callchain_param.enabled = true;
|
||||
|
||||
opts->call_graph_enabled = !unset;
|
||||
if (callchain_param.record_mode == CALLCHAIN_NONE)
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
|
||||
if (opts->call_graph == CALLCHAIN_NONE)
|
||||
opts->call_graph = CALLCHAIN_FP;
|
||||
|
||||
callchain_debug(opts);
|
||||
callchain_debug();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_record_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct record *rec = cb;
|
||||
|
||||
if (!strcmp(var, "record.call-graph"))
|
||||
return record_parse_callchain(value, &rec->opts);
|
||||
var = "call-graph.record-mode"; /* fall-through */
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
@ -781,6 +696,7 @@ static const char * const record_usage[] = {
|
||||
*/
|
||||
static struct record record = {
|
||||
.opts = {
|
||||
.sample_time = true,
|
||||
.mmap_pages = UINT_MAX,
|
||||
.user_freq = UINT_MAX,
|
||||
.user_interval = ULLONG_MAX,
|
||||
@ -907,7 +823,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(record_usage, record_options);
|
||||
}
|
||||
|
||||
symbol__init();
|
||||
symbol__init(NULL);
|
||||
|
||||
if (symbol_conf.kptr_restrict)
|
||||
pr_warning(
|
||||
|
@ -58,17 +58,19 @@ struct report {
|
||||
const char *symbol_filter_str;
|
||||
float min_percent;
|
||||
u64 nr_entries;
|
||||
u64 queue_size;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static int report__config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct report *rep = cb;
|
||||
|
||||
if (!strcmp(var, "report.group")) {
|
||||
symbol_conf.event_group = perf_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "report.percent-limit")) {
|
||||
struct report *rep = cb;
|
||||
rep->min_percent = strtof(value, NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -76,6 +78,10 @@ static int report__config(const char *var, const char *value, void *cb)
|
||||
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "report.queue-size")) {
|
||||
rep->queue_size = perf_config_u64(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
@ -578,7 +584,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.attr = perf_event__process_attr,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.max_stack = PERF_MAX_STACK_DEPTH,
|
||||
@ -674,6 +680,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
||||
"Disable symbol demangling"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
|
||||
OPT_CALLBACK(0, "percent-limit", &report, "percent",
|
||||
"Don't show entries under that percent", parse_percent_limit),
|
||||
@ -712,14 +720,19 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
repeat:
|
||||
session = perf_session__new(&file, false, &report.tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
if (report.queue_size) {
|
||||
ordered_events__set_alloc_size(&session->ordered_events,
|
||||
report.queue_size);
|
||||
}
|
||||
|
||||
report.session = session;
|
||||
|
||||
has_br_stack = perf_header__has_feat(&session->header,
|
||||
HEADER_BRANCH_STACK);
|
||||
|
||||
if (branch_mode == -1 && has_br_stack) {
|
||||
if ((branch_mode == -1 && has_br_stack) || branch_mode == 1) {
|
||||
sort__mode = SORT_MODE__BRANCH;
|
||||
symbol_conf.cumulate_callchain = false;
|
||||
}
|
||||
@ -787,7 +800,7 @@ repeat:
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
if (symbol__init(&session->header.env) < 0)
|
||||
goto error;
|
||||
|
||||
if (argc) {
|
||||
|
@ -428,6 +428,7 @@ static u64 get_cpu_usage_nsec_parent(void)
|
||||
static int self_open_counters(void)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
@ -440,7 +441,8 @@ static int self_open_counters(void)
|
||||
|
||||
if (fd < 0)
|
||||
pr_err("Error: sys_perf_event_open() syscall returned "
|
||||
"with %d (%s)\n", fd, strerror(errno));
|
||||
"with %d (%s)\n", fd,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return fd;
|
||||
}
|
||||
|
||||
@ -1462,6 +1464,8 @@ static int perf_sched__read_events(struct perf_sched *sched,
|
||||
return -1;
|
||||
}
|
||||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
if (perf_session__set_tracepoints_handlers(session, handlers))
|
||||
goto out_delete;
|
||||
|
||||
@ -1662,7 +1666,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_sched__process_fork_event,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
.sort_list = LIST_HEAD_INIT(sched.sort_list),
|
||||
@ -1747,7 +1751,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (!strcmp(argv[0], "script"))
|
||||
return cmd_script(argc, argv, prefix);
|
||||
|
||||
symbol__init();
|
||||
if (!strncmp(argv[0], "rec", 3)) {
|
||||
return __cmd_record(argc, argv);
|
||||
} else if (!strncmp(argv[0], "lat", 3)) {
|
||||
|
@ -184,10 +184,6 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
||||
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
|
||||
PERF_OUTPUT_IP))
|
||||
return -EINVAL;
|
||||
|
||||
if (!no_callchain &&
|
||||
!(attr->sample_type & PERF_SAMPLE_CALLCHAIN))
|
||||
symbol_conf.use_callchain = false;
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(ADDR) &&
|
||||
@ -290,6 +286,19 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
set_print_ip_opts(&evsel->attr);
|
||||
}
|
||||
|
||||
if (!no_callchain) {
|
||||
bool use_callchain = false;
|
||||
|
||||
evlist__for_each(session->evlist, evsel) {
|
||||
if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||
use_callchain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!use_callchain)
|
||||
symbol_conf.use_callchain = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* set default for tracepoints to print symbols only
|
||||
* if callchains are present
|
||||
@ -476,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int default_flush_script(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int default_stop_script(void)
|
||||
{
|
||||
return 0;
|
||||
@ -489,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused,
|
||||
|
||||
static struct scripting_ops default_scripting_ops = {
|
||||
.start_script = default_start_script,
|
||||
.flush_script = default_flush_script,
|
||||
.stop_script = default_stop_script,
|
||||
.process_event = process_event,
|
||||
.generate_script = default_generate_script,
|
||||
@ -504,6 +519,11 @@ static void setup_scripting(void)
|
||||
scripting_ops = &default_scripting_ops;
|
||||
}
|
||||
|
||||
static int flush_scripting(void)
|
||||
{
|
||||
return scripting_ops->flush_script();
|
||||
}
|
||||
|
||||
static int cleanup_scripting(void)
|
||||
{
|
||||
pr_debug("\nperf script stopped\n");
|
||||
@ -1471,12 +1491,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
bool show_full_info = false;
|
||||
bool header = false;
|
||||
bool header_only = false;
|
||||
bool script_started = false;
|
||||
char *rec_script_path = NULL;
|
||||
char *rep_script_path = NULL;
|
||||
struct perf_session *session;
|
||||
char *script_path = NULL;
|
||||
const char **__argv;
|
||||
int i, j, err;
|
||||
int i, j, err = 0;
|
||||
struct perf_script script = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
@ -1488,7 +1509,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.attr = process_attr,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
@ -1718,26 +1739,28 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
if (!script_name)
|
||||
setup_pager();
|
||||
|
||||
session = perf_session__new(&file, false, &script.tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
if (header || header_only) {
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
if (header_only)
|
||||
return 0;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (symbol__init(&session->header.env) < 0)
|
||||
goto out_delete;
|
||||
|
||||
script.session = session;
|
||||
|
||||
if (cpu_list) {
|
||||
if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))
|
||||
return -1;
|
||||
err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
|
||||
if (err < 0)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (!no_callchain)
|
||||
@ -1752,53 +1775,62 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (output_set_by_user()) {
|
||||
fprintf(stderr,
|
||||
"custom fields not supported for generated scripts");
|
||||
return -1;
|
||||
err = -EINVAL;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
input = open(file.path, O_RDONLY); /* input_name */
|
||||
if (input < 0) {
|
||||
err = -errno;
|
||||
perror("failed to open file");
|
||||
return -1;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
err = fstat(input, &perf_stat);
|
||||
if (err < 0) {
|
||||
perror("failed to stat file");
|
||||
return -1;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (!perf_stat.st_size) {
|
||||
fprintf(stderr, "zero-sized file, nothing to do!\n");
|
||||
return 0;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
scripting_ops = script_spec__lookup(generate_script_lang);
|
||||
if (!scripting_ops) {
|
||||
fprintf(stderr, "invalid language specifier");
|
||||
return -1;
|
||||
err = -ENOENT;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
err = scripting_ops->generate_script(session->tevent.pevent,
|
||||
"perf-script");
|
||||
goto out;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (script_name) {
|
||||
err = scripting_ops->start_script(script_name, argc, argv);
|
||||
if (err)
|
||||
goto out;
|
||||
goto out_delete;
|
||||
pr_debug("perf script started with script %s\n\n", script_name);
|
||||
script_started = true;
|
||||
}
|
||||
|
||||
|
||||
err = perf_session__check_output_opt(session);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
goto out_delete;
|
||||
|
||||
err = __cmd_script(&script);
|
||||
|
||||
flush_scripting();
|
||||
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
cleanup_scripting();
|
||||
|
||||
if (script_started)
|
||||
cleanup_scripting();
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -593,7 +593,7 @@ static int __run_perf_stat(int argc, const char **argv)
|
||||
|
||||
if (perf_evlist__apply_filters(evsel_list)) {
|
||||
error("failed to set filter with %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
strerror_r(errno, msg, sizeof(msg)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -732,7 +732,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
|
||||
}
|
||||
}
|
||||
|
||||
static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double msecs = avg / 1e6;
|
||||
const char *fmt_v, *fmt_n;
|
||||
@ -741,7 +741,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
fmt_v = csv_output ? "%.6f%s" : "%18.6f%s";
|
||||
fmt_n = csv_output ? "%s" : "%-25s";
|
||||
|
||||
aggr_printout(evsel, cpu, nr);
|
||||
aggr_printout(evsel, id, nr);
|
||||
|
||||
scnprintf(name, sizeof(name), "%s%s",
|
||||
perf_evsel__name(evsel), csv_output ? "" : " (msec)");
|
||||
@ -947,11 +947,12 @@ static void print_ll_cache_misses(int cpu,
|
||||
fprintf(output, " of all LL-cache hits ");
|
||||
}
|
||||
|
||||
static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double total, ratio = 0.0, total2;
|
||||
double sc = evsel->scale;
|
||||
const char *fmt;
|
||||
int cpu = cpu_map__id_to_cpu(id);
|
||||
|
||||
if (csv_output) {
|
||||
fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s";
|
||||
@ -962,7 +963,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s";
|
||||
}
|
||||
|
||||
aggr_printout(evsel, cpu, nr);
|
||||
aggr_printout(evsel, id, nr);
|
||||
|
||||
if (aggr_mode == AGGR_GLOBAL)
|
||||
cpu = 0;
|
||||
|
@ -1605,7 +1605,9 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
(void)perf_header__process_sections(&session->header,
|
||||
perf_data_file__fd(session->file),
|
||||
@ -1920,7 +1922,7 @@ int cmd_timechart(int argc, const char **argv,
|
||||
.fork = process_fork_event,
|
||||
.exit = process_exit_event,
|
||||
.sample = process_sample_event,
|
||||
.ordered_samples = true,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.proc_num = 15,
|
||||
.min_time = 1000000,
|
||||
@ -1982,8 +1984,6 @@ int cmd_timechart(int argc, const char **argv,
|
||||
return -1;
|
||||
}
|
||||
|
||||
symbol__init();
|
||||
|
||||
if (argc && !strncmp(argv[0], "rec", 3)) {
|
||||
argc = parse_options(argc, argv, record_options, record_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/uio.h>
|
||||
@ -276,11 +276,17 @@ static void perf_top__print_sym_table(struct perf_top *top)
|
||||
return;
|
||||
}
|
||||
|
||||
if (top->zero) {
|
||||
hists__delete_entries(&top->sym_evsel->hists);
|
||||
} else {
|
||||
hists__decay_entries(&top->sym_evsel->hists,
|
||||
top->hide_user_symbols,
|
||||
top->hide_kernel_symbols);
|
||||
}
|
||||
|
||||
hists__collapse_resort(&top->sym_evsel->hists, NULL);
|
||||
hists__output_resort(&top->sym_evsel->hists);
|
||||
hists__decay_entries(&top->sym_evsel->hists,
|
||||
top->hide_user_symbols,
|
||||
top->hide_kernel_symbols);
|
||||
|
||||
hists__output_recalc_col_len(&top->sym_evsel->hists,
|
||||
top->print_entries - printed);
|
||||
putchar('\n');
|
||||
@ -427,18 +433,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
|
||||
if (!perf_top__key_mapped(top, c)) {
|
||||
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
||||
struct termios tc, save;
|
||||
struct termios save;
|
||||
|
||||
perf_top__print_mapped_keys(top);
|
||||
fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
|
||||
fflush(stdout);
|
||||
|
||||
tcgetattr(0, &save);
|
||||
tc = save;
|
||||
tc.c_lflag &= ~(ICANON | ECHO);
|
||||
tc.c_cc[VMIN] = 0;
|
||||
tc.c_cc[VTIME] = 0;
|
||||
tcsetattr(0, TCSANOW, &tc);
|
||||
set_term_quiet_input(&save);
|
||||
|
||||
poll(&stdin_poll, 1, -1);
|
||||
c = getc(stdin);
|
||||
@ -542,11 +543,16 @@ static void perf_top__sort_new_samples(void *arg)
|
||||
if (t->evlist->selected != NULL)
|
||||
t->sym_evsel = t->evlist->selected;
|
||||
|
||||
if (t->zero) {
|
||||
hists__delete_entries(&t->sym_evsel->hists);
|
||||
} else {
|
||||
hists__decay_entries(&t->sym_evsel->hists,
|
||||
t->hide_user_symbols,
|
||||
t->hide_kernel_symbols);
|
||||
}
|
||||
|
||||
hists__collapse_resort(&t->sym_evsel->hists, NULL);
|
||||
hists__output_resort(&t->sym_evsel->hists);
|
||||
hists__decay_entries(&t->sym_evsel->hists,
|
||||
t->hide_user_symbols,
|
||||
t->hide_kernel_symbols);
|
||||
}
|
||||
|
||||
static void *display_thread_tui(void *arg)
|
||||
@ -577,23 +583,32 @@ static void *display_thread_tui(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void display_sig(int sig __maybe_unused)
|
||||
{
|
||||
done = 1;
|
||||
}
|
||||
|
||||
static void display_setup_sig(void)
|
||||
{
|
||||
signal(SIGSEGV, display_sig);
|
||||
signal(SIGFPE, display_sig);
|
||||
signal(SIGINT, display_sig);
|
||||
signal(SIGQUIT, display_sig);
|
||||
signal(SIGTERM, display_sig);
|
||||
}
|
||||
|
||||
static void *display_thread(void *arg)
|
||||
{
|
||||
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
||||
struct termios tc, save;
|
||||
struct termios save;
|
||||
struct perf_top *top = arg;
|
||||
int delay_msecs, c;
|
||||
|
||||
tcgetattr(0, &save);
|
||||
tc = save;
|
||||
tc.c_lflag &= ~(ICANON | ECHO);
|
||||
tc.c_cc[VMIN] = 0;
|
||||
tc.c_cc[VTIME] = 0;
|
||||
|
||||
display_setup_sig();
|
||||
pthread__unblock_sigwinch();
|
||||
repeat:
|
||||
delay_msecs = top->delay_secs * 1000;
|
||||
tcsetattr(0, TCSANOW, &tc);
|
||||
set_term_quiet_input(&save);
|
||||
/* trash return*/
|
||||
getc(stdin);
|
||||
|
||||
@ -620,13 +635,16 @@ repeat:
|
||||
}
|
||||
}
|
||||
|
||||
tcsetattr(0, TCSAFLUSH, &save);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
|
||||
static int symbol_filter(struct map *map, struct symbol *sym)
|
||||
{
|
||||
const char *name = sym->name;
|
||||
|
||||
if (!map->dso->kernel)
|
||||
return 0;
|
||||
/*
|
||||
* ppc64 uses function descriptors and appends a '.' to the
|
||||
* start of every instruction address. Remove it.
|
||||
@ -876,7 +894,7 @@ try_again:
|
||||
|
||||
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
|
||||
ui__error("Failed to mmap with %d (%s)\n",
|
||||
errno, strerror(errno));
|
||||
errno, strerror_r(errno, msg, sizeof(msg)));
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
@ -911,7 +929,7 @@ static int __cmd_top(struct perf_top *top)
|
||||
|
||||
top->session = perf_session__new(NULL, false, NULL);
|
||||
if (top->session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
machines__set_symbol_filter(&top->session->machines, symbol_filter);
|
||||
|
||||
@ -946,7 +964,7 @@ static int __cmd_top(struct perf_top *top)
|
||||
perf_evlist__enable(top->evlist);
|
||||
|
||||
/* Wait for a minimal set of events before starting the snapshot */
|
||||
poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
|
||||
perf_evlist__poll(top->evlist, 100);
|
||||
|
||||
perf_top__mmap_read(top);
|
||||
|
||||
@ -963,7 +981,7 @@ static int __cmd_top(struct perf_top *top)
|
||||
param.sched_priority = top->realtime_prio;
|
||||
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
|
||||
ui__error("Could not set realtime priority.\n");
|
||||
goto out_delete;
|
||||
goto out_join;
|
||||
}
|
||||
}
|
||||
|
||||
@ -973,10 +991,12 @@ static int __cmd_top(struct perf_top *top)
|
||||
perf_top__mmap_read(top);
|
||||
|
||||
if (hits == top->samples)
|
||||
ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
|
||||
ret = perf_evlist__poll(top->evlist, 100);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out_join:
|
||||
pthread_join(thread, NULL);
|
||||
out_delete:
|
||||
perf_session__delete(top->session);
|
||||
top->session = NULL;
|
||||
@ -1000,10 +1020,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
|
||||
static int perf_top_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct perf_top *top = cb;
|
||||
|
||||
if (!strcmp(var, "top.call-graph"))
|
||||
return record_parse_callchain(value, &top->record_opts);
|
||||
var = "call-graph.record-mode"; /* fall-through */
|
||||
if (!strcmp(var, "top.children")) {
|
||||
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
|
||||
return 0;
|
||||
@ -1122,6 +1140,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
@ -1131,6 +1151,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"Don't show entries under that percent", parse_percent_limit),
|
||||
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
|
||||
"How to display percentage of filtered entries", parse_filter_percentage),
|
||||
OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
|
||||
"width[,width...]",
|
||||
"don't try to adjust column width, use these fixed values"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const top_usage[] = {
|
||||
@ -1217,7 +1240,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
symbol_conf.priv_size = sizeof(struct annotation);
|
||||
|
||||
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
|
||||
if (symbol__init() < 0)
|
||||
if (symbol__init(NULL) < 0)
|
||||
return -1;
|
||||
|
||||
sort__setup_elide(stdout);
|
||||
|
@ -402,6 +402,31 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,
|
||||
|
||||
#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags
|
||||
|
||||
static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
#define P_MREMAP_FLAG(n) \
|
||||
if (flags & MREMAP_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~MREMAP_##n; \
|
||||
}
|
||||
|
||||
P_MREMAP_FLAG(MAYMOVE);
|
||||
#ifdef MREMAP_FIXED
|
||||
P_MREMAP_FLAG(FIXED);
|
||||
#endif
|
||||
#undef P_MREMAP_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags
|
||||
|
||||
static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
@ -1004,6 +1029,7 @@ static struct syscall_fmt {
|
||||
[2] = SCA_MMAP_PROT, /* prot */ }, },
|
||||
{ .name = "mremap", .hexret = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* addr */
|
||||
[3] = SCA_MREMAP_FLAGS, /* flags */
|
||||
[4] = SCA_HEX, /* new_addr */ }, },
|
||||
{ .name = "munlock", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
|
||||
@ -1385,7 +1411,7 @@ static int trace__tool_process(struct perf_tool *tool,
|
||||
|
||||
static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
|
||||
{
|
||||
int err = symbol__init();
|
||||
int err = symbol__init(NULL);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
@ -1669,7 +1695,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int ret;
|
||||
long ret;
|
||||
u64 duration = 0;
|
||||
struct thread *thread;
|
||||
int id = perf_evsel__sc_tp_uint(evsel, id, sample);
|
||||
@ -1722,9 +1748,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
|
||||
if (sc->fmt == NULL) {
|
||||
signed_print:
|
||||
fprintf(trace->output, ") = %d", ret);
|
||||
fprintf(trace->output, ") = %ld", ret);
|
||||
} else if (ret < 0 && sc->fmt->errmsg) {
|
||||
char bf[256];
|
||||
char bf[STRERR_BUFSIZE];
|
||||
const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
|
||||
*e = audit_errno_to_name(-ret);
|
||||
|
||||
@ -1732,7 +1758,7 @@ signed_print:
|
||||
} else if (ret == 0 && sc->fmt->timeout)
|
||||
fprintf(trace->output, ") = 0 Timeout");
|
||||
else if (sc->fmt->hexret)
|
||||
fprintf(trace->output, ") = %#x", ret);
|
||||
fprintf(trace->output, ") = %#lx", ret);
|
||||
else
|
||||
goto signed_print;
|
||||
|
||||
@ -2018,6 +2044,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
int err = -1, i;
|
||||
unsigned long before;
|
||||
const bool forks = argc > 0;
|
||||
bool draining = false;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
trace->live = true;
|
||||
|
||||
@ -2079,7 +2107,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
|
||||
err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
|
||||
if (err < 0) {
|
||||
fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno));
|
||||
fprintf(trace->output, "Couldn't mmap the events: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -2143,8 +2172,12 @@ next_event:
|
||||
if (trace->nr_events == before) {
|
||||
int timeout = done ? 100 : -1;
|
||||
|
||||
if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0)
|
||||
if (!draining && perf_evlist__poll(evlist, timeout) > 0) {
|
||||
if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0)
|
||||
draining = true;
|
||||
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
goto again;
|
||||
}
|
||||
@ -2209,18 +2242,18 @@ static int trace__replay(struct trace *trace)
|
||||
trace->tool.tracing_data = perf_event__process_tracing_data;
|
||||
trace->tool.build_id = perf_event__process_build_id;
|
||||
|
||||
trace->tool.ordered_samples = true;
|
||||
trace->tool.ordered_events = true;
|
||||
trace->tool.ordering_requires_timestamps = true;
|
||||
|
||||
/* add tid to output */
|
||||
trace->multiple_threads = true;
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
session = perf_session__new(&file, false, &trace->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
|
||||
if (symbol__init(&session->header.env) < 0)
|
||||
goto out;
|
||||
|
||||
trace->host = &session->machines.host;
|
||||
|
||||
|
@ -48,10 +48,6 @@ 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
|
||||
@ -120,6 +116,29 @@ ifdef PARSER_DEBUG
|
||||
CFLAGS += -DPARSER_DEBUG
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPYTHON
|
||||
# Try different combinations to accommodate systems that only have
|
||||
# python[2][-config] in weird combinations but always preferring
|
||||
# python2 and python2-config as per pep-0394. If we catch a
|
||||
# python[-config] in version 3, the version check will kill it.
|
||||
PYTHON2 := $(if $(call get-executable,python2),python2,python)
|
||||
override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2))
|
||||
PYTHON2_CONFIG := \
|
||||
$(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config)
|
||||
override PYTHON_CONFIG := \
|
||||
$(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG))
|
||||
|
||||
PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG))
|
||||
|
||||
PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
|
||||
PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
|
||||
|
||||
FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS)
|
||||
FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS)
|
||||
FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS)
|
||||
FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS)
|
||||
endif
|
||||
|
||||
CFLAGS += -fno-omit-frame-pointer
|
||||
CFLAGS += -ggdb3
|
||||
CFLAGS += -funwind-tables
|
||||
@ -355,6 +374,12 @@ ifndef NO_LIBELF
|
||||
endif # NO_DWARF
|
||||
endif # NO_LIBELF
|
||||
|
||||
ifeq ($(ARCH),powerpc)
|
||||
ifndef NO_DWARF
|
||||
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_LIBUNWIND
|
||||
ifneq ($(feature-libunwind), 1)
|
||||
msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR);
|
||||
@ -482,21 +507,14 @@ define disable-python_code
|
||||
NO_LIBPYTHON := 1
|
||||
endef
|
||||
|
||||
override PYTHON := \
|
||||
$(call get-executable-or-default,PYTHON,python)
|
||||
|
||||
ifndef PYTHON
|
||||
$(call disable-python,python interpreter)
|
||||
ifdef NO_LIBPYTHON
|
||||
$(call disable-python)
|
||||
else
|
||||
|
||||
PYTHON_WORD := $(call shell-wordify,$(PYTHON))
|
||||
|
||||
ifdef NO_LIBPYTHON
|
||||
$(call disable-python)
|
||||
ifndef PYTHON
|
||||
$(call disable-python,python interpreter)
|
||||
else
|
||||
|
||||
override PYTHON_CONFIG := \
|
||||
$(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config)
|
||||
PYTHON_WORD := $(call shell-wordify,$(PYTHON))
|
||||
|
||||
ifndef PYTHON_CONFIG
|
||||
$(call disable-python,python-config tool)
|
||||
@ -635,11 +653,13 @@ else
|
||||
sysconfdir = $(prefix)/etc
|
||||
ETC_PERFCONFIG = etc/perfconfig
|
||||
endif
|
||||
ifndef lib
|
||||
ifeq ($(IS_X86_64),1)
|
||||
lib = lib64
|
||||
else
|
||||
lib = lib
|
||||
endif
|
||||
endif # lib
|
||||
libdir = $(prefix)/$(lib)
|
||||
|
||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||
|
@ -101,25 +101,11 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
|
||||
test-libperl.bin:
|
||||
$(BUILD) $(FLAGS_PERL_EMBED)
|
||||
|
||||
override PYTHON := python
|
||||
override PYTHON_CONFIG := python-config
|
||||
|
||||
escape-for-shell-sq = $(subst ','\'',$(1))
|
||||
shell-sq = '$(escape-for-shell-sq)'
|
||||
|
||||
PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG))
|
||||
|
||||
PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
|
||||
PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
|
||||
PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
|
||||
PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
|
||||
FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
|
||||
|
||||
test-libpython.bin:
|
||||
$(BUILD) $(FLAGS_PYTHON_EMBED)
|
||||
$(BUILD)
|
||||
|
||||
test-libpython-version.bin:
|
||||
$(BUILD) $(FLAGS_PYTHON_EMBED)
|
||||
$(BUILD)
|
||||
|
||||
test-libbfd.bin:
|
||||
$(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl
|
||||
|
@ -132,7 +132,7 @@ endef
|
||||
#
|
||||
# Usage: bool-value = $(call is-absolute,path)
|
||||
#
|
||||
is-absolute = $(shell echo $(shell-sq) | grep ^/ -q && echo y)
|
||||
is-absolute = $(shell echo $(shell-sq) | grep -q ^/ && echo y)
|
||||
|
||||
# lookup
|
||||
#
|
||||
|
259
tools/perf/perf-with-kcore.sh
Normal file
259
tools/perf/perf-with-kcore.sh
Normal file
@ -0,0 +1,259 @@
|
||||
#!/bin/bash
|
||||
# perf-with-kcore: use perf with a copy of kcore
|
||||
# Copyright (c) 2014, Intel Corporation.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
|
||||
set -e
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]" >&2
|
||||
echo " <perf sub-command> can be record, script, report or inject" >&2
|
||||
echo " or: perf-with-kcore fix_buildid_cache_permissions" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
find_perf()
|
||||
{
|
||||
if [ -n "$PERF" ] ; then
|
||||
return
|
||||
fi
|
||||
PERF=`which perf || true`
|
||||
if [ -z "$PERF" ] ; then
|
||||
echo "Failed to find perf" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -x "$PERF" ] ; then
|
||||
echo "Failed to find perf" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Using $PERF"
|
||||
"$PERF" version
|
||||
}
|
||||
|
||||
copy_kcore()
|
||||
{
|
||||
echo "Copying kcore"
|
||||
|
||||
if [ $EUID -eq 0 ] ; then
|
||||
SUDO=""
|
||||
else
|
||||
SUDO="sudo"
|
||||
fi
|
||||
|
||||
rm -f perf.data.junk
|
||||
("$PERF" record -o perf.data.junk $PERF_OPTIONS -- sleep 60) >/dev/null 2>/dev/null &
|
||||
PERF_PID=$!
|
||||
|
||||
# Need to make sure that perf has started
|
||||
sleep 1
|
||||
|
||||
KCORE=$(($SUDO "$PERF" buildid-cache -v -f -k /proc/kcore >/dev/null) 2>&1)
|
||||
case "$KCORE" in
|
||||
"kcore added to build-id cache directory "*)
|
||||
KCORE_DIR=${KCORE#"kcore added to build-id cache directory "}
|
||||
;;
|
||||
*)
|
||||
kill $PERF_PID
|
||||
wait >/dev/null 2>/dev/null || true
|
||||
rm perf.data.junk
|
||||
echo "$KCORE"
|
||||
echo "Failed to find kcore" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
kill $PERF_PID
|
||||
wait >/dev/null 2>/dev/null || true
|
||||
rm perf.data.junk
|
||||
|
||||
$SUDO cp -a "$KCORE_DIR" "$(pwd)/$PERF_DATA_DIR"
|
||||
$SUDO rm -f "$KCORE_DIR/kcore"
|
||||
$SUDO rm -f "$KCORE_DIR/kallsyms"
|
||||
$SUDO rm -f "$KCORE_DIR/modules"
|
||||
$SUDO rmdir "$KCORE_DIR"
|
||||
|
||||
KCORE_DIR_BASENAME=$(basename "$KCORE_DIR")
|
||||
KCORE_DIR="$(pwd)/$PERF_DATA_DIR/$KCORE_DIR_BASENAME"
|
||||
|
||||
$SUDO chown $UID "$KCORE_DIR"
|
||||
$SUDO chown $UID "$KCORE_DIR/kcore"
|
||||
$SUDO chown $UID "$KCORE_DIR/kallsyms"
|
||||
$SUDO chown $UID "$KCORE_DIR/modules"
|
||||
|
||||
$SUDO chgrp $GROUPS "$KCORE_DIR"
|
||||
$SUDO chgrp $GROUPS "$KCORE_DIR/kcore"
|
||||
$SUDO chgrp $GROUPS "$KCORE_DIR/kallsyms"
|
||||
$SUDO chgrp $GROUPS "$KCORE_DIR/modules"
|
||||
|
||||
ln -s "$KCORE_DIR_BASENAME" "$PERF_DATA_DIR/kcore_dir"
|
||||
}
|
||||
|
||||
fix_buildid_cache_permissions()
|
||||
{
|
||||
if [ $EUID -ne 0 ] ; then
|
||||
echo "This script must be run as root via sudo " >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$SUDO_USER" ] ; then
|
||||
echo "This script must be run via sudo" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_HOME=$(bash <<< "echo ~$SUDO_USER")
|
||||
|
||||
if [ "$HOME" != "$USER_HOME" ] ; then
|
||||
echo "Fix unnecessary because root has a home: $HOME" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Fixing buildid cache permissions"
|
||||
|
||||
find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
|
||||
find "$USER_HOME/.debug" -xdev -type f -links 1 ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
|
||||
find "$USER_HOME/.debug" -xdev -type l ! -user "$SUDO_USER" -ls -exec chown -h "$SUDO_USER" \{\} \;
|
||||
|
||||
if [ -n "$SUDO_GID" ] ; then
|
||||
find "$USER_HOME/.debug" -xdev -type d ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
|
||||
find "$USER_HOME/.debug" -xdev -type f -links 1 ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
|
||||
find "$USER_HOME/.debug" -xdev -type l ! -group "$SUDO_GID" -ls -exec chgrp -h "$SUDO_GID" \{\} \;
|
||||
fi
|
||||
|
||||
echo "Done"
|
||||
}
|
||||
|
||||
check_buildid_cache_permissions()
|
||||
{
|
||||
if [ $EUID -eq 0 ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -user "$USER" -print -quit)
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -user "$USER" -print -quit)
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -user "$USER" -print -quit)
|
||||
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -group "$GROUPS" -print -quit)
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -group "$GROUPS" -print -quit)
|
||||
PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -group "$GROUPS" -print -quit)
|
||||
|
||||
if [ -n "$PERMISSIONS_OK" ] ; then
|
||||
echo "*** WARNING *** buildid cache permissions may need fixing" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
record()
|
||||
{
|
||||
echo "Recording"
|
||||
|
||||
if [ $EUID -ne 0 ] ; then
|
||||
|
||||
if [ "$(cat /proc/sys/kernel/kptr_restrict)" -ne 0 ] ; then
|
||||
echo "*** WARNING *** /proc/sys/kernel/kptr_restrict prevents access to kernel addresses" >&2
|
||||
fi
|
||||
|
||||
if echo "$PERF_OPTIONS" | grep -q ' -a \|^-a \| -a$\|^-a$\| --all-cpus \|^--all-cpus \| --all-cpus$\|^--all-cpus$' ; then
|
||||
echo "*** WARNING *** system-wide tracing without root access will not be able to read all necessary information from /proc" >&2
|
||||
fi
|
||||
|
||||
if echo "$PERF_OPTIONS" | grep -q 'intel_pt\|intel_bts\| -I\|^-I' ; then
|
||||
if [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt -1 ] ; then
|
||||
echo "*** WARNING *** /proc/sys/kernel/perf_event_paranoid restricts buffer size and tracepoint (sched_switch) use" >&2
|
||||
fi
|
||||
|
||||
if echo "$PERF_OPTIONS" | grep -q ' --per-thread \|^--per-thread \| --per-thread$\|^--per-thread$' ; then
|
||||
true
|
||||
elif echo "$PERF_OPTIONS" | grep -q ' -t \|^-t \| -t$\|^-t$' ; then
|
||||
true
|
||||
elif [ ! -r /sys/kernel/debug -o ! -x /sys/kernel/debug ] ; then
|
||||
echo "*** WARNING *** /sys/kernel/debug permissions prevent tracepoint (sched_switch) use" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$1" ] ; then
|
||||
echo "Workload is required for recording" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -e "$PERF_DATA_DIR" ] ; then
|
||||
echo "'$PERF_DATA_DIR' exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find_perf
|
||||
|
||||
mkdir "$PERF_DATA_DIR"
|
||||
|
||||
echo "$PERF record -o $PERF_DATA_DIR/perf.data $PERF_OPTIONS -- $*"
|
||||
"$PERF" record -o "$PERF_DATA_DIR/perf.data" $PERF_OPTIONS -- $* || true
|
||||
|
||||
if rmdir "$PERF_DATA_DIR" > /dev/null 2>/dev/null ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
copy_kcore
|
||||
|
||||
echo "Done"
|
||||
}
|
||||
|
||||
subcommand()
|
||||
{
|
||||
find_perf
|
||||
check_buildid_cache_permissions
|
||||
echo "$PERF $PERF_SUB_COMMAND -i $PERF_DATA_DIR/perf.data --kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms $*"
|
||||
"$PERF" $PERF_SUB_COMMAND -i "$PERF_DATA_DIR/perf.data" "--kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms" $*
|
||||
}
|
||||
|
||||
if [ "$1" = "fix_buildid_cache_permissions" ] ; then
|
||||
fix_buildid_cache_permissions
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PERF_SUB_COMMAND=$1
|
||||
PERF_DATA_DIR=$2
|
||||
shift || true
|
||||
shift || true
|
||||
|
||||
if [ -z "$PERF_SUB_COMMAND" ] ; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -z "$PERF_DATA_DIR" ] ; then
|
||||
usage
|
||||
fi
|
||||
|
||||
case "$PERF_SUB_COMMAND" in
|
||||
"record")
|
||||
while [ "$1" != "--" ] ; do
|
||||
PERF_OPTIONS+="$1 "
|
||||
shift || break
|
||||
done
|
||||
if [ "$1" != "--" ] ; then
|
||||
echo "Options and workload are required for recording" >&2
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
record $*
|
||||
;;
|
||||
"script")
|
||||
subcommand $*
|
||||
;;
|
||||
"report")
|
||||
subcommand $*
|
||||
;;
|
||||
"inject")
|
||||
subcommand $*
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
@ -313,6 +313,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
int status;
|
||||
struct stat st;
|
||||
const char *prefix;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
prefix = NULL;
|
||||
if (p->option & RUN_SETUP)
|
||||
@ -343,7 +344,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
status = 1;
|
||||
/* Check for ENOSPC and EIO errors.. */
|
||||
if (fflush(stdout)) {
|
||||
fprintf(stderr, "write failure on standard output: %s", strerror(errno));
|
||||
fprintf(stderr, "write failure on standard output: %s",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out;
|
||||
}
|
||||
if (ferror(stdout)) {
|
||||
@ -351,7 +353,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
goto out;
|
||||
}
|
||||
if (fclose(stdout)) {
|
||||
fprintf(stderr, "close failed on standard output: %s", strerror(errno));
|
||||
fprintf(stderr, "close failed on standard output: %s",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out;
|
||||
}
|
||||
status = 0;
|
||||
@ -466,6 +469,7 @@ void pthread__unblock_sigwinch(void)
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
/* The page_size is placed in util object. */
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
@ -561,7 +565,7 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
|
||||
fprintf(stderr, "Failed to run command '%s': %s\n",
|
||||
cmd, strerror(errno));
|
||||
cmd, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void);
|
||||
|
||||
struct record_opts {
|
||||
struct target target;
|
||||
int call_graph;
|
||||
bool call_graph_enabled;
|
||||
bool group;
|
||||
bool inherit_stat;
|
||||
bool no_buffering;
|
||||
@ -60,7 +58,6 @@ struct record_opts {
|
||||
u64 branch_stack;
|
||||
u64 default_interval;
|
||||
u64 user_interval;
|
||||
u16 stack_dump_size;
|
||||
bool sample_transaction;
|
||||
unsigned initial_delay;
|
||||
};
|
||||
|
@ -153,6 +153,18 @@ static struct test {
|
||||
.desc = "Test cumulation of child hist entries",
|
||||
.func = test__hists_cumulate,
|
||||
},
|
||||
{
|
||||
.desc = "Test tracking with sched_switch",
|
||||
.func = test__switch_tracking,
|
||||
},
|
||||
{
|
||||
.desc = "Filter fds with revents mask in a fdarray",
|
||||
.func = test__fdarray__filter,
|
||||
},
|
||||
{
|
||||
.desc = "Add fd to a fdarray, making it autogrow",
|
||||
.func = test__fdarray__add,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
@ -185,9 +197,11 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
|
||||
static int run_test(struct test *test)
|
||||
{
|
||||
int status, err = -1, child = fork();
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (child < 0) {
|
||||
pr_err("failed to fork test: %s\n", strerror(errno));
|
||||
pr_err("failed to fork test: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -297,7 +311,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
symbol_conf.sort_by_name = true;
|
||||
symbol_conf.try_vmlinux_path = true;
|
||||
|
||||
if (symbol__init() < 0)
|
||||
if (symbol__init(NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (skip != NULL)
|
||||
|
174
tools/perf/tests/fdarray.c
Normal file
174
tools/perf/tests/fdarray.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include <api/fd/array.h>
|
||||
#include "util/debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
static void fdarray__init_revents(struct fdarray *fda, short revents)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fda->nr = fda->nr_alloc;
|
||||
|
||||
for (fd = 0; fd < fda->nr; ++fd) {
|
||||
fda->entries[fd].fd = fda->nr - fd;
|
||||
fda->entries[fd].revents = revents;
|
||||
}
|
||||
}
|
||||
|
||||
static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
|
||||
if (!verbose)
|
||||
return 0;
|
||||
|
||||
printed += fprintf(fp, "\n%s: ", prefix);
|
||||
return printed + fdarray__fprintf(fda, fp);
|
||||
}
|
||||
|
||||
int test__fdarray__filter(void)
|
||||
{
|
||||
int nr_fds, expected_fd[2], fd, err = TEST_FAIL;
|
||||
struct fdarray *fda = fdarray__new(5, 5);
|
||||
|
||||
if (fda == NULL) {
|
||||
pr_debug("\nfdarray__new() failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
fdarray__init_revents(fda, POLLIN);
|
||||
nr_fds = fdarray__filter(fda, POLLHUP, NULL);
|
||||
if (nr_fds != fda->nr_alloc) {
|
||||
pr_debug("\nfdarray__filter()=%d != %d shouldn't have filtered anything",
|
||||
nr_fds, fda->nr_alloc);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
fdarray__init_revents(fda, POLLHUP);
|
||||
nr_fds = fdarray__filter(fda, POLLHUP, NULL);
|
||||
if (nr_fds != 0) {
|
||||
pr_debug("\nfdarray__filter()=%d != %d, should have filtered all fds",
|
||||
nr_fds, fda->nr_alloc);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
fdarray__init_revents(fda, POLLHUP);
|
||||
fda->entries[2].revents = POLLIN;
|
||||
expected_fd[0] = fda->entries[2].fd;
|
||||
|
||||
pr_debug("\nfiltering all but fda->entries[2]:");
|
||||
fdarray__fprintf_prefix(fda, "before", stderr);
|
||||
nr_fds = fdarray__filter(fda, POLLHUP, NULL);
|
||||
fdarray__fprintf_prefix(fda, " after", stderr);
|
||||
if (nr_fds != 1) {
|
||||
pr_debug("\nfdarray__filter()=%d != 1, should have left just one event", nr_fds);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (fda->entries[0].fd != expected_fd[0]) {
|
||||
pr_debug("\nfda->entries[0].fd=%d != %d\n",
|
||||
fda->entries[0].fd, expected_fd[0]);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
fdarray__init_revents(fda, POLLHUP);
|
||||
fda->entries[0].revents = POLLIN;
|
||||
expected_fd[0] = fda->entries[0].fd;
|
||||
fda->entries[3].revents = POLLIN;
|
||||
expected_fd[1] = fda->entries[3].fd;
|
||||
|
||||
pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):");
|
||||
fdarray__fprintf_prefix(fda, "before", stderr);
|
||||
nr_fds = fdarray__filter(fda, POLLHUP, NULL);
|
||||
fdarray__fprintf_prefix(fda, " after", stderr);
|
||||
if (nr_fds != 2) {
|
||||
pr_debug("\nfdarray__filter()=%d != 2, should have left just two events",
|
||||
nr_fds);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
for (fd = 0; fd < 2; ++fd) {
|
||||
if (fda->entries[fd].fd != expected_fd[fd]) {
|
||||
pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd,
|
||||
fda->entries[fd].fd, expected_fd[fd]);
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
err = 0;
|
||||
out_delete:
|
||||
fdarray__delete(fda);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int test__fdarray__add(void)
|
||||
{
|
||||
int err = TEST_FAIL;
|
||||
struct fdarray *fda = fdarray__new(2, 2);
|
||||
|
||||
if (fda == NULL) {
|
||||
pr_debug("\nfdarray__new() failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
#define FDA_CHECK(_idx, _fd, _revents) \
|
||||
if (fda->entries[_idx].fd != _fd) { \
|
||||
pr_debug("\n%d: fda->entries[%d](%d) != %d!", \
|
||||
__LINE__, _idx, fda->entries[1].fd, _fd); \
|
||||
goto out_delete; \
|
||||
} \
|
||||
if (fda->entries[_idx].events != (_revents)) { \
|
||||
pr_debug("\n%d: fda->entries[%d].revents(%d) != %d!", \
|
||||
__LINE__, _idx, fda->entries[_idx].fd, _revents); \
|
||||
goto out_delete; \
|
||||
}
|
||||
|
||||
#define FDA_ADD(_idx, _fd, _revents, _nr) \
|
||||
if (fdarray__add(fda, _fd, _revents) < 0) { \
|
||||
pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \
|
||||
__LINE__,_fd, _revents); \
|
||||
goto out_delete; \
|
||||
} \
|
||||
if (fda->nr != _nr) { \
|
||||
pr_debug("\n%d: fdarray__add(fda, %d, %d)=%d != %d", \
|
||||
__LINE__,_fd, _revents, fda->nr, _nr); \
|
||||
goto out_delete; \
|
||||
} \
|
||||
FDA_CHECK(_idx, _fd, _revents)
|
||||
|
||||
FDA_ADD(0, 1, POLLIN, 1);
|
||||
FDA_ADD(1, 2, POLLERR, 2);
|
||||
|
||||
fdarray__fprintf_prefix(fda, "before growing array", stderr);
|
||||
|
||||
FDA_ADD(2, 35, POLLHUP, 3);
|
||||
|
||||
if (fda->entries == NULL) {
|
||||
pr_debug("\nfdarray__add(fda, 35, POLLHUP) should have allocated fda->pollfd!");
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
fdarray__fprintf_prefix(fda, "after 3rd add", stderr);
|
||||
|
||||
FDA_ADD(3, 88, POLLIN | POLLOUT, 4);
|
||||
|
||||
fdarray__fprintf_prefix(fda, "after 4th add", stderr);
|
||||
|
||||
FDA_CHECK(0, 1, POLLIN);
|
||||
FDA_CHECK(1, 2, POLLERR);
|
||||
FDA_CHECK(2, 35, POLLHUP);
|
||||
FDA_CHECK(3, 88, POLLIN | POLLOUT);
|
||||
|
||||
#undef FDA_ADD
|
||||
#undef FDA_CHECK
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
err = 0;
|
||||
out_delete:
|
||||
fdarray__delete(fda);
|
||||
out:
|
||||
return err;
|
||||
}
|
@ -31,6 +31,7 @@ int test__basic_mmap(void)
|
||||
unsigned int nr_events[nsyscalls],
|
||||
expected_nr_events[nsyscalls], i, j;
|
||||
struct perf_evsel *evsels[nsyscalls], *evsel;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
@ -49,7 +50,7 @@ int test__basic_mmap(void)
|
||||
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[0], strerror(errno));
|
||||
cpus->map[0], strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
@ -79,7 +80,7 @@ int test__basic_mmap(void)
|
||||
if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -89,7 +90,7 @@ int test__basic_mmap(void)
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
pr_debug("failed to mmap events: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ int test__open_syscall_event_on_all_cpus(void)
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
cpu_set_t cpu_set;
|
||||
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
@ -35,7 +36,7 @@ int test__open_syscall_event_on_all_cpus(void)
|
||||
if (perf_evsel__open(evsel, cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
@ -56,7 +57,7 @@ int test__open_syscall_event_on_all_cpus(void)
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[cpu],
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_close_fd;
|
||||
}
|
||||
for (i = 0; i < ncalls; ++i) {
|
||||
|
@ -22,6 +22,7 @@ int test__syscall_open_tp_fields(void)
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i, nr_events = 0, nr_polls = 0;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (evlist == NULL) {
|
||||
pr_debug("%s: perf_evlist__new\n", __func__);
|
||||
@ -48,13 +49,15 @@ int test__syscall_open_tp_fields(void)
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
pr_debug("perf_evlist__open: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
pr_debug("perf_evlist__mmap: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -102,7 +105,7 @@ int test__syscall_open_tp_fields(void)
|
||||
}
|
||||
|
||||
if (nr_events == before)
|
||||
poll(evlist->pollfd, evlist->nr_fds, 10);
|
||||
perf_evlist__poll(evlist, 10);
|
||||
|
||||
if (++nr_polls > 5) {
|
||||
pr_debug("%s: no events!\n", __func__);
|
||||
|
@ -9,6 +9,7 @@ int test__open_syscall_event(void)
|
||||
struct perf_evsel *evsel;
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
@ -24,7 +25,7 @@ int test__open_syscall_event(void)
|
||||
if (perf_evsel__open_per_thread(evsel, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ int test__PERF_RECORD(void)
|
||||
int err = -1, errs = 0, i, wakeups = 0;
|
||||
u32 cpu;
|
||||
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (evlist == NULL || argv == NULL) {
|
||||
pr_debug("Not enough memory to create evlist\n");
|
||||
@ -100,7 +101,8 @@ int test__PERF_RECORD(void)
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
|
||||
if (err < 0) {
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -110,7 +112,8 @@ int test__PERF_RECORD(void)
|
||||
* So that we can check perf_sample.cpu on all the samples.
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
pr_debug("sched_setaffinity: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -120,7 +123,8 @@ int test__PERF_RECORD(void)
|
||||
*/
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
pr_debug("perf_evlist__open: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -131,7 +135,8 @@ int test__PERF_RECORD(void)
|
||||
*/
|
||||
err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
pr_debug("perf_evlist__mmap: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -263,7 +268,7 @@ int test__PERF_RECORD(void)
|
||||
* perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
|
||||
*/
|
||||
if (total_events == before && false)
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
perf_evlist__poll(evlist, -1);
|
||||
|
||||
sleep(1);
|
||||
if (++wakeups > 5) {
|
||||
|
@ -152,7 +152,7 @@ int test__pmu(void)
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = perf_pmu__config_terms(&formats, &attr, terms);
|
||||
ret = perf_pmu__config_terms(&formats, &attr, terms, false);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -100,6 +100,7 @@ static int __test__rdpmc(void)
|
||||
};
|
||||
u64 delta_sum = 0;
|
||||
struct sigaction sa;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_sigaction = segfault_handler;
|
||||
@ -109,14 +110,15 @@ static int __test__rdpmc(void)
|
||||
perf_event_open_cloexec_flag());
|
||||
if (fd < 0) {
|
||||
pr_err("Error: sys_perf_event_open() syscall returned "
|
||||
"with %d (%s)\n", fd, strerror(errno));
|
||||
"with %d (%s)\n", fd,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (addr == (void *)(-1)) {
|
||||
pr_err("Error: mmap() syscall returned with (%s)\n",
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
|
||||
volatile int tmp = 0;
|
||||
u64 total_periods = 0;
|
||||
int nr_samples = 0;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
union perf_event *event;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist;
|
||||
@ -62,14 +63,15 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
|
||||
|
||||
err = -errno;
|
||||
pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n",
|
||||
strerror(errno), knob, (u64)attr.sample_freq);
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)),
|
||||
knob, (u64)attr.sample_freq);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, 128, true);
|
||||
if (err < 0) {
|
||||
pr_debug("failed to mmap event: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
|
572
tools/perf/tests/switch-tracking.c
Normal file
572
tools/perf/tests/switch-tracking.c
Normal file
@ -0,0 +1,572 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "parse-events.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
|
||||
static int spin_sleep(void)
|
||||
{
|
||||
struct timeval start, now, diff, maxtime;
|
||||
struct timespec ts;
|
||||
int err, i;
|
||||
|
||||
maxtime.tv_sec = 0;
|
||||
maxtime.tv_usec = 50000;
|
||||
|
||||
err = gettimeofday(&start, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Spin for 50ms */
|
||||
while (1) {
|
||||
for (i = 0; i < 1000; i++)
|
||||
barrier();
|
||||
|
||||
err = gettimeofday(&now, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
timersub(&now, &start, &diff);
|
||||
if (timercmp(&diff, &maxtime, > /* For checkpatch */))
|
||||
break;
|
||||
}
|
||||
|
||||
ts.tv_nsec = 50 * 1000 * 1000;
|
||||
ts.tv_sec = 0;
|
||||
|
||||
/* Sleep for 50ms */
|
||||
err = nanosleep(&ts, NULL);
|
||||
if (err == EINTR)
|
||||
err = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct switch_tracking {
|
||||
struct perf_evsel *switch_evsel;
|
||||
struct perf_evsel *cycles_evsel;
|
||||
pid_t *tids;
|
||||
int nr_tids;
|
||||
int comm_seen[4];
|
||||
int cycles_before_comm_1;
|
||||
int cycles_between_comm_2_and_comm_3;
|
||||
int cycles_after_comm_4;
|
||||
};
|
||||
|
||||
static int check_comm(struct switch_tracking *switch_tracking,
|
||||
union perf_event *event, const char *comm, int nr)
|
||||
{
|
||||
if (event->header.type == PERF_RECORD_COMM &&
|
||||
(pid_t)event->comm.pid == getpid() &&
|
||||
(pid_t)event->comm.tid == getpid() &&
|
||||
strcmp(event->comm.comm, comm) == 0) {
|
||||
if (switch_tracking->comm_seen[nr]) {
|
||||
pr_debug("Duplicate comm event\n");
|
||||
return -1;
|
||||
}
|
||||
switch_tracking->comm_seen[nr] = 1;
|
||||
pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
|
||||
{
|
||||
int i, nr = cpu + 1;
|
||||
|
||||
if (cpu < 0)
|
||||
return -1;
|
||||
|
||||
if (!switch_tracking->tids) {
|
||||
switch_tracking->tids = calloc(nr, sizeof(pid_t));
|
||||
if (!switch_tracking->tids)
|
||||
return -1;
|
||||
for (i = 0; i < nr; i++)
|
||||
switch_tracking->tids[i] = -1;
|
||||
switch_tracking->nr_tids = nr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cpu >= switch_tracking->nr_tids) {
|
||||
void *addr;
|
||||
|
||||
addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
|
||||
if (!addr)
|
||||
return -1;
|
||||
switch_tracking->tids = addr;
|
||||
for (i = switch_tracking->nr_tids; i < nr; i++)
|
||||
switch_tracking->tids[i] = -1;
|
||||
switch_tracking->nr_tids = nr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_evlist *evlist,
|
||||
union perf_event *event,
|
||||
struct switch_tracking *switch_tracking)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
struct perf_evsel *evsel;
|
||||
pid_t next_tid, prev_tid;
|
||||
int cpu, err;
|
||||
|
||||
if (perf_evlist__parse_sample(evlist, event, &sample)) {
|
||||
pr_debug("perf_evlist__parse_sample failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__id2evsel(evlist, sample.id);
|
||||
if (evsel == switch_tracking->switch_evsel) {
|
||||
next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
|
||||
prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
|
||||
cpu = sample.cpu;
|
||||
pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
|
||||
cpu, prev_tid, next_tid);
|
||||
err = check_cpu(switch_tracking, cpu);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* Check for no missing sched_switch events i.e. that the
|
||||
* evsel->system_wide flag has worked.
|
||||
*/
|
||||
if (switch_tracking->tids[cpu] != -1 &&
|
||||
switch_tracking->tids[cpu] != prev_tid) {
|
||||
pr_debug("Missing sched_switch events\n");
|
||||
return -1;
|
||||
}
|
||||
switch_tracking->tids[cpu] = next_tid;
|
||||
}
|
||||
|
||||
if (evsel == switch_tracking->cycles_evsel) {
|
||||
pr_debug3("cycles event\n");
|
||||
if (!switch_tracking->comm_seen[0])
|
||||
switch_tracking->cycles_before_comm_1 = 1;
|
||||
if (switch_tracking->comm_seen[1] &&
|
||||
!switch_tracking->comm_seen[2])
|
||||
switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
|
||||
if (switch_tracking->comm_seen[3])
|
||||
switch_tracking->cycles_after_comm_4 = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event(struct perf_evlist *evlist, union perf_event *event,
|
||||
struct switch_tracking *switch_tracking)
|
||||
{
|
||||
if (event->header.type == PERF_RECORD_SAMPLE)
|
||||
return process_sample_event(evlist, event, switch_tracking);
|
||||
|
||||
if (event->header.type == PERF_RECORD_COMM) {
|
||||
int err, done = 0;
|
||||
|
||||
err = check_comm(switch_tracking, event, "Test COMM 1", 0);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
done += err;
|
||||
err = check_comm(switch_tracking, event, "Test COMM 2", 1);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
done += err;
|
||||
err = check_comm(switch_tracking, event, "Test COMM 3", 2);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
done += err;
|
||||
err = check_comm(switch_tracking, event, "Test COMM 4", 3);
|
||||
if (err < 0)
|
||||
return -1;
|
||||
done += err;
|
||||
if (done != 1) {
|
||||
pr_debug("Unexpected comm event\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct event_node {
|
||||
struct list_head list;
|
||||
union perf_event *event;
|
||||
u64 event_time;
|
||||
};
|
||||
|
||||
static int add_event(struct perf_evlist *evlist, struct list_head *events,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
struct event_node *node;
|
||||
|
||||
node = malloc(sizeof(struct event_node));
|
||||
if (!node) {
|
||||
pr_debug("malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
node->event = event;
|
||||
list_add(&node->list, events);
|
||||
|
||||
if (perf_evlist__parse_sample(evlist, event, &sample)) {
|
||||
pr_debug("perf_evlist__parse_sample failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sample.time) {
|
||||
pr_debug("event with no time\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
node->event_time = sample.time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_event_nodes(struct list_head *events)
|
||||
{
|
||||
struct event_node *node;
|
||||
|
||||
while (!list_empty(events)) {
|
||||
node = list_entry(events->next, struct event_node, list);
|
||||
list_del(&node->list);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
static int compar(const void *a, const void *b)
|
||||
{
|
||||
const struct event_node *nodea = a;
|
||||
const struct event_node *nodeb = b;
|
||||
s64 cmp = nodea->event_time - nodeb->event_time;
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
static int process_events(struct perf_evlist *evlist,
|
||||
struct switch_tracking *switch_tracking)
|
||||
{
|
||||
union perf_event *event;
|
||||
unsigned pos, cnt = 0;
|
||||
LIST_HEAD(events);
|
||||
struct event_node *events_array, *node;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
cnt += 1;
|
||||
ret = add_event(evlist, &events, event);
|
||||
perf_evlist__mmap_consume(evlist, i);
|
||||
if (ret < 0)
|
||||
goto out_free_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
events_array = calloc(cnt, sizeof(struct event_node));
|
||||
if (!events_array) {
|
||||
pr_debug("calloc failed\n");
|
||||
ret = -1;
|
||||
goto out_free_nodes;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
list_for_each_entry(node, &events, list)
|
||||
events_array[pos++] = *node;
|
||||
|
||||
qsort(events_array, cnt, sizeof(struct event_node), compar);
|
||||
|
||||
for (pos = 0; pos < cnt; pos++) {
|
||||
ret = process_event(evlist, events_array[pos].event,
|
||||
switch_tracking);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out_free:
|
||||
pr_debug("%u events recorded\n", cnt);
|
||||
free(events_array);
|
||||
out_free_nodes:
|
||||
free_event_nodes(&events);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* test__switch_tracking - test using sched_switch and tracking events.
|
||||
*
|
||||
* This function implements a test that checks that sched_switch events and
|
||||
* tracking events can be recorded for a workload (current process) using the
|
||||
* evsel->system_wide and evsel->tracking flags (respectively) with other events
|
||||
* sometimes enabled or disabled.
|
||||
*/
|
||||
int test__switch_tracking(void)
|
||||
{
|
||||
const char *sched_switch = "sched:sched_switch";
|
||||
struct switch_tracking switch_tracking = { .tids = NULL, };
|
||||
struct record_opts opts = {
|
||||
.mmap_pages = UINT_MAX,
|
||||
.user_freq = UINT_MAX,
|
||||
.user_interval = ULLONG_MAX,
|
||||
.freq = 4000,
|
||||
.target = {
|
||||
.uses_mmap = true,
|
||||
},
|
||||
};
|
||||
struct thread_map *threads = NULL;
|
||||
struct cpu_map *cpus = NULL;
|
||||
struct perf_evlist *evlist = NULL;
|
||||
struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
|
||||
struct perf_evsel *switch_evsel, *tracking_evsel;
|
||||
const char *comm;
|
||||
int err = -1;
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (!threads) {
|
||||
pr_debug("thread_map__new failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (!cpus) {
|
||||
pr_debug("cpu_map__new failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (!evlist) {
|
||||
pr_debug("perf_evlist__new failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
perf_evlist__set_maps(evlist, cpus, threads);
|
||||
|
||||
/* First event */
|
||||
err = parse_events(evlist, "cpu-clock:u");
|
||||
if (err) {
|
||||
pr_debug("Failed to parse event dummy:u\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
cpu_clocks_evsel = perf_evlist__last(evlist);
|
||||
|
||||
/* Second event */
|
||||
err = parse_events(evlist, "cycles:u");
|
||||
if (err) {
|
||||
pr_debug("Failed to parse event cycles:u\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
cycles_evsel = perf_evlist__last(evlist);
|
||||
|
||||
/* Third event */
|
||||
if (!perf_evlist__can_select_event(evlist, sched_switch)) {
|
||||
fprintf(stderr, " (no sched_switch)");
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = parse_events(evlist, sched_switch);
|
||||
if (err) {
|
||||
pr_debug("Failed to parse event %s\n", sched_switch);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
switch_evsel = perf_evlist__last(evlist);
|
||||
|
||||
perf_evsel__set_sample_bit(switch_evsel, CPU);
|
||||
perf_evsel__set_sample_bit(switch_evsel, TIME);
|
||||
|
||||
switch_evsel->system_wide = true;
|
||||
switch_evsel->no_aux_samples = true;
|
||||
switch_evsel->immediate = true;
|
||||
|
||||
/* Test moving an event to the front */
|
||||
if (cycles_evsel == perf_evlist__first(evlist)) {
|
||||
pr_debug("cycles event already at front");
|
||||
goto out_err;
|
||||
}
|
||||
perf_evlist__to_front(evlist, cycles_evsel);
|
||||
if (cycles_evsel != perf_evlist__first(evlist)) {
|
||||
pr_debug("Failed to move cycles event to front");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
perf_evsel__set_sample_bit(cycles_evsel, CPU);
|
||||
perf_evsel__set_sample_bit(cycles_evsel, TIME);
|
||||
|
||||
/* Fourth event */
|
||||
err = parse_events(evlist, "dummy:u");
|
||||
if (err) {
|
||||
pr_debug("Failed to parse event dummy:u\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
tracking_evsel = perf_evlist__last(evlist);
|
||||
|
||||
perf_evlist__set_tracking_event(evlist, tracking_evsel);
|
||||
|
||||
tracking_evsel->attr.freq = 0;
|
||||
tracking_evsel->attr.sample_period = 1;
|
||||
|
||||
perf_evsel__set_sample_bit(tracking_evsel, TIME);
|
||||
|
||||
/* Config events */
|
||||
perf_evlist__config(evlist, &opts);
|
||||
|
||||
/* Check moved event is still at the front */
|
||||
if (cycles_evsel != perf_evlist__first(evlist)) {
|
||||
pr_debug("Front event no longer at front");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Check tracking event is tracking */
|
||||
if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
|
||||
pr_debug("Tracking event not tracking\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Check non-tracking events are not tracking */
|
||||
evlist__for_each(evlist, evsel) {
|
||||
if (evsel != tracking_evsel) {
|
||||
if (evsel->attr.mmap || evsel->attr.comm) {
|
||||
pr_debug("Non-tracking event is tracking\n");
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (perf_evlist__open(evlist) < 0) {
|
||||
fprintf(stderr, " (not supported)");
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err) {
|
||||
pr_debug("perf_evlist__mmap failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
|
||||
if (err) {
|
||||
pr_debug("perf_evlist__disable_event failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = spin_sleep();
|
||||
if (err) {
|
||||
pr_debug("spin_sleep failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
comm = "Test COMM 1";
|
||||
err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
|
||||
if (err) {
|
||||
pr_debug("PR_SET_NAME failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = perf_evlist__disable_event(evlist, cycles_evsel);
|
||||
if (err) {
|
||||
pr_debug("perf_evlist__disable_event failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
comm = "Test COMM 2";
|
||||
err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
|
||||
if (err) {
|
||||
pr_debug("PR_SET_NAME failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = spin_sleep();
|
||||
if (err) {
|
||||
pr_debug("spin_sleep failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
comm = "Test COMM 3";
|
||||
err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
|
||||
if (err) {
|
||||
pr_debug("PR_SET_NAME failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = perf_evlist__enable_event(evlist, cycles_evsel);
|
||||
if (err) {
|
||||
pr_debug("perf_evlist__disable_event failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
comm = "Test COMM 4";
|
||||
err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
|
||||
if (err) {
|
||||
pr_debug("PR_SET_NAME failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = spin_sleep();
|
||||
if (err) {
|
||||
pr_debug("spin_sleep failed!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
switch_tracking.switch_evsel = switch_evsel;
|
||||
switch_tracking.cycles_evsel = cycles_evsel;
|
||||
|
||||
err = process_events(evlist, &switch_tracking);
|
||||
|
||||
zfree(&switch_tracking.tids);
|
||||
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Check all 4 comm events were seen i.e. that evsel->tracking works */
|
||||
if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
|
||||
!switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
|
||||
pr_debug("Missing comm events\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Check cycles event got enabled */
|
||||
if (!switch_tracking.cycles_before_comm_1) {
|
||||
pr_debug("Missing cycles events\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Check cycles event got disabled */
|
||||
if (switch_tracking.cycles_between_comm_2_and_comm_3) {
|
||||
pr_debug("cycles events even though event was disabled\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Check cycles event got enabled again */
|
||||
if (!switch_tracking.cycles_after_comm_4) {
|
||||
pr_debug("Missing cycles events\n");
|
||||
goto out_err;
|
||||
}
|
||||
out:
|
||||
if (evlist) {
|
||||
perf_evlist__disable(evlist);
|
||||
perf_evlist__delete(evlist);
|
||||
} else {
|
||||
cpu_map__delete(cpus);
|
||||
thread_map__delete(threads);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
out_err:
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
@ -42,6 +42,7 @@ int test__task_exit(void)
|
||||
.uses_mmap = true,
|
||||
};
|
||||
const char *argv[] = { "true", NULL };
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
signal(SIGCHLD, sig_handler);
|
||||
|
||||
@ -82,13 +83,14 @@ int test__task_exit(void)
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't open the evlist: %s\n", strerror(-err));
|
||||
pr_debug("Couldn't open the evlist: %s\n",
|
||||
strerror_r(-err, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
pr_debug("failed to mmap events: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
@ -103,7 +105,7 @@ retry:
|
||||
}
|
||||
|
||||
if (!exited || !nr_exit) {
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
perf_evlist__poll(evlist, -1);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,9 @@ int test__mmap_thread_lookup(void);
|
||||
int test__thread_mg_share(void);
|
||||
int test__hists_output(void);
|
||||
int test__hists_cumulate(void);
|
||||
int test__switch_tracking(void);
|
||||
int test__fdarray__filter(void);
|
||||
int test__fdarray__add(void);
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "../../util/pstack.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../util/top.h"
|
||||
#include "../../arch/common.h"
|
||||
|
||||
#include "../browser.h"
|
||||
@ -228,8 +229,10 @@ static void callchain_node__init_have_children(struct callchain_node *node)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
|
||||
list_for_each_entry(chain, &node->val, list)
|
||||
if (!list_empty(&node->val)) {
|
||||
chain = list_entry(node->val.prev, struct callchain_list, list);
|
||||
chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
|
||||
}
|
||||
|
||||
callchain_node__init_have_children_rb_tree(node);
|
||||
}
|
||||
@ -474,26 +477,87 @@ static char *callchain_list__sym_name(struct callchain_list *cl,
|
||||
return bf;
|
||||
}
|
||||
|
||||
struct callchain_print_arg {
|
||||
/* for hists browser */
|
||||
off_t row_offset;
|
||||
bool is_current_entry;
|
||||
|
||||
/* for file dump */
|
||||
FILE *fp;
|
||||
int printed;
|
||||
};
|
||||
|
||||
typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row,
|
||||
struct callchain_print_arg *arg);
|
||||
|
||||
static void hist_browser__show_callchain_entry(struct hist_browser *browser,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row,
|
||||
struct callchain_print_arg *arg)
|
||||
{
|
||||
int color, width;
|
||||
char folded_sign = callchain_list__folded(chain);
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
width = browser->b.width - (offset + 2);
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
arg->is_current_entry = true;
|
||||
}
|
||||
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
slsmg_write_nstring(" ", offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(str, width);
|
||||
}
|
||||
|
||||
static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row __maybe_unused,
|
||||
struct callchain_print_arg *arg)
|
||||
{
|
||||
char folded_sign = callchain_list__folded(chain);
|
||||
|
||||
arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
|
||||
folded_sign, str);
|
||||
}
|
||||
|
||||
typedef bool (*check_output_full_fn)(struct hist_browser *browser,
|
||||
unsigned short row);
|
||||
|
||||
static bool hist_browser__check_output_full(struct hist_browser *browser,
|
||||
unsigned short row)
|
||||
{
|
||||
return browser->b.rows == row;
|
||||
}
|
||||
|
||||
static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
|
||||
unsigned short row __maybe_unused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LEVEL_OFFSET_STEP 3
|
||||
|
||||
static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
|
||||
struct callchain_node *chain_node,
|
||||
u64 total, int level,
|
||||
unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
struct rb_root *root, int level,
|
||||
unsigned short row, u64 total,
|
||||
print_callchain_entry_fn print,
|
||||
struct callchain_print_arg *arg,
|
||||
check_output_full_fn is_output_full)
|
||||
{
|
||||
struct rb_node *node;
|
||||
int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total, remaining;
|
||||
int first_row = row, offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = chain_node->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
remaining = new_total;
|
||||
node = rb_first(&chain_node->rb_root);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
@ -503,30 +567,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
|
||||
int first = true;
|
||||
int extra_offset = 0;
|
||||
|
||||
remaining -= cumul;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
char bf[1024], *alloc_str;
|
||||
const char *str;
|
||||
int color;
|
||||
bool was_first = first;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
else if (level > 1)
|
||||
extra_offset = LEVEL_OFFSET_STEP;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
if (*row_offset != 0) {
|
||||
--*row_offset;
|
||||
if (arg->row_offset != 0) {
|
||||
arg->row_offset--;
|
||||
goto do_next;
|
||||
}
|
||||
|
||||
alloc_str = NULL;
|
||||
str = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
if (was_first) {
|
||||
double percent = cumul * 100.0 / new_total;
|
||||
|
||||
if (was_first && level > 1) {
|
||||
double percent = cumul * 100.0 / total;
|
||||
|
||||
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
||||
str = "Not enough memory!";
|
||||
@ -534,22 +596,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
|
||||
str = alloc_str;
|
||||
}
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
width = browser->b.width - (offset + extra_offset + 2);
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
*is_current_entry = true;
|
||||
}
|
||||
print(browser, chain, str, offset + extra_offset, row, arg);
|
||||
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
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.rows)
|
||||
if (is_output_full(browser, ++row))
|
||||
goto out;
|
||||
do_next:
|
||||
if (folded_sign == '+')
|
||||
@ -558,92 +609,24 @@ do_next:
|
||||
|
||||
if (folded_sign == '-') {
|
||||
const int new_level = level + (extra_offset ? 2 : 1);
|
||||
row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
|
||||
new_level, row, row_offset,
|
||||
is_current_entry);
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = child->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
row += hist_browser__show_callchain(browser, &child->rb_root,
|
||||
new_level, row, new_total,
|
||||
print, arg, is_output_full);
|
||||
}
|
||||
if (row == browser->b.rows)
|
||||
goto out;
|
||||
if (is_output_full(browser, row))
|
||||
break;
|
||||
node = next;
|
||||
}
|
||||
out:
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
static int hist_browser__show_callchain_node(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
int level, unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
int first_row = row,
|
||||
offset = level * LEVEL_OFFSET_STEP,
|
||||
width = browser->b.width - offset;
|
||||
char folded_sign = ' ';
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char bf[1024], *s;
|
||||
int color;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
|
||||
if (*row_offset != 0) {
|
||||
--*row_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
*is_current_entry = true;
|
||||
}
|
||||
|
||||
s = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
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.rows)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (folded_sign == '-')
|
||||
row += hist_browser__show_callchain_node_rb_tree(browser, node,
|
||||
browser->hists->stats.total_period,
|
||||
level + 1, row,
|
||||
row_offset,
|
||||
is_current_entry);
|
||||
out:
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
struct rb_root *chain,
|
||||
int level, unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int first_row = row;
|
||||
|
||||
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
|
||||
row += hist_browser__show_callchain_node(browser, node, level,
|
||||
row, row_offset,
|
||||
is_current_entry);
|
||||
if (row == browser->b.rows)
|
||||
break;
|
||||
}
|
||||
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
struct hpp_arg {
|
||||
struct ui_browser *b;
|
||||
char folded_sign;
|
||||
@ -653,17 +636,18 @@ struct hpp_arg {
|
||||
static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
struct hpp_arg *arg = hpp->ptr;
|
||||
int ret;
|
||||
int ret, len;
|
||||
va_list args;
|
||||
double percent;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
va_end(args);
|
||||
|
||||
ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
|
||||
|
||||
ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
|
||||
slsmg_printf("%s", hpp->buf);
|
||||
|
||||
advance_hpp(hpp, ret);
|
||||
@ -677,12 +661,12 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \
|
||||
} \
|
||||
\
|
||||
static int \
|
||||
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
|
||||
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
|
||||
__hpp__slsmg_color_printf, true); \
|
||||
return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
|
||||
__hpp__slsmg_color_printf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
@ -692,18 +676,20 @@ static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
|
||||
} \
|
||||
\
|
||||
static int \
|
||||
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
|
||||
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
if (!symbol_conf.cumulate_callchain) { \
|
||||
int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
|
||||
int len = fmt->user_len ?: fmt->len; \
|
||||
int ret = scnprintf(hpp->buf, hpp->size, \
|
||||
"%*s", len, "N/A"); \
|
||||
slsmg_printf("%s", hpp->buf); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
|
||||
__hpp__slsmg_color_printf, true); \
|
||||
return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
|
||||
" %*.2f%%", __hpp__slsmg_color_printf, true); \
|
||||
}
|
||||
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period)
|
||||
@ -812,10 +798,18 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
--row_offset;
|
||||
|
||||
if (folded_sign == '-' && row != browser->b.rows) {
|
||||
printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
|
||||
1, row, &row_offset,
|
||||
¤t_entry);
|
||||
if (current_entry)
|
||||
u64 total = hists__total_period(entry->hists);
|
||||
struct callchain_print_arg arg = {
|
||||
.row_offset = row_offset,
|
||||
.is_current_entry = current_entry,
|
||||
};
|
||||
|
||||
printed += hist_browser__show_callchain(browser,
|
||||
&entry->sorted_chain, 1, row, total,
|
||||
hist_browser__show_callchain_entry, &arg,
|
||||
hist_browser__check_output_full);
|
||||
|
||||
if (arg.is_current_entry)
|
||||
browser->he_selection = entry;
|
||||
}
|
||||
|
||||
@ -847,9 +841,6 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
|
||||
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;
|
||||
@ -1074,113 +1065,21 @@ do_offset:
|
||||
}
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
|
||||
struct callchain_node *chain_node,
|
||||
u64 total, int level,
|
||||
FILE *fp)
|
||||
{
|
||||
struct rb_node *node;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total, remaining;
|
||||
int printed = 0;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = chain_node->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
remaining = new_total;
|
||||
node = rb_first(&chain_node->rb_root);
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
u64 cumul = callchain_cumul_hits(child);
|
||||
struct callchain_list *chain;
|
||||
char folded_sign = ' ';
|
||||
int first = true;
|
||||
int extra_offset = 0;
|
||||
|
||||
remaining -= cumul;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
char bf[1024], *alloc_str;
|
||||
const char *str;
|
||||
bool was_first = first;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
extra_offset = LEVEL_OFFSET_STEP;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
|
||||
alloc_str = NULL;
|
||||
str = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
if (was_first) {
|
||||
double percent = cumul * 100.0 / new_total;
|
||||
|
||||
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
||||
str = "Not enough memory!";
|
||||
else
|
||||
str = alloc_str;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
|
||||
free(alloc_str);
|
||||
if (folded_sign == '+')
|
||||
break;
|
||||
}
|
||||
|
||||
if (folded_sign == '-') {
|
||||
const int new_level = level + (extra_offset ? 2 : 1);
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
|
||||
new_level, fp);
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
int level, FILE *fp)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
char folded_sign = ' ';
|
||||
int printed = 0;
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char bf[1024], *s;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
|
||||
}
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
|
||||
browser->hists->stats.total_period,
|
||||
level + 1, fp);
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
|
||||
struct rb_root *chain, int level, FILE *fp)
|
||||
struct hist_entry *he, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int printed = 0;
|
||||
u64 total = hists__total_period(he->hists);
|
||||
struct callchain_print_arg arg = {
|
||||
.fp = fp,
|
||||
};
|
||||
|
||||
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
if (symbol_conf.cumulate_callchain)
|
||||
total = he->stat_acc->period;
|
||||
|
||||
printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
|
||||
}
|
||||
|
||||
return printed;
|
||||
hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
|
||||
hist_browser__fprintf_callchain_entry, &arg,
|
||||
hist_browser__check_dump_full);
|
||||
return arg.printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
@ -1219,7 +1118,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
|
||||
printed += hist_browser__fprintf_callchain(browser, he, fp);
|
||||
|
||||
return printed;
|
||||
}
|
||||
@ -1498,6 +1397,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
char buf[64];
|
||||
char script_opt[64];
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
#define HIST_BROWSER_HELP_COMMON \
|
||||
"h/?/F1 Show this window\n" \
|
||||
@ -1529,6 +1429,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"z Toggle zeroing of samples\n"
|
||||
"/ Filter symbol by name";
|
||||
|
||||
if (browser == NULL)
|
||||
@ -1547,6 +1448,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
|
||||
memset(options, 0, sizeof(options));
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
|
||||
if (symbol_conf.col_width_list_str)
|
||||
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
|
||||
|
||||
while (1) {
|
||||
const struct thread *thread = NULL;
|
||||
const struct dso *dso = NULL;
|
||||
@ -1623,6 +1530,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
case 'F':
|
||||
symbol_conf.filter_relative ^= 1;
|
||||
continue;
|
||||
case 'z':
|
||||
if (!is_report_browser(hbt)) {
|
||||
struct perf_top *top = hbt->arg;
|
||||
|
||||
top->zero = !top->zero;
|
||||
}
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
|
@ -11,6 +11,7 @@
|
||||
static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
int ret = 0;
|
||||
int len;
|
||||
va_list args;
|
||||
double percent;
|
||||
const char *markup;
|
||||
@ -18,6 +19,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
size_t size = hpp->size;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
va_end(args);
|
||||
|
||||
@ -25,7 +27,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
if (markup)
|
||||
ret += scnprintf(buf, size, markup);
|
||||
|
||||
ret += scnprintf(buf + ret, size - ret, fmt, percent);
|
||||
ret += scnprintf(buf + ret, size - ret, fmt, len, percent);
|
||||
|
||||
if (markup)
|
||||
ret += scnprintf(buf + ret, size - ret, "</span>");
|
||||
@ -39,12 +41,12 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
@ -57,8 +59,8 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period)
|
||||
@ -205,10 +207,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
||||
if (perf_hpp__is_sort_entry(fmt))
|
||||
sym_col = col_idx;
|
||||
|
||||
fmt->header(fmt, &hpp, hists_to_evsel(hists));
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, ltrim(s),
|
||||
-1, fmt->name,
|
||||
renderer, "markup",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
@ -15,9 +15,9 @@
|
||||
__ret; \
|
||||
})
|
||||
|
||||
int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, const char *fmt,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, const char *fmt, int len,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
int ret;
|
||||
struct hists *hists = he->hists;
|
||||
@ -32,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
if (total)
|
||||
percent = 100.0 * get_field(he) / total;
|
||||
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
|
||||
} else
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
|
||||
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
int prev_idx, idx_delta;
|
||||
@ -60,19 +60,19 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
*/
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, 0.0);
|
||||
fmt, len, 0.0);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, 0ULL);
|
||||
fmt, len, 0ULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt,
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
|
||||
100.0 * period / total);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt,
|
||||
period);
|
||||
len, period);
|
||||
}
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
@ -86,10 +86,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
*/
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, 0.0);
|
||||
fmt, len, 0.0);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, 0ULL);
|
||||
fmt, len, 0ULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,16 +104,35 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, const char *fmt,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he, hpp_field_fn get_field,
|
||||
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
if (!symbol_conf.cumulate_callchain) {
|
||||
return snprintf(hpp->buf, hpp->size, "%*s",
|
||||
fmt_percent ? 8 : 12, "N/A");
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
|
||||
if (symbol_conf.field_sep) {
|
||||
return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
|
||||
print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent);
|
||||
if (fmt_percent)
|
||||
len -= 2; /* 2 for a space and a % sign */
|
||||
else
|
||||
len -= 1;
|
||||
|
||||
return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he, hpp_field_fn get_field,
|
||||
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
if (!symbol_conf.cumulate_callchain) {
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
|
||||
}
|
||||
|
||||
return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
static int field_cmp(u64 field_a, u64 field_b)
|
||||
@ -190,30 +209,26 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct perf_evsel *evsel) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
if (symbol_conf.event_group) \
|
||||
len = max(len, evsel->nr_members * _unit_width); \
|
||||
\
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
|
||||
static int hpp__width_fn(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp __maybe_unused,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
|
||||
if (symbol_conf.event_group)
|
||||
len = max(len, evsel->nr_members * fmt->len);
|
||||
|
||||
if (len < (int)strlen(fmt->name))
|
||||
len = strlen(fmt->name);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp __maybe_unused, \
|
||||
struct perf_evsel *evsel) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
if (symbol_conf.event_group) \
|
||||
len = max(len, evsel->nr_members * _unit_width); \
|
||||
\
|
||||
return len; \
|
||||
static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int len = hpp__width_fn(fmt, hpp, evsel);
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
|
||||
}
|
||||
|
||||
static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
@ -221,11 +236,12 @@ static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
va_list args;
|
||||
ssize_t ssize = hpp->size;
|
||||
double percent;
|
||||
int ret;
|
||||
int ret, len;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent);
|
||||
va_end(args);
|
||||
|
||||
return (ret >= ssize) ? (ssize - 1) : ret;
|
||||
@ -250,20 +266,19 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
|
||||
hpp_entry_scnprintf, true); \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_FN(_type, _field) \
|
||||
@ -278,20 +293,19 @@ static u64 he_get_acc_##_field(struct hist_entry *he) \
|
||||
return he->stat_acc->_field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \
|
||||
hpp_entry_scnprintf, true); \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_ACC_FN(_type, _field) \
|
||||
@ -306,12 +320,11 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \
|
||||
hpp_entry_scnprintf, false); \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \
|
||||
hpp_entry_scnprintf, false); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_RAW_FN(_type, _field) \
|
||||
@ -321,37 +334,29 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
}
|
||||
|
||||
|
||||
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
#define HPP_PERCENT_FNS(_type, _field) \
|
||||
__HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
__HPP_SORT_FN(_type, _field)
|
||||
|
||||
#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
#define HPP_PERCENT_ACC_FNS(_type, _field) \
|
||||
__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
|
||||
__HPP_SORT_ACC_FN(_type, _field)
|
||||
|
||||
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
#define HPP_RAW_FNS(_type, _field) \
|
||||
__HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
__HPP_SORT_RAW_FN(_type, _field)
|
||||
|
||||
__HPP_HEADER_FN(overhead_self, "Self", 8, 8)
|
||||
HPP_PERCENT_FNS(overhead, period)
|
||||
HPP_PERCENT_FNS(overhead_sys, period_sys)
|
||||
HPP_PERCENT_FNS(overhead_us, period_us)
|
||||
HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys)
|
||||
HPP_PERCENT_FNS(overhead_guest_us, period_guest_us)
|
||||
HPP_PERCENT_ACC_FNS(overhead_acc, period)
|
||||
|
||||
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
|
||||
HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
|
||||
HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)
|
||||
|
||||
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
|
||||
HPP_RAW_FNS(period, "Period", period, 12, 12)
|
||||
HPP_RAW_FNS(samples, nr_events)
|
||||
HPP_RAW_FNS(period, period)
|
||||
|
||||
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
|
||||
struct hist_entry *b __maybe_unused)
|
||||
@ -359,47 +364,50 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
#define HPP__COLOR_PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.color = hpp__color_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name, \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.color = hpp__color_ ## _fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _name, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
#define HPP__COLOR_ACC_PRINT_FNS(_name) \
|
||||
#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.color = hpp__color_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name, \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.color = hpp__color_ ## _fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _name, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
#define HPP__PRINT_FNS(_name) \
|
||||
#define HPP__PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name, \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _name, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
HPP__COLOR_PRINT_FNS(overhead),
|
||||
HPP__COLOR_PRINT_FNS(overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_us),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_us),
|
||||
HPP__COLOR_ACC_PRINT_FNS(overhead_acc),
|
||||
HPP__PRINT_FNS(samples),
|
||||
HPP__PRINT_FNS(period)
|
||||
HPP__COLOR_PRINT_FNS("Overhead", overhead),
|
||||
HPP__COLOR_PRINT_FNS("sys", overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS("usr", overhead_us),
|
||||
HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us),
|
||||
HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc),
|
||||
HPP__PRINT_FNS("Samples", samples),
|
||||
HPP__PRINT_FNS("Period", period)
|
||||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
@ -444,14 +452,12 @@ void perf_hpp__init(void)
|
||||
/*
|
||||
* If user specified field order, no need to setup default fields.
|
||||
*/
|
||||
if (field_order)
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].header =
|
||||
hpp__header_overhead_self;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
|
||||
}
|
||||
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
@ -513,11 +519,11 @@ void perf_hpp__column_disable(unsigned col)
|
||||
|
||||
void perf_hpp__cancel_cumulate(void)
|
||||
{
|
||||
if (field_order)
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead";
|
||||
}
|
||||
|
||||
void perf_hpp__setup_output_field(void)
|
||||
@ -622,3 +628,59 @@ unsigned int hists__sort_list_width(struct hists *hists)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt))
|
||||
return perf_hpp__reset_sort_width(fmt, hists);
|
||||
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
if (fmt == &perf_hpp__format[idx])
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == PERF_HPP__MAX_INDEX)
|
||||
return;
|
||||
|
||||
switch (idx) {
|
||||
case PERF_HPP__OVERHEAD:
|
||||
case PERF_HPP__OVERHEAD_SYS:
|
||||
case PERF_HPP__OVERHEAD_US:
|
||||
case PERF_HPP__OVERHEAD_ACC:
|
||||
fmt->len = 8;
|
||||
break;
|
||||
|
||||
case PERF_HPP__OVERHEAD_GUEST_SYS:
|
||||
case PERF_HPP__OVERHEAD_GUEST_US:
|
||||
fmt->len = 9;
|
||||
break;
|
||||
|
||||
case PERF_HPP__SAMPLES:
|
||||
case PERF_HPP__PERIOD:
|
||||
fmt->len = 12;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__set_user_width(const char *width_list_str)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
const char *ptr = width_list_str;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
char *p;
|
||||
|
||||
int len = strtol(ptr, &p, 10);
|
||||
fmt->user_len = len;
|
||||
|
||||
if (*p == ',')
|
||||
ptr = p + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -395,10 +395,12 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
|
||||
init_rem_hits();
|
||||
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
|
||||
if (symbol_conf.col_width_list_str)
|
||||
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
|
||||
|
||||
if (!show_header)
|
||||
goto print_entries;
|
||||
|
||||
|
@ -232,9 +232,16 @@ static int mov__parse(struct ins_operands *ops)
|
||||
return -1;
|
||||
|
||||
target = ++s;
|
||||
comment = strchr(s, '#');
|
||||
|
||||
while (s[0] != '\0' && !isspace(s[0]))
|
||||
++s;
|
||||
if (comment != NULL)
|
||||
s = comment - 1;
|
||||
else
|
||||
s = strchr(s, '\0') - 1;
|
||||
|
||||
while (s > target && isspace(s[0]))
|
||||
--s;
|
||||
s++;
|
||||
prev = *s;
|
||||
*s = '\0';
|
||||
|
||||
@ -244,7 +251,6 @@ static int mov__parse(struct ins_operands *ops)
|
||||
if (ops->target.raw == NULL)
|
||||
goto out_free_source;
|
||||
|
||||
comment = strchr(s, '#');
|
||||
if (comment == NULL)
|
||||
return 0;
|
||||
|
||||
@ -899,10 +905,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
|
||||
struct kcore_extract kce;
|
||||
bool delete_extract = false;
|
||||
|
||||
if (filename) {
|
||||
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
|
||||
symbol_conf.symfs, filename);
|
||||
}
|
||||
if (filename)
|
||||
symbol__join_symfs(symfs_filename, filename);
|
||||
|
||||
if (filename == NULL) {
|
||||
if (dso->has_build_id) {
|
||||
@ -922,8 +926,7 @@ fallback:
|
||||
* DSO is the same as when 'perf record' ran.
|
||||
*/
|
||||
filename = (char *)dso->long_name;
|
||||
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
|
||||
symbol_conf.symfs, filename);
|
||||
symbol__join_symfs(symfs_filename, filename);
|
||||
free_filename = false;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *);
|
||||
extern int perf_default_config(const char *, const char *, void *);
|
||||
extern int perf_config(config_fn_t fn, void *);
|
||||
extern int perf_config_int(const char *, const char *);
|
||||
extern u64 perf_config_u64(const char *, const char *);
|
||||
extern int perf_config_bool(const char *, const char *);
|
||||
extern int config_error_nonbool(const char *);
|
||||
extern const char *perf_config_dirname(const char *, const char *);
|
||||
|
@ -25,77 +25,172 @@
|
||||
|
||||
__thread struct callchain_cursor callchain_cursor;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
static int get_stack_size(const char *str, unsigned long *_size)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long size;
|
||||
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
|
||||
|
||||
size = strtoul(str, &endptr, 0);
|
||||
|
||||
do {
|
||||
if (*endptr)
|
||||
break;
|
||||
|
||||
size = round_up(size, sizeof(u64));
|
||||
if (!size || size > max_size)
|
||||
break;
|
||||
|
||||
*_size = size;
|
||||
return 0;
|
||||
|
||||
} while (0);
|
||||
|
||||
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
|
||||
max_size, str);
|
||||
return -1;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
|
||||
int parse_callchain_record_opt(const char *arg)
|
||||
{
|
||||
char *tok, *name, *saveptr = NULL;
|
||||
char *buf;
|
||||
int ret = -1;
|
||||
|
||||
/* We need buffer that we know we can write to. */
|
||||
buf = malloc(strlen(arg) + 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(buf, arg);
|
||||
|
||||
tok = strtok_r((char *)buf, ",", &saveptr);
|
||||
name = tok ? : (char *)buf;
|
||||
|
||||
do {
|
||||
/* Framepointer style */
|
||||
if (!strncmp(name, "fp", sizeof("fp"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for -g fp\n");
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
/* Dwarf style */
|
||||
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
callchain_param.record_mode = CALLCHAIN_DWARF;
|
||||
callchain_param.dump_size = default_stack_dump_size;
|
||||
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
if (tok) {
|
||||
unsigned long size = 0;
|
||||
|
||||
ret = get_stack_size(tok, &size);
|
||||
callchain_param.dump_size = size;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
} else {
|
||||
pr_err("callchain: Unknown --call-graph option "
|
||||
"value: %s\n", arg);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_callchain_mode(const char *value)
|
||||
{
|
||||
if (!strncmp(value, "graph", strlen(value))) {
|
||||
callchain_param.mode = CHAIN_GRAPH_ABS;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "flat", strlen(value))) {
|
||||
callchain_param.mode = CHAIN_FLAT;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "fractal", strlen(value))) {
|
||||
callchain_param.mode = CHAIN_GRAPH_REL;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_callchain_order(const char *value)
|
||||
{
|
||||
if (!strncmp(value, "caller", strlen(value))) {
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "callee", strlen(value))) {
|
||||
callchain_param.order = ORDER_CALLEE;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_callchain_sort_key(const char *value)
|
||||
{
|
||||
if (!strncmp(value, "function", strlen(value))) {
|
||||
callchain_param.key = CCKEY_FUNCTION;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "address", strlen(value))) {
|
||||
callchain_param.key = CCKEY_ADDRESS;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
parse_callchain_report_opt(const char *arg)
|
||||
{
|
||||
char *tok, *tok2;
|
||||
char *tok;
|
||||
char *endptr;
|
||||
bool minpcnt_set = false;
|
||||
|
||||
symbol_conf.use_callchain = true;
|
||||
|
||||
if (!arg)
|
||||
return 0;
|
||||
|
||||
tok = strtok((char *)arg, ",");
|
||||
if (!tok)
|
||||
return -1;
|
||||
while ((tok = strtok((char *)arg, ",")) != NULL) {
|
||||
if (!strncmp(tok, "none", strlen(tok))) {
|
||||
callchain_param.mode = CHAIN_NONE;
|
||||
symbol_conf.use_callchain = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the output mode */
|
||||
if (!strncmp(tok, "graph", strlen(arg))) {
|
||||
callchain_param.mode = CHAIN_GRAPH_ABS;
|
||||
if (!parse_callchain_mode(tok) ||
|
||||
!parse_callchain_order(tok) ||
|
||||
!parse_callchain_sort_key(tok)) {
|
||||
/* parsing ok - move on to the next */
|
||||
} else if (!minpcnt_set) {
|
||||
/* try to get the min percent */
|
||||
callchain_param.min_percent = strtod(tok, &endptr);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
minpcnt_set = true;
|
||||
} else {
|
||||
/* try print limit at last */
|
||||
callchain_param.print_limit = strtoul(tok, &endptr, 0);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else if (!strncmp(tok, "flat", strlen(arg))) {
|
||||
callchain_param.mode = CHAIN_FLAT;
|
||||
} else if (!strncmp(tok, "fractal", strlen(arg))) {
|
||||
callchain_param.mode = CHAIN_GRAPH_REL;
|
||||
} else if (!strncmp(tok, "none", strlen(arg))) {
|
||||
callchain_param.mode = CHAIN_NONE;
|
||||
symbol_conf.use_callchain = false;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
arg = NULL;
|
||||
}
|
||||
|
||||
/* get the min percentage */
|
||||
tok = strtok(NULL, ",");
|
||||
if (!tok)
|
||||
goto setup;
|
||||
|
||||
callchain_param.min_percent = strtod(tok, &endptr);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
|
||||
/* get the print limit */
|
||||
tok2 = strtok(NULL, ",");
|
||||
if (!tok2)
|
||||
goto setup;
|
||||
|
||||
if (tok2[0] != 'c') {
|
||||
callchain_param.print_limit = strtoul(tok2, &endptr, 0);
|
||||
tok2 = strtok(NULL, ",");
|
||||
if (!tok2)
|
||||
goto setup;
|
||||
}
|
||||
|
||||
/* get the call chain order */
|
||||
if (!strncmp(tok2, "caller", strlen("caller")))
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
else if (!strncmp(tok2, "callee", strlen("callee")))
|
||||
callchain_param.order = ORDER_CALLEE;
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* Get the sort key */
|
||||
tok2 = strtok(NULL, ",");
|
||||
if (!tok2)
|
||||
goto setup;
|
||||
if (!strncmp(tok2, "function", strlen("function")))
|
||||
callchain_param.key = CCKEY_FUNCTION;
|
||||
else if (!strncmp(tok2, "address", strlen("address")))
|
||||
callchain_param.key = CCKEY_ADDRESS;
|
||||
else
|
||||
return -1;
|
||||
setup:
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
pr_err("Can't register callchain params\n");
|
||||
return -1;
|
||||
@ -103,6 +198,47 @@ setup:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_callchain_config(const char *var, const char *value)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
if (prefixcmp(var, "call-graph."))
|
||||
return 0;
|
||||
var += sizeof("call-graph.") - 1;
|
||||
|
||||
if (!strcmp(var, "record-mode"))
|
||||
return parse_callchain_record_opt(value);
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
if (!strcmp(var, "dump-size")) {
|
||||
unsigned long size = 0;
|
||||
int ret;
|
||||
|
||||
ret = get_stack_size(value, &size);
|
||||
callchain_param.dump_size = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!strcmp(var, "print-type"))
|
||||
return parse_callchain_mode(value);
|
||||
if (!strcmp(var, "order"))
|
||||
return parse_callchain_order(value);
|
||||
if (!strcmp(var, "sort-key"))
|
||||
return parse_callchain_sort_key(value);
|
||||
if (!strcmp(var, "threshold")) {
|
||||
callchain_param.min_percent = strtod(value, &endptr);
|
||||
if (value == endptr)
|
||||
return -1;
|
||||
}
|
||||
if (!strcmp(var, "print-limit")) {
|
||||
callchain_param.print_limit = strtod(value, &endptr);
|
||||
if (value == endptr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
|
||||
enum chain_mode mode)
|
||||
|
@ -54,6 +54,9 @@ enum chain_key {
|
||||
};
|
||||
|
||||
struct callchain_param {
|
||||
bool enabled;
|
||||
enum perf_call_graph_mode record_mode;
|
||||
u32 dump_size;
|
||||
enum chain_mode mode;
|
||||
u32 print_limit;
|
||||
double min_percent;
|
||||
@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
|
||||
struct option;
|
||||
struct hist_entry;
|
||||
|
||||
int record_parse_callchain(const char *arg, struct record_opts *opts);
|
||||
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
int record_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
@ -166,7 +168,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
|
||||
bool hide_unresolved);
|
||||
|
||||
extern const char record_callchain_help[];
|
||||
int parse_callchain_record_opt(const char *arg);
|
||||
int parse_callchain_report_opt(const char *arg);
|
||||
int perf_callchain_config(const char *var, const char *value);
|
||||
|
||||
static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
|
||||
struct callchain_cursor *src)
|
||||
|
@ -1,7 +1,9 @@
|
||||
#include <sched.h>
|
||||
#include "util.h"
|
||||
#include "../perf.h"
|
||||
#include "cloexec.h"
|
||||
#include "asm/bug.h"
|
||||
#include "debug.h"
|
||||
|
||||
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
|
||||
|
||||
@ -9,15 +11,30 @@ 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,
|
||||
.type = PERF_TYPE_SOFTWARE,
|
||||
.config = PERF_COUNT_SW_CPU_CLOCK,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
int fd;
|
||||
int err;
|
||||
int cpu;
|
||||
pid_t pid = -1;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
/* check cloexec flag */
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1,
|
||||
PERF_FLAG_FD_CLOEXEC);
|
||||
cpu = sched_getcpu();
|
||||
if (cpu < 0)
|
||||
cpu = 0;
|
||||
|
||||
while (1) {
|
||||
/* check cloexec flag */
|
||||
fd = sys_perf_event_open(&attr, pid, cpu, -1,
|
||||
PERF_FLAG_FD_CLOEXEC);
|
||||
if (fd < 0 && pid == -1 && errno == EACCES) {
|
||||
pid = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
err = errno;
|
||||
|
||||
if (fd >= 0) {
|
||||
@ -25,17 +42,17 @@ static int perf_flag_probe(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
WARN_ONCE(err != EINVAL,
|
||||
WARN_ONCE(err != EINVAL && err != EBUSY,
|
||||
"perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
|
||||
err, strerror(err));
|
||||
err, strerror_r(err, sbuf, sizeof(sbuf)));
|
||||
|
||||
/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
|
||||
err = errno;
|
||||
|
||||
if (WARN_ONCE(fd < 0,
|
||||
if (WARN_ONCE(fd < 0 && err != EBUSY,
|
||||
"perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
|
||||
err, strerror(err)))
|
||||
err, strerror_r(err, sbuf, sizeof(sbuf))))
|
||||
return -1;
|
||||
|
||||
close(fd);
|
||||
|
@ -335,3 +335,19 @@ int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...)
|
||||
va_end(args);
|
||||
return value_color_snprintf(bf, size, fmt, percent);
|
||||
}
|
||||
|
||||
int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int len;
|
||||
double percent;
|
||||
const char *color;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
va_end(args);
|
||||
|
||||
color = get_percent_color(percent);
|
||||
return color_snprintf(bf, size, color, fmt, len, percent);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
|
||||
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
|
||||
int value_color_snprintf(char *bf, size_t size, const char *fmt, double value);
|
||||
int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...);
|
||||
int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...);
|
||||
int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
|
||||
const char *get_percent_color(double percent);
|
||||
|
||||
|
@ -74,7 +74,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
|
||||
return new;
|
||||
}
|
||||
|
||||
struct comm *comm__new(const char *str, u64 timestamp)
|
||||
struct comm *comm__new(const char *str, u64 timestamp, bool exec)
|
||||
{
|
||||
struct comm *comm = zalloc(sizeof(*comm));
|
||||
|
||||
@ -82,6 +82,7 @@ struct comm *comm__new(const char *str, u64 timestamp)
|
||||
return NULL;
|
||||
|
||||
comm->start = timestamp;
|
||||
comm->exec = exec;
|
||||
|
||||
comm->comm_str = comm_str__findnew(str, &comm_str_root);
|
||||
if (!comm->comm_str) {
|
||||
@ -94,7 +95,7 @@ struct comm *comm__new(const char *str, u64 timestamp)
|
||||
return comm;
|
||||
}
|
||||
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp)
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
|
||||
{
|
||||
struct comm_str *new, *old = comm->comm_str;
|
||||
|
||||
@ -106,6 +107,8 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp)
|
||||
comm_str__put(old);
|
||||
comm->comm_str = new;
|
||||
comm->start = timestamp;
|
||||
if (exec)
|
||||
comm->exec = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -11,11 +11,13 @@ struct comm {
|
||||
struct comm_str *comm_str;
|
||||
u64 start;
|
||||
struct list_head list;
|
||||
bool exec;
|
||||
};
|
||||
|
||||
void comm__free(struct comm *comm);
|
||||
struct comm *comm__new(const char *str, u64 timestamp);
|
||||
struct comm *comm__new(const char *str, u64 timestamp, bool exec);
|
||||
const char *comm__str(const struct comm *comm);
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp);
|
||||
int comm__override(struct comm *comm, const char *str, u64 timestamp,
|
||||
bool exec);
|
||||
|
||||
#endif /* __PERF_COMM_H */
|
||||
|
@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data)
|
||||
const unsigned char *bomptr = utf8_bom;
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
int line, c = get_next_char();
|
||||
|
||||
if (bomptr && *bomptr) {
|
||||
/* We are at the file beginning; skip UTF8-encoded BOM
|
||||
* if present. Sane editors won't put this in on their
|
||||
@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data)
|
||||
if (!isalpha(c))
|
||||
break;
|
||||
var[baselen] = tolower(c);
|
||||
if (get_value(fn, data, var, baselen+1) < 0)
|
||||
|
||||
/*
|
||||
* The get_value function might or might not reach the '\n',
|
||||
* so saving the current line number for error reporting.
|
||||
*/
|
||||
line = config_linenr;
|
||||
if (get_value(fn, data, var, baselen+1) < 0) {
|
||||
config_linenr = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
die("bad config file line %d in %s", config_linenr, config_file_name);
|
||||
}
|
||||
@ -286,6 +295,21 @@ static int parse_unit_factor(const char *end, unsigned long *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_parse_llong(const char *value, long long *ret)
|
||||
{
|
||||
if (value && *value) {
|
||||
char *end;
|
||||
long long val = strtoll(value, &end, 0);
|
||||
unsigned long factor = 1;
|
||||
|
||||
if (!parse_unit_factor(end, &factor))
|
||||
return 0;
|
||||
*ret = val * factor;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_parse_long(const char *value, long *ret)
|
||||
{
|
||||
if (value && *value) {
|
||||
@ -307,6 +331,15 @@ static void die_bad_config(const char *name)
|
||||
die("bad config value for '%s'", name);
|
||||
}
|
||||
|
||||
u64 perf_config_u64(const char *name, const char *value)
|
||||
{
|
||||
long long ret = 0;
|
||||
|
||||
if (!perf_parse_llong(value, &ret))
|
||||
die_bad_config(name);
|
||||
return (u64) ret;
|
||||
}
|
||||
|
||||
int perf_config_int(const char *name, const char *value)
|
||||
{
|
||||
long ret = 0;
|
||||
@ -372,6 +405,9 @@ int perf_default_config(const char *var, const char *value,
|
||||
if (!prefixcmp(var, "ui."))
|
||||
return perf_ui_config(var, value);
|
||||
|
||||
if (!prefixcmp(var, "call-graph."))
|
||||
return perf_callchain_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
|
@ -50,12 +50,14 @@ static int open_file_read(struct perf_data_file *file)
|
||||
{
|
||||
struct stat st;
|
||||
int fd;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
fd = open(file->path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
int err = errno;
|
||||
|
||||
pr_err("failed to open %s: %s", file->path, strerror(err));
|
||||
pr_err("failed to open %s: %s", file->path,
|
||||
strerror_r(err, sbuf, sizeof(sbuf)));
|
||||
if (err == ENOENT && !strcmp(file->path, "perf.data"))
|
||||
pr_err(" (try 'perf record' first)");
|
||||
pr_err("\n");
|
||||
@ -88,6 +90,7 @@ static int open_file_read(struct perf_data_file *file)
|
||||
static int open_file_write(struct perf_data_file *file)
|
||||
{
|
||||
int fd;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (check_backup(file))
|
||||
return -1;
|
||||
@ -95,7 +98,8 @@ static int open_file_write(struct perf_data_file *file)
|
||||
fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
|
||||
|
||||
if (fd < 0)
|
||||
pr_err("failed to open %s : %s\n", file->path, strerror(errno));
|
||||
pr_err("failed to open %s : %s\n", file->path,
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
@ -13,8 +13,12 @@
|
||||
#include "util.h"
|
||||
#include "target.h"
|
||||
|
||||
#define NSECS_PER_SEC 1000000000ULL
|
||||
#define NSECS_PER_USEC 1000ULL
|
||||
|
||||
int verbose;
|
||||
bool dump_trace = false, quiet = false;
|
||||
int debug_ordered_events;
|
||||
|
||||
static int _eprintf(int level, int var, const char *fmt, va_list args)
|
||||
{
|
||||
@ -42,6 +46,35 @@ int eprintf(int level, int var, const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __eprintf_time(u64 t, const char *fmt, va_list args)
|
||||
{
|
||||
int ret = 0;
|
||||
u64 secs, usecs, nsecs = t;
|
||||
|
||||
secs = nsecs / NSECS_PER_SEC;
|
||||
nsecs -= secs * NSECS_PER_SEC;
|
||||
usecs = nsecs / NSECS_PER_USEC;
|
||||
|
||||
ret = fprintf(stderr, "[%13" PRIu64 ".%06" PRIu64 "] ",
|
||||
secs, usecs);
|
||||
ret += vfprintf(stderr, fmt, args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int eprintf_time(int level, int var, u64 t, const char *fmt, ...)
|
||||
{
|
||||
int ret = 0;
|
||||
va_list args;
|
||||
|
||||
if (var >= level) {
|
||||
va_start(args, fmt);
|
||||
ret = __eprintf_time(t, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overloading libtraceevent standard info print
|
||||
* function, display with -v in perf.
|
||||
@ -110,7 +143,8 @@ static struct debug_variable {
|
||||
const char *name;
|
||||
int *ptr;
|
||||
} debug_variables[] = {
|
||||
{ .name = "verbose", .ptr = &verbose },
|
||||
{ .name = "verbose", .ptr = &verbose },
|
||||
{ .name = "ordered-events", .ptr = &debug_ordered_events},
|
||||
{ .name = NULL, }
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define __PERF_DEBUG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "event.h"
|
||||
#include "../ui/helpline.h"
|
||||
#include "../ui/progress.h"
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
extern int verbose;
|
||||
extern bool quiet, dump_trace;
|
||||
extern int debug_ordered_events;
|
||||
|
||||
#ifndef pr_fmt
|
||||
#define pr_fmt(fmt) fmt
|
||||
@ -29,6 +31,14 @@ extern bool quiet, dump_trace;
|
||||
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
|
||||
#define pr_time_N(n, var, t, fmt, ...) \
|
||||
eprintf_time(n, var, t, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define pr_oe_time(t, fmt, ...) pr_time_N(1, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_oe_time2(t, fmt, ...) pr_time_N(2, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
|
||||
#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */
|
||||
|
||||
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void trace_event(union perf_event *event);
|
||||
|
||||
@ -38,6 +48,7 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void pr_stat(const char *fmt, ...);
|
||||
|
||||
int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
|
||||
|
||||
int perf_debug_option(const char *str);
|
||||
|
||||
|
@ -37,6 +37,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
||||
{
|
||||
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
|
||||
switch (type) {
|
||||
case DSO_BINARY_TYPE__DEBUGLINK: {
|
||||
@ -60,26 +61,25 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
|
||||
snprintf(filename, size, "%s/usr/lib/debug%s.debug",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
|
||||
snprintf(filename + len, size - len, "%s.debug", dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
|
||||
snprintf(filename, size, "%s/usr/lib/debug%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
|
||||
snprintf(filename + len, size - len, "%s", dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
|
||||
{
|
||||
const char *last_slash;
|
||||
size_t len;
|
||||
size_t dir_size;
|
||||
|
||||
last_slash = dso->long_name + dso->long_name_len;
|
||||
while (last_slash != dso->long_name && *last_slash != '/')
|
||||
last_slash--;
|
||||
|
||||
len = scnprintf(filename, size, "%s", symbol_conf.symfs);
|
||||
len = __symbol__join_symfs(filename, size, "");
|
||||
dir_size = last_slash - dso->long_name + 2;
|
||||
if (dir_size > (size - len)) {
|
||||
ret = -1;
|
||||
@ -100,26 +100,24 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
||||
build_id__sprintf(dso->build_id,
|
||||
sizeof(dso->build_id),
|
||||
build_id_hex);
|
||||
snprintf(filename, size,
|
||||
"%s/usr/lib/debug/.build-id/%.2s/%s.debug",
|
||||
symbol_conf.symfs, build_id_hex, build_id_hex + 2);
|
||||
len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/");
|
||||
snprintf(filename + len, size - len, "%.2s/%s.debug",
|
||||
build_id_hex, build_id_hex + 2);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__VMLINUX:
|
||||
case DSO_BINARY_TYPE__GUEST_VMLINUX:
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
|
||||
snprintf(filename, size, "%s%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
__symbol__join_symfs(filename, size, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__GUEST_KMODULE:
|
||||
snprintf(filename, size, "%s%s%s", symbol_conf.symfs,
|
||||
root_dir, dso->long_name);
|
||||
path__join3(filename, size, symbol_conf.symfs,
|
||||
root_dir, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
|
||||
snprintf(filename, size, "%s%s", symbol_conf.symfs,
|
||||
dso->long_name);
|
||||
__symbol__join_symfs(filename, size, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__KCORE:
|
||||
@ -164,13 +162,15 @@ static void close_first_dso(void);
|
||||
static int do_open(char *name)
|
||||
{
|
||||
int fd;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
do {
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
pr_debug("dso open failed, mmap: %s\n", strerror(errno));
|
||||
pr_debug("dso open failed, mmap: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
if (!dso__data_open_cnt || errno != EMFILE)
|
||||
break;
|
||||
|
||||
@ -532,10 +532,12 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
|
||||
static int data_file_size(struct dso *dso)
|
||||
{
|
||||
struct stat st;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (!dso->data.file_size) {
|
||||
if (fstat(dso->data.fd, &st)) {
|
||||
pr_err("dso mmap failed, fstat: %s\n", strerror(errno));
|
||||
pr_err("dso mmap failed, fstat: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return -1;
|
||||
}
|
||||
dso->data.file_size = st.st_size;
|
||||
@ -651,6 +653,65 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
|
||||
return dso;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a matching entry and/or link current entry to RB tree.
|
||||
* Either one of the dso or name parameter must be non-NULL or the
|
||||
* function will not work.
|
||||
*/
|
||||
static struct dso *dso__findlink_by_longname(struct rb_root *root,
|
||||
struct dso *dso, const char *name)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
if (!name)
|
||||
name = dso->long_name;
|
||||
/*
|
||||
* Find node with the matching name
|
||||
*/
|
||||
while (*p) {
|
||||
struct dso *this = rb_entry(*p, struct dso, rb_node);
|
||||
int rc = strcmp(name, this->long_name);
|
||||
|
||||
parent = *p;
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* In case the new DSO is a duplicate of an existing
|
||||
* one, print an one-time warning & put the new entry
|
||||
* at the end of the list of duplicates.
|
||||
*/
|
||||
if (!dso || (dso == this))
|
||||
return this; /* Find matching dso */
|
||||
/*
|
||||
* The core kernel DSOs may have duplicated long name.
|
||||
* In this case, the short name should be different.
|
||||
* Comparing the short names to differentiate the DSOs.
|
||||
*/
|
||||
rc = strcmp(dso->short_name, this->short_name);
|
||||
if (rc == 0) {
|
||||
pr_err("Duplicated dso name: %s\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (rc < 0)
|
||||
p = &parent->rb_left;
|
||||
else
|
||||
p = &parent->rb_right;
|
||||
}
|
||||
if (dso) {
|
||||
/* Add new node and rebalance tree */
|
||||
rb_link_node(&dso->rb_node, parent, p);
|
||||
rb_insert_color(&dso->rb_node, root);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct dso *
|
||||
dso__find_by_longname(const struct rb_root *root, const char *name)
|
||||
{
|
||||
return dso__findlink_by_longname((struct rb_root *)root, NULL, name);
|
||||
}
|
||||
|
||||
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
|
||||
{
|
||||
if (name == NULL)
|
||||
@ -753,6 +814,7 @@ struct dso *dso__new(const char *name)
|
||||
dso->a2l_fails = 1;
|
||||
dso->kernel = DSO_TYPE_USER;
|
||||
dso->needs_swap = DSO_SWAP__UNSET;
|
||||
RB_CLEAR_NODE(&dso->rb_node);
|
||||
INIT_LIST_HEAD(&dso->node);
|
||||
INIT_LIST_HEAD(&dso->data.open_entry);
|
||||
}
|
||||
@ -763,6 +825,10 @@ struct dso *dso__new(const char *name)
|
||||
void dso__delete(struct dso *dso)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!RB_EMPTY_NODE(&dso->rb_node))
|
||||
pr_err("DSO %s is still in rbtree when being deleted!\n",
|
||||
dso->long_name);
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
symbols__delete(&dso->symbols[i]);
|
||||
|
||||
@ -849,35 +915,34 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
|
||||
return have_build_id;
|
||||
}
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso)
|
||||
void dsos__add(struct dsos *dsos, struct dso *dso)
|
||||
{
|
||||
list_add_tail(&dso->node, head);
|
||||
list_add_tail(&dso->node, &dsos->head);
|
||||
dso__findlink_by_longname(&dsos->root, dso, NULL);
|
||||
}
|
||||
|
||||
struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short)
|
||||
struct dso *dsos__find(const struct dsos *dsos, const char *name,
|
||||
bool cmp_short)
|
||||
{
|
||||
struct dso *pos;
|
||||
|
||||
if (cmp_short) {
|
||||
list_for_each_entry(pos, head, node)
|
||||
list_for_each_entry(pos, &dsos->head, node)
|
||||
if (strcmp(pos->short_name, name) == 0)
|
||||
return pos;
|
||||
return NULL;
|
||||
}
|
||||
list_for_each_entry(pos, head, node)
|
||||
if (strcmp(pos->long_name, name) == 0)
|
||||
return pos;
|
||||
return NULL;
|
||||
return dso__find_by_longname(&dsos->root, name);
|
||||
}
|
||||
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name)
|
||||
struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
|
||||
{
|
||||
struct dso *dso = dsos__find(head, name, false);
|
||||
struct dso *dso = dsos__find(dsos, name, false);
|
||||
|
||||
if (!dso) {
|
||||
dso = dso__new(name);
|
||||
if (dso != NULL) {
|
||||
dsos__add(head, dso);
|
||||
dsos__add(dsos, dso);
|
||||
dso__set_basename(dso);
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +90,18 @@ struct dso_cache {
|
||||
char data[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* DSOs are put into both a list for fast iteration and rbtree for fast
|
||||
* long name lookup.
|
||||
*/
|
||||
struct dsos {
|
||||
struct list_head head;
|
||||
struct rb_root root; /* rbtree root sorted by long name */
|
||||
};
|
||||
|
||||
struct dso {
|
||||
struct list_head node;
|
||||
struct rb_node rb_node; /* rbtree node sorted by long name */
|
||||
struct rb_root symbols[MAP__NR_TYPES];
|
||||
struct rb_root symbol_names[MAP__NR_TYPES];
|
||||
void *a2l;
|
||||
@ -224,10 +234,10 @@ struct map *dso__new_map(const char *name);
|
||||
struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
|
||||
const char *short_name, int dso_type);
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso);
|
||||
struct dso *dsos__find(const struct list_head *head, const char *name,
|
||||
void dsos__add(struct dsos *dsos, struct dso *dso);
|
||||
struct dso *dsos__find(const struct dsos *dsos, const char *name,
|
||||
bool cmp_short);
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name);
|
||||
struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
|
||||
|
||||
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
|
@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
struct map *map;
|
||||
struct kmap *kmap;
|
||||
int err;
|
||||
union perf_event *event;
|
||||
|
||||
if (machine->vmlinux_maps[0] == NULL)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We should get this from /sys/kernel/sections/.text, but till that is
|
||||
* available use this, and after it is use this as a fallback for older
|
||||
* kernels.
|
||||
*/
|
||||
union perf_event *event = zalloc((sizeof(event->mmap) +
|
||||
machine->id_hdr_size));
|
||||
event = zalloc((sizeof(event->mmap) + machine->id_hdr_size));
|
||||
if (event == NULL) {
|
||||
pr_debug("Not enough memory synthesizing mmap event "
|
||||
"for kernel modules\n");
|
||||
@ -784,9 +788,9 @@ try_again:
|
||||
* "[vdso]" dso, but for now lets use the old trick of looking
|
||||
* in the whole kernel symbol list.
|
||||
*/
|
||||
if ((long long)al->addr < 0 &&
|
||||
cpumode == PERF_RECORD_MISC_USER &&
|
||||
machine && mg != &machine->kmaps) {
|
||||
if (cpumode == PERF_RECORD_MISC_USER && machine &&
|
||||
mg != &machine->kmaps &&
|
||||
machine__kernel_ip(machine, al->addr)) {
|
||||
mg = &machine->kmaps;
|
||||
load_map = true;
|
||||
goto try_again;
|
||||
|
@ -156,6 +156,8 @@ struct perf_sample {
|
||||
u32 cpu;
|
||||
u32 raw_size;
|
||||
u64 data_src;
|
||||
u32 flags;
|
||||
u16 insn_len;
|
||||
void *raw_data;
|
||||
struct ip_callchain *callchain;
|
||||
struct branch_stack *branch_stack;
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/hash.h>
|
||||
|
||||
static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
|
||||
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
|
||||
|
||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||
#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
|
||||
|
||||
@ -37,6 +40,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
|
||||
INIT_HLIST_HEAD(&evlist->heads[i]);
|
||||
INIT_LIST_HEAD(&evlist->entries);
|
||||
perf_evlist__set_maps(evlist, cpus, threads);
|
||||
fdarray__init(&evlist->pollfd, 64);
|
||||
evlist->workload.pid = -1;
|
||||
}
|
||||
|
||||
@ -102,7 +106,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)
|
||||
void perf_evlist__exit(struct perf_evlist *evlist)
|
||||
{
|
||||
zfree(&evlist->mmap);
|
||||
zfree(&evlist->pollfd);
|
||||
fdarray__exit(&evlist->pollfd);
|
||||
}
|
||||
|
||||
void perf_evlist__delete(struct perf_evlist *evlist)
|
||||
@ -122,6 +126,7 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
|
||||
{
|
||||
list_add_tail(&entry->node, &evlist->entries);
|
||||
entry->idx = evlist->nr_entries;
|
||||
entry->tracking = !entry->idx;
|
||||
|
||||
if (!evlist->nr_entries++)
|
||||
perf_evlist__set_id_pos(evlist);
|
||||
@ -265,17 +270,27 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_evlist__nr_threads(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
if (evsel->system_wide)
|
||||
return 1;
|
||||
else
|
||||
return thread_map__nr(evlist->threads);
|
||||
}
|
||||
|
||||
void perf_evlist__disable(struct perf_evlist *evlist)
|
||||
{
|
||||
int cpu, thread;
|
||||
struct perf_evsel *pos;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
int nr_threads;
|
||||
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
evlist__for_each(evlist, pos) {
|
||||
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
|
||||
continue;
|
||||
nr_threads = perf_evlist__nr_threads(evlist, pos);
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_DISABLE, 0);
|
||||
@ -288,12 +303,13 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
||||
int cpu, thread;
|
||||
struct perf_evsel *pos;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
int nr_threads;
|
||||
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
evlist__for_each(evlist, pos) {
|
||||
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
|
||||
continue;
|
||||
nr_threads = perf_evlist__nr_threads(evlist, pos);
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
@ -305,12 +321,14 @@ int perf_evlist__disable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int cpu, thread, err;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = perf_evlist__nr_threads(evlist, evsel);
|
||||
|
||||
if (!evsel->fd)
|
||||
return 0;
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
err = ioctl(FD(evsel, cpu, thread),
|
||||
PERF_EVENT_IOC_DISABLE, 0);
|
||||
if (err)
|
||||
@ -324,12 +342,14 @@ int perf_evlist__enable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int cpu, thread, err;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = perf_evlist__nr_threads(evlist, evsel);
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
err = ioctl(FD(evsel, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (err)
|
||||
@ -339,21 +359,111 @@ int perf_evlist__enable_event(struct perf_evlist *evlist,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel, int cpu)
|
||||
{
|
||||
int thread, err;
|
||||
int nr_threads = perf_evlist__nr_threads(evlist, evsel);
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
err = ioctl(FD(evsel, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel,
|
||||
int thread)
|
||||
{
|
||||
int cpu, err;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel, int idx)
|
||||
{
|
||||
bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus);
|
||||
|
||||
if (per_cpu_mmaps)
|
||||
return perf_evlist__enable_event_cpu(evlist, evsel, idx);
|
||||
else
|
||||
return perf_evlist__enable_event_thread(evlist, evsel, idx);
|
||||
}
|
||||
|
||||
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
{
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
int nfds = nr_cpus * nr_threads * evlist->nr_entries;
|
||||
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
|
||||
return evlist->pollfd != NULL ? 0 : -ENOMEM;
|
||||
int nfds = 0;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (evsel->system_wide)
|
||||
nfds += nr_cpus;
|
||||
else
|
||||
nfds += nr_cpus * nr_threads;
|
||||
}
|
||||
|
||||
if (fdarray__available_entries(&evlist->pollfd) < nfds &&
|
||||
fdarray__grow(&evlist->pollfd, nfds) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
|
||||
static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx)
|
||||
{
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
evlist->pollfd[evlist->nr_fds].fd = fd;
|
||||
evlist->pollfd[evlist->nr_fds].events = POLLIN;
|
||||
evlist->nr_fds++;
|
||||
int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP);
|
||||
/*
|
||||
* Save the idx so that when we filter out fds POLLHUP'ed we can
|
||||
* close the associated evlist->mmap[] entry.
|
||||
*/
|
||||
if (pos >= 0) {
|
||||
evlist->pollfd.priv[pos].idx = idx;
|
||||
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
|
||||
{
|
||||
return __perf_evlist__add_pollfd(evlist, fd, -1);
|
||||
}
|
||||
|
||||
static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd)
|
||||
{
|
||||
struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd);
|
||||
|
||||
perf_evlist__mmap_put(evlist, fda->priv[fd].idx);
|
||||
}
|
||||
|
||||
int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
|
||||
{
|
||||
return fdarray__filter(&evlist->pollfd, revents_and_mask,
|
||||
perf_evlist__munmap_filtered);
|
||||
}
|
||||
|
||||
int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
|
||||
{
|
||||
return fdarray__poll(&evlist->pollfd, timeout);
|
||||
}
|
||||
|
||||
static void perf_evlist__id_hash(struct perf_evlist *evlist,
|
||||
@ -566,14 +676,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
||||
return event;
|
||||
}
|
||||
|
||||
static bool perf_mmap__empty(struct perf_mmap *md)
|
||||
{
|
||||
return perf_mmap__read_head(md) != md->prev;
|
||||
}
|
||||
|
||||
static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
++evlist->mmap[idx].refcnt;
|
||||
}
|
||||
|
||||
static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
BUG_ON(evlist->mmap[idx].refcnt == 0);
|
||||
|
||||
if (--evlist->mmap[idx].refcnt == 0)
|
||||
__perf_evlist__munmap(evlist, idx);
|
||||
}
|
||||
|
||||
void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
struct perf_mmap *md = &evlist->mmap[idx];
|
||||
|
||||
if (!evlist->overwrite) {
|
||||
struct perf_mmap *md = &evlist->mmap[idx];
|
||||
unsigned int old = md->prev;
|
||||
|
||||
perf_mmap__write_tail(md, old);
|
||||
}
|
||||
|
||||
if (md->refcnt == 1 && perf_mmap__empty(md))
|
||||
perf_evlist__mmap_put(evlist, idx);
|
||||
}
|
||||
|
||||
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
|
||||
@ -581,6 +713,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
|
||||
if (evlist->mmap[idx].base != NULL) {
|
||||
munmap(evlist->mmap[idx].base, evlist->mmap_len);
|
||||
evlist->mmap[idx].base = NULL;
|
||||
evlist->mmap[idx].refcnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,6 +747,20 @@ struct mmap_params {
|
||||
static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
|
||||
struct mmap_params *mp, int fd)
|
||||
{
|
||||
/*
|
||||
* The last one will be done at perf_evlist__mmap_consume(), so that we
|
||||
* make sure we don't prevent tools from consuming every last event in
|
||||
* the ring buffer.
|
||||
*
|
||||
* I.e. we can get the POLLHUP meaning that the fd doesn't exist
|
||||
* anymore, but the last events for it are still in the ring buffer,
|
||||
* waiting to be consumed.
|
||||
*
|
||||
* Tools can chose to ignore this at their own discretion, but the
|
||||
* evlist layer can't just drop it when filtering events in
|
||||
* perf_evlist__filter_pollfd().
|
||||
*/
|
||||
evlist->mmap[idx].refcnt = 2;
|
||||
evlist->mmap[idx].prev = 0;
|
||||
evlist->mmap[idx].mask = mp->mask;
|
||||
evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot,
|
||||
@ -625,7 +772,6 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_evlist__add_pollfd(evlist, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -636,7 +782,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
evlist__for_each(evlist, evsel) {
|
||||
int fd = FD(evsel, cpu, thread);
|
||||
int fd;
|
||||
|
||||
if (evsel->system_wide && thread)
|
||||
continue;
|
||||
|
||||
fd = FD(evsel, cpu, thread);
|
||||
|
||||
if (*output == -1) {
|
||||
*output = fd;
|
||||
@ -645,6 +796,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
|
||||
} else {
|
||||
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
|
||||
return -1;
|
||||
|
||||
perf_evlist__mmap_get(evlist, idx);
|
||||
}
|
||||
|
||||
if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) {
|
||||
perf_evlist__mmap_put(evlist, idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
|
||||
@ -804,7 +962,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||
if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
|
||||
if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
evlist->overwrite = overwrite;
|
||||
@ -1061,6 +1219,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
|
||||
}
|
||||
|
||||
if (!evlist->workload.pid) {
|
||||
int ret;
|
||||
|
||||
if (pipe_output)
|
||||
dup2(2, 1);
|
||||
|
||||
@ -1078,8 +1238,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
|
||||
/*
|
||||
* Wait until the parent tells us to go.
|
||||
*/
|
||||
if (read(go_pipe[0], &bf, 1) == -1)
|
||||
perror("unable to read pipe");
|
||||
ret = read(go_pipe[0], &bf, 1);
|
||||
/*
|
||||
* The parent will ask for the execvp() to be performed by
|
||||
* writing exactly one byte, in workload.cork_fd, usually via
|
||||
* perf_evlist__start_workload().
|
||||
*
|
||||
* For cancelling the workload without actuallin running it,
|
||||
* the parent will just close workload.cork_fd, without writing
|
||||
* anything, i.e. read will return zero and we just exit()
|
||||
* here.
|
||||
*/
|
||||
if (ret != 1) {
|
||||
if (ret == -1)
|
||||
perror("unable to read pipe");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
execvp(argv[0], (char **)argv);
|
||||
|
||||
@ -1202,7 +1376,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
|
||||
int err, char *buf, size_t size)
|
||||
{
|
||||
int printed, value;
|
||||
char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf));
|
||||
char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf));
|
||||
|
||||
switch (err) {
|
||||
case EACCES:
|
||||
@ -1250,3 +1424,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
|
||||
|
||||
list_splice(&move, &evlist->entries);
|
||||
}
|
||||
|
||||
void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *tracking_evsel)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
if (tracking_evsel->tracking)
|
||||
return;
|
||||
|
||||
evlist__for_each(evlist, evsel) {
|
||||
if (evsel != tracking_evsel)
|
||||
evsel->tracking = false;
|
||||
}
|
||||
|
||||
tracking_evsel->tracking = true;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __PERF_EVLIST_H 1
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <api/fd/array.h>
|
||||
#include <stdio.h>
|
||||
#include "../perf.h"
|
||||
#include "event.h"
|
||||
@ -17,9 +18,15 @@ struct record_opts;
|
||||
#define PERF_EVLIST__HLIST_BITS 8
|
||||
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
|
||||
|
||||
/**
|
||||
* struct perf_mmap - perf's ring buffer mmap details
|
||||
*
|
||||
* @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this
|
||||
*/
|
||||
struct perf_mmap {
|
||||
void *base;
|
||||
int mask;
|
||||
int refcnt;
|
||||
unsigned int prev;
|
||||
char event_copy[PERF_SAMPLE_MAX_SIZE];
|
||||
};
|
||||
@ -29,7 +36,6 @@ struct perf_evlist {
|
||||
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
|
||||
int nr_entries;
|
||||
int nr_groups;
|
||||
int nr_fds;
|
||||
int nr_mmaps;
|
||||
size_t mmap_len;
|
||||
int id_pos;
|
||||
@ -40,8 +46,8 @@ struct perf_evlist {
|
||||
pid_t pid;
|
||||
} workload;
|
||||
bool overwrite;
|
||||
struct fdarray pollfd;
|
||||
struct perf_mmap *mmap;
|
||||
struct pollfd *pollfd;
|
||||
struct thread_map *threads;
|
||||
struct cpu_map *cpus;
|
||||
struct perf_evsel *selected;
|
||||
@ -82,7 +88,11 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
|
||||
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
|
||||
int cpu, int thread, u64 id);
|
||||
|
||||
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
|
||||
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
|
||||
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
|
||||
int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask);
|
||||
|
||||
int perf_evlist__poll(struct perf_evlist *evlist, int timeout);
|
||||
|
||||
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
|
||||
|
||||
@ -122,6 +132,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
int perf_evlist__enable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel, int idx);
|
||||
|
||||
void perf_evlist__set_selected(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
@ -262,4 +274,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
|
||||
#define evlist__for_each_safe(evlist, tmp, evsel) \
|
||||
__evlist__for_each_safe(&(evlist)->entries, tmp, evsel)
|
||||
|
||||
void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *tracking_evsel);
|
||||
|
||||
#endif /* __PERF_EVLIST_H */
|
||||
|
@ -162,6 +162,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
|
||||
struct perf_event_attr *attr, int idx)
|
||||
{
|
||||
evsel->idx = idx;
|
||||
evsel->tracking = !idx;
|
||||
evsel->attr = *attr;
|
||||
evsel->leader = evsel;
|
||||
evsel->unit = "";
|
||||
@ -502,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
|
||||
}
|
||||
|
||||
static void
|
||||
perf_evsel__config_callgraph(struct perf_evsel *evsel,
|
||||
struct record_opts *opts)
|
||||
perf_evsel__config_callgraph(struct perf_evsel *evsel)
|
||||
{
|
||||
bool function = perf_evsel__is_function_event(evsel);
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
|
||||
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
|
||||
|
||||
if (opts->call_graph == CALLCHAIN_DWARF) {
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF) {
|
||||
if (!function) {
|
||||
perf_evsel__set_sample_bit(evsel, REGS_USER);
|
||||
perf_evsel__set_sample_bit(evsel, STACK_USER);
|
||||
attr->sample_regs_user = PERF_REGS_MASK;
|
||||
attr->sample_stack_user = opts->stack_dump_size;
|
||||
attr->sample_stack_user = callchain_param.dump_size;
|
||||
attr->exclude_callchain_user = 1;
|
||||
} else {
|
||||
pr_info("Cannot use DWARF unwind for function trace event,"
|
||||
@ -561,7 +561,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *leader = evsel->leader;
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
int track = evsel->tracking;
|
||||
bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;
|
||||
|
||||
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
|
||||
@ -624,8 +624,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
|
||||
attr->mmap_data = track;
|
||||
}
|
||||
|
||||
if (opts->call_graph_enabled && !evsel->no_aux_samples)
|
||||
perf_evsel__config_callgraph(evsel, opts);
|
||||
if (callchain_param.enabled && !evsel->no_aux_samples)
|
||||
perf_evsel__config_callgraph(evsel);
|
||||
|
||||
if (target__has_cpu(&opts->target))
|
||||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
@ -633,9 +633,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
|
||||
if (opts->period)
|
||||
perf_evsel__set_sample_bit(evsel, PERIOD);
|
||||
|
||||
if (!perf_missing_features.sample_id_all &&
|
||||
(opts->sample_time || !opts->no_inherit ||
|
||||
target__has_cpu(&opts->target) || per_cpu))
|
||||
/*
|
||||
* When the user explicitely disabled time don't force it here.
|
||||
*/
|
||||
if (opts->sample_time &&
|
||||
(!perf_missing_features.sample_id_all &&
|
||||
(!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu)))
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
|
||||
if (opts->raw_samples && !evsel->no_aux_samples) {
|
||||
@ -692,6 +695,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
int cpu, thread;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
|
||||
|
||||
if (evsel->fd) {
|
||||
@ -710,6 +717,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea
|
||||
{
|
||||
int cpu, thread;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||
for (thread = 0; thread < nthreads; thread++) {
|
||||
int fd = FD(evsel, cpu, thread),
|
||||
@ -740,6 +750,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
|
||||
if (evsel->sample_id == NULL)
|
||||
return -ENOMEM;
|
||||
@ -784,6 +797,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
int cpu, thread;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
for (cpu = 0; cpu < ncpus; cpu++)
|
||||
for (thread = 0; thread < nthreads; ++thread) {
|
||||
close(FD(evsel, cpu, thread));
|
||||
@ -872,6 +888,9 @@ int __perf_evsel__read(struct perf_evsel *evsel,
|
||||
int cpu, thread;
|
||||
struct perf_counts_values *aggr = &evsel->counts->aggr, count;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
aggr->val = aggr->ena = aggr->run = 0;
|
||||
|
||||
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||
@ -994,13 +1013,18 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)
|
||||
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
struct thread_map *threads)
|
||||
{
|
||||
int cpu, thread;
|
||||
int cpu, thread, nthreads;
|
||||
unsigned long flags = PERF_FLAG_FD_CLOEXEC;
|
||||
int pid = -1, err;
|
||||
enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
else
|
||||
nthreads = threads->nr;
|
||||
|
||||
if (evsel->fd == NULL &&
|
||||
perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
|
||||
perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (evsel->cgrp) {
|
||||
@ -1024,10 +1048,10 @@ retry_sample_id:
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; cpu++) {
|
||||
|
||||
for (thread = 0; thread < threads->nr; thread++) {
|
||||
for (thread = 0; thread < nthreads; thread++) {
|
||||
int group_fd;
|
||||
|
||||
if (!evsel->cgrp)
|
||||
if (!evsel->cgrp && !evsel->system_wide)
|
||||
pid = threads->map[thread];
|
||||
|
||||
group_fd = get_group_fd(evsel, cpu, thread);
|
||||
@ -1100,7 +1124,7 @@ out_close:
|
||||
close(FD(evsel, cpu, thread));
|
||||
FD(evsel, cpu, thread) = -1;
|
||||
}
|
||||
thread = threads->nr;
|
||||
thread = nthreads;
|
||||
} while (--cpu >= 0);
|
||||
return err;
|
||||
}
|
||||
@ -2002,6 +2026,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
|
||||
int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
|
||||
int err, char *msg, size_t size)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
switch (err) {
|
||||
case EPERM:
|
||||
case EACCES:
|
||||
@ -2036,13 +2062,20 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
|
||||
"No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.");
|
||||
#endif
|
||||
break;
|
||||
case EBUSY:
|
||||
if (find_process("oprofiled"))
|
||||
return scnprintf(msg, size,
|
||||
"The PMU counters are busy/taken by another profiler.\n"
|
||||
"We found oprofile daemon running, please stop it and try again.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf(msg, size,
|
||||
"The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n"
|
||||
"The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"
|
||||
"/bin/dmesg may provide additional information.\n"
|
||||
"No CONFIG_PERF_EVENTS=y kernel support configured?\n",
|
||||
err, strerror(err), perf_evsel__name(evsel));
|
||||
err, strerror_r(err, sbuf, sizeof(sbuf)),
|
||||
perf_evsel__name(evsel));
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ struct perf_evsel {
|
||||
bool needs_swap;
|
||||
bool no_aux_samples;
|
||||
bool immediate;
|
||||
bool system_wide;
|
||||
bool tracking;
|
||||
/* parse modifier helper */
|
||||
int exclude_GH;
|
||||
int nr_members;
|
||||
|
@ -214,11 +214,11 @@ static int machine__hit_all_dsos(struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __dsos__hit_all(&machine->kernel_dsos);
|
||||
err = __dsos__hit_all(&machine->kernel_dsos.head);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return __dsos__hit_all(&machine->user_dsos);
|
||||
return __dsos__hit_all(&machine->user_dsos.head);
|
||||
}
|
||||
|
||||
int dsos__hit_all(struct perf_session *session)
|
||||
@ -288,11 +288,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
|
||||
umisc = PERF_RECORD_MISC_GUEST_USER;
|
||||
}
|
||||
|
||||
err = __dsos__write_buildid_table(&machine->kernel_dsos, machine,
|
||||
err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
|
||||
machine->pid, kmisc, fd);
|
||||
if (err == 0)
|
||||
err = __dsos__write_buildid_table(&machine->user_dsos, machine,
|
||||
machine->pid, umisc, fd);
|
||||
err = __dsos__write_buildid_table(&machine->user_dsos.head,
|
||||
machine, machine->pid, umisc,
|
||||
fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -455,9 +456,10 @@ static int __dsos__cache_build_ids(struct list_head *head,
|
||||
|
||||
static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
|
||||
{
|
||||
int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine,
|
||||
int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine,
|
||||
debugdir);
|
||||
ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);
|
||||
ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine,
|
||||
debugdir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -483,8 +485,10 @@ static int perf_session__cache_build_ids(struct perf_session *session)
|
||||
|
||||
static bool machine__read_build_ids(struct machine *machine, bool with_hits)
|
||||
{
|
||||
bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits);
|
||||
ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);
|
||||
bool ret;
|
||||
|
||||
ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
|
||||
ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1548,7 +1552,7 @@ static int __event_process_build_id(struct build_id_event *bev,
|
||||
struct perf_session *session)
|
||||
{
|
||||
int err = -1;
|
||||
struct list_head *head;
|
||||
struct dsos *dsos;
|
||||
struct machine *machine;
|
||||
u16 misc;
|
||||
struct dso *dso;
|
||||
@ -1563,22 +1567,22 @@ static int __event_process_build_id(struct build_id_event *bev,
|
||||
switch (misc) {
|
||||
case PERF_RECORD_MISC_KERNEL:
|
||||
dso_type = DSO_TYPE_KERNEL;
|
||||
head = &machine->kernel_dsos;
|
||||
dsos = &machine->kernel_dsos;
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||
dso_type = DSO_TYPE_GUEST_KERNEL;
|
||||
head = &machine->kernel_dsos;
|
||||
dsos = &machine->kernel_dsos;
|
||||
break;
|
||||
case PERF_RECORD_MISC_USER:
|
||||
case PERF_RECORD_MISC_GUEST_USER:
|
||||
dso_type = DSO_TYPE_USER;
|
||||
head = &machine->user_dsos;
|
||||
dsos = &machine->user_dsos;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
dso = __dsos__findnew(head, filename);
|
||||
dso = __dsos__findnew(dsos, filename);
|
||||
if (dso != NULL) {
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user