diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index bc3f2efa0d86..789d846a9184 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -99,10 +99,6 @@ int armpmu_event_set_period(struct perf_event *event) s64 period = hwc->sample_period; int ret = 0; - /* The period may have been changed by PERF_EVENT_IOC_PERIOD */ - if (unlikely(period != hwc->last_period)) - left = period - (hwc->last_period - left); - if (unlikely(left <= -period)) { left = period; local64_set(&hwc->period_left, left); diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h index 75c6ecdb8f37..7422a999a39a 100644 --- a/arch/powerpc/include/asm/uprobes.h +++ b/arch/powerpc/include/asm/uprobes.h @@ -36,9 +36,8 @@ typedef ppc_opcode_t uprobe_opcode_t; struct arch_uprobe { union { - u8 insn[MAX_UINSN_BYTES]; - u8 ixol[MAX_UINSN_BYTES]; - u32 ainsn; + u32 insn; + u32 ixol; }; }; diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c index 59f419b935f2..003b20964ea0 100644 --- a/arch/powerpc/kernel/uprobes.c +++ b/arch/powerpc/kernel/uprobes.c @@ -186,7 +186,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) * emulate_step() returns 1 if the insn was successfully emulated. * For all other cases, we need to single-step in hardware. */ - ret = emulate_step(regs, auprobe->ainsn); + ret = emulate_step(regs, auprobe->insn); if (ret > 0) return true; diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 47b56a7e99cb..6359506a19ee 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -36,7 +36,7 @@ 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 +obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o perf_event_intel_rapl.o endif diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c new file mode 100644 index 000000000000..5ad35ad94d0f --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c @@ -0,0 +1,679 @@ +/* + * perf_event_intel_rapl.c: support Intel RAPL energy consumption counters + * Copyright (C) 2013 Google, Inc., Stephane Eranian + * + * Intel RAPL interface is specified in the IA-32 Manual Vol3b + * section 14.7.1 (September 2013) + * + * RAPL provides more controls than just reporting energy consumption + * however here we only expose the 3 energy consumption free running + * counters (pp0, pkg, dram). + * + * Each of those counters increments in a power unit defined by the + * RAPL_POWER_UNIT MSR. On SandyBridge, this unit is 1/(2^16) Joules + * but it can vary. + * + * Counter to rapl events mappings: + * + * pp0 counter: consumption of all physical cores (power plane 0) + * event: rapl_energy_cores + * perf code: 0x1 + * + * pkg counter: consumption of the whole processor package + * event: rapl_energy_pkg + * perf code: 0x2 + * + * dram counter: consumption of the dram domain (servers only) + * event: rapl_energy_dram + * perf code: 0x3 + * + * dram counter: consumption of the builtin-gpu domain (client only) + * event: rapl_energy_gpu + * perf code: 0x4 + * + * We manage those counters as free running (read-only). They may be + * use simultaneously by other tools, such as turbostat. + * + * The events only support system-wide mode counting. There is no + * sampling support because it does not make sense and is not + * supported by the RAPL hardware. + * + * Because we want to avoid floating-point operations in the kernel, + * the events are all reported in fixed point arithmetic (32.32). + * Tools must adjust the counts to convert them to Watts using + * the duration of the measurement. Tools may use a function such as + * ldexp(raw_count, -32); + */ +#include +#include +#include +#include +#include "perf_event.h" + +/* + * RAPL energy status counters + */ +#define RAPL_IDX_PP0_NRG_STAT 0 /* all cores */ +#define INTEL_RAPL_PP0 0x1 /* pseudo-encoding */ +#define RAPL_IDX_PKG_NRG_STAT 1 /* entire package */ +#define INTEL_RAPL_PKG 0x2 /* pseudo-encoding */ +#define RAPL_IDX_RAM_NRG_STAT 2 /* DRAM */ +#define INTEL_RAPL_RAM 0x3 /* pseudo-encoding */ +#define RAPL_IDX_PP1_NRG_STAT 3 /* DRAM */ +#define INTEL_RAPL_PP1 0x4 /* pseudo-encoding */ + +/* Clients have PP0, PKG */ +#define RAPL_IDX_CLN (1<config + * any other bit is reserved + */ +#define RAPL_EVENT_MASK 0xFFULL + +#define DEFINE_RAPL_FORMAT_ATTR(_var, _name, _format) \ +static ssize_t __rapl_##_var##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + char *page) \ +{ \ + BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \ + return sprintf(page, _format "\n"); \ +} \ +static struct kobj_attribute format_attr_##_var = \ + __ATTR(_name, 0444, __rapl_##_var##_show, NULL) + +#define RAPL_EVENT_DESC(_name, _config) \ +{ \ + .attr = __ATTR(_name, 0444, rapl_event_show, NULL), \ + .config = _config, \ +} + +#define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */ + +struct rapl_pmu { + spinlock_t lock; + int hw_unit; /* 1/2^hw_unit Joule */ + int n_active; /* number of active events */ + struct list_head active_list; + struct pmu *pmu; /* pointer to rapl_pmu_class */ + ktime_t timer_interval; /* in ktime_t unit */ + struct hrtimer hrtimer; +}; + +static struct pmu rapl_pmu_class; +static cpumask_t rapl_cpu_mask; +static int rapl_cntr_mask; + +static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu); +static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu_to_free); + +static inline u64 rapl_read_counter(struct perf_event *event) +{ + u64 raw; + rdmsrl(event->hw.event_base, raw); + return raw; +} + +static inline u64 rapl_scale(u64 v) +{ + /* + * scale delta to smallest unit (1/2^32) + * users must then scale back: count * 1/(1e9*2^32) to get Joules + * or use ldexp(count, -32). + * Watts = Joules/Time delta + */ + return v << (32 - __get_cpu_var(rapl_pmu)->hw_unit); +} + +static u64 rapl_event_update(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 prev_raw_count, new_raw_count; + s64 delta, sdelta; + int shift = RAPL_CNTR_WIDTH; + +again: + prev_raw_count = local64_read(&hwc->prev_count); + rdmsrl(event->hw.event_base, new_raw_count); + + if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) { + cpu_relax(); + goto again; + } + + /* + * Now we have the new raw value and have updated the prev + * timestamp already. We can now calculate the elapsed delta + * (event-)time and add that to the generic event. + * + * Careful, not all hw sign-extends above the physical width + * of the count. + */ + delta = (new_raw_count << shift) - (prev_raw_count << shift); + delta >>= shift; + + sdelta = rapl_scale(delta); + + local64_add(sdelta, &event->count); + + return new_raw_count; +} + +static void rapl_start_hrtimer(struct rapl_pmu *pmu) +{ + __hrtimer_start_range_ns(&pmu->hrtimer, + pmu->timer_interval, 0, + HRTIMER_MODE_REL_PINNED, 0); +} + +static void rapl_stop_hrtimer(struct rapl_pmu *pmu) +{ + hrtimer_cancel(&pmu->hrtimer); +} + +static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer) +{ + struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct perf_event *event; + unsigned long flags; + + if (!pmu->n_active) + return HRTIMER_NORESTART; + + spin_lock_irqsave(&pmu->lock, flags); + + list_for_each_entry(event, &pmu->active_list, active_entry) { + rapl_event_update(event); + } + + spin_unlock_irqrestore(&pmu->lock, flags); + + hrtimer_forward_now(hrtimer, pmu->timer_interval); + + return HRTIMER_RESTART; +} + +static void rapl_hrtimer_init(struct rapl_pmu *pmu) +{ + struct hrtimer *hr = &pmu->hrtimer; + + hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hr->function = rapl_hrtimer_handle; +} + +static void __rapl_pmu_event_start(struct rapl_pmu *pmu, + struct perf_event *event) +{ + if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) + return; + + event->hw.state = 0; + + list_add_tail(&event->active_entry, &pmu->active_list); + + local64_set(&event->hw.prev_count, rapl_read_counter(event)); + + pmu->n_active++; + if (pmu->n_active == 1) + rapl_start_hrtimer(pmu); +} + +static void rapl_pmu_event_start(struct perf_event *event, int mode) +{ + struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + unsigned long flags; + + spin_lock_irqsave(&pmu->lock, flags); + __rapl_pmu_event_start(pmu, event); + spin_unlock_irqrestore(&pmu->lock, flags); +} + +static void rapl_pmu_event_stop(struct perf_event *event, int mode) +{ + struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long flags; + + spin_lock_irqsave(&pmu->lock, flags); + + /* mark event as deactivated and stopped */ + if (!(hwc->state & PERF_HES_STOPPED)) { + WARN_ON_ONCE(pmu->n_active <= 0); + pmu->n_active--; + if (pmu->n_active == 0) + rapl_stop_hrtimer(pmu); + + list_del(&event->active_entry); + + WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); + hwc->state |= PERF_HES_STOPPED; + } + + /* check if update of sw counter is necessary */ + if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) { + /* + * Drain the remaining delta count out of a event + * that we are disabling: + */ + rapl_event_update(event); + hwc->state |= PERF_HES_UPTODATE; + } + + spin_unlock_irqrestore(&pmu->lock, flags); +} + +static int rapl_pmu_event_add(struct perf_event *event, int mode) +{ + struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long flags; + + spin_lock_irqsave(&pmu->lock, flags); + + hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; + + if (mode & PERF_EF_START) + __rapl_pmu_event_start(pmu, event); + + spin_unlock_irqrestore(&pmu->lock, flags); + + return 0; +} + +static void rapl_pmu_event_del(struct perf_event *event, int flags) +{ + rapl_pmu_event_stop(event, PERF_EF_UPDATE); +} + +static int rapl_pmu_event_init(struct perf_event *event) +{ + u64 cfg = event->attr.config & RAPL_EVENT_MASK; + int bit, msr, ret = 0; + + /* only look at RAPL events */ + if (event->attr.type != rapl_pmu_class.type) + return -ENOENT; + + /* check only supported bits are set */ + if (event->attr.config & ~RAPL_EVENT_MASK) + return -EINVAL; + + /* + * check event is known (determines counter) + */ + switch (cfg) { + case INTEL_RAPL_PP0: + bit = RAPL_IDX_PP0_NRG_STAT; + msr = MSR_PP0_ENERGY_STATUS; + break; + case INTEL_RAPL_PKG: + bit = RAPL_IDX_PKG_NRG_STAT; + msr = MSR_PKG_ENERGY_STATUS; + break; + case INTEL_RAPL_RAM: + bit = RAPL_IDX_RAM_NRG_STAT; + msr = MSR_DRAM_ENERGY_STATUS; + break; + case INTEL_RAPL_PP1: + bit = RAPL_IDX_PP1_NRG_STAT; + msr = MSR_PP1_ENERGY_STATUS; + break; + default: + return -EINVAL; + } + /* check event supported */ + if (!(rapl_cntr_mask & (1 << bit))) + 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; + + /* must be done before validate_group */ + event->hw.event_base = msr; + event->hw.config = cfg; + event->hw.idx = bit; + + return ret; +} + +static void rapl_pmu_event_read(struct perf_event *event) +{ + rapl_event_update(event); +} + +static ssize_t rapl_get_attr_cpumask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &rapl_cpu_mask); + + buf[n++] = '\n'; + buf[n] = '\0'; + return n; +} + +static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL); + +static struct attribute *rapl_pmu_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static struct attribute_group rapl_pmu_attr_group = { + .attrs = rapl_pmu_attrs, +}; + +EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01"); +EVENT_ATTR_STR(energy-pkg , rapl_pkg, "event=0x02"); +EVENT_ATTR_STR(energy-ram , rapl_ram, "event=0x03"); +EVENT_ATTR_STR(energy-gpu , rapl_gpu, "event=0x04"); + +EVENT_ATTR_STR(energy-cores.unit, rapl_cores_unit, "Joules"); +EVENT_ATTR_STR(energy-pkg.unit , rapl_pkg_unit, "Joules"); +EVENT_ATTR_STR(energy-ram.unit , rapl_ram_unit, "Joules"); +EVENT_ATTR_STR(energy-gpu.unit , rapl_gpu_unit, "Joules"); + +/* + * we compute in 0.23 nJ increments regardless of MSR + */ +EVENT_ATTR_STR(energy-cores.scale, rapl_cores_scale, "2.3283064365386962890625e-10"); +EVENT_ATTR_STR(energy-pkg.scale, rapl_pkg_scale, "2.3283064365386962890625e-10"); +EVENT_ATTR_STR(energy-ram.scale, rapl_ram_scale, "2.3283064365386962890625e-10"); +EVENT_ATTR_STR(energy-gpu.scale, rapl_gpu_scale, "2.3283064365386962890625e-10"); + +static struct attribute *rapl_events_srv_attr[] = { + EVENT_PTR(rapl_cores), + EVENT_PTR(rapl_pkg), + EVENT_PTR(rapl_ram), + + EVENT_PTR(rapl_cores_unit), + EVENT_PTR(rapl_pkg_unit), + EVENT_PTR(rapl_ram_unit), + + EVENT_PTR(rapl_cores_scale), + EVENT_PTR(rapl_pkg_scale), + EVENT_PTR(rapl_ram_scale), + NULL, +}; + +static struct attribute *rapl_events_cln_attr[] = { + EVENT_PTR(rapl_cores), + EVENT_PTR(rapl_pkg), + EVENT_PTR(rapl_gpu), + + EVENT_PTR(rapl_cores_unit), + EVENT_PTR(rapl_pkg_unit), + EVENT_PTR(rapl_gpu_unit), + + EVENT_PTR(rapl_cores_scale), + EVENT_PTR(rapl_pkg_scale), + EVENT_PTR(rapl_gpu_scale), + NULL, +}; + +static struct attribute_group rapl_pmu_events_group = { + .name = "events", + .attrs = NULL, /* patched at runtime */ +}; + +DEFINE_RAPL_FORMAT_ATTR(event, event, "config:0-7"); +static struct attribute *rapl_formats_attr[] = { + &format_attr_event.attr, + NULL, +}; + +static struct attribute_group rapl_pmu_format_group = { + .name = "format", + .attrs = rapl_formats_attr, +}; + +const struct attribute_group *rapl_attr_groups[] = { + &rapl_pmu_attr_group, + &rapl_pmu_format_group, + &rapl_pmu_events_group, + NULL, +}; + +static struct pmu rapl_pmu_class = { + .attr_groups = rapl_attr_groups, + .task_ctx_nr = perf_invalid_context, /* system-wide only */ + .event_init = rapl_pmu_event_init, + .add = rapl_pmu_event_add, /* must have */ + .del = rapl_pmu_event_del, /* must have */ + .start = rapl_pmu_event_start, + .stop = rapl_pmu_event_stop, + .read = rapl_pmu_event_read, +}; + +static void rapl_cpu_exit(int cpu) +{ + struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu); + int i, phys_id = topology_physical_package_id(cpu); + int target = -1; + + /* find a new cpu on same package */ + for_each_online_cpu(i) { + if (i == cpu) + continue; + if (phys_id == topology_physical_package_id(i)) { + target = i; + break; + } + } + /* + * clear cpu from cpumask + * if was set in cpumask and still some cpu on package, + * then move to new cpu + */ + if (cpumask_test_and_clear_cpu(cpu, &rapl_cpu_mask) && target >= 0) + cpumask_set_cpu(target, &rapl_cpu_mask); + + WARN_ON(cpumask_empty(&rapl_cpu_mask)); + /* + * migrate events and context to new cpu + */ + if (target >= 0) + perf_pmu_migrate_context(pmu->pmu, cpu, target); + + /* cancel overflow polling timer for CPU */ + rapl_stop_hrtimer(pmu); +} + +static void rapl_cpu_init(int cpu) +{ + int i, phys_id = topology_physical_package_id(cpu); + + /* check if phys_is is already covered */ + for_each_cpu(i, &rapl_cpu_mask) { + if (phys_id == topology_physical_package_id(i)) + return; + } + /* was not found, so add it */ + cpumask_set_cpu(cpu, &rapl_cpu_mask); +} + +static int rapl_cpu_prepare(int cpu) +{ + struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu); + int phys_id = topology_physical_package_id(cpu); + u64 ms; + + if (pmu) + return 0; + + if (phys_id < 0) + return -1; + + pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu)); + if (!pmu) + return -1; + + spin_lock_init(&pmu->lock); + + INIT_LIST_HEAD(&pmu->active_list); + + /* + * grab power unit as: 1/2^unit Joules + * + * we cache in local PMU instance + */ + rdmsrl(MSR_RAPL_POWER_UNIT, pmu->hw_unit); + pmu->hw_unit = (pmu->hw_unit >> 8) & 0x1FULL; + pmu->pmu = &rapl_pmu_class; + + /* + * use reference of 200W for scaling the timeout + * to avoid missing counter overflows. + * 200W = 200 Joules/sec + * divide interval by 2 to avoid lockstep (2 * 100) + * if hw unit is 32, then we use 2 ms 1/200/2 + */ + if (pmu->hw_unit < 32) + ms = (1000 / (2 * 100)) * (1ULL << (32 - pmu->hw_unit - 1)); + else + ms = 2; + + pmu->timer_interval = ms_to_ktime(ms); + + rapl_hrtimer_init(pmu); + + /* set RAPL pmu for this cpu for now */ + per_cpu(rapl_pmu, cpu) = pmu; + per_cpu(rapl_pmu_to_free, cpu) = NULL; + + return 0; +} + +static void rapl_cpu_kfree(int cpu) +{ + struct rapl_pmu *pmu = per_cpu(rapl_pmu_to_free, cpu); + + kfree(pmu); + + per_cpu(rapl_pmu_to_free, cpu) = NULL; +} + +static int rapl_cpu_dying(int cpu) +{ + struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu); + + if (!pmu) + return 0; + + per_cpu(rapl_pmu, cpu) = NULL; + + per_cpu(rapl_pmu_to_free, cpu) = pmu; + + return 0; +} + +static int rapl_cpu_notifier(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + rapl_cpu_prepare(cpu); + break; + case CPU_STARTING: + rapl_cpu_init(cpu); + break; + case CPU_UP_CANCELED: + case CPU_DYING: + rapl_cpu_dying(cpu); + break; + case CPU_ONLINE: + case CPU_DEAD: + rapl_cpu_kfree(cpu); + break; + case CPU_DOWN_PREPARE: + rapl_cpu_exit(cpu); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static const struct x86_cpu_id rapl_cpu_match[] = { + [0] = { .vendor = X86_VENDOR_INTEL, .family = 6 }, + [1] = {}, +}; + +static int __init rapl_pmu_init(void) +{ + struct rapl_pmu *pmu; + int cpu, ret; + + /* + * check for Intel processor family 6 + */ + if (!x86_match_cpu(rapl_cpu_match)) + return 0; + + /* check supported CPU */ + switch (boot_cpu_data.x86_model) { + case 42: /* Sandy Bridge */ + case 58: /* Ivy Bridge */ + case 60: /* Haswell */ + case 69: /* Haswell-Celeron */ + rapl_cntr_mask = RAPL_IDX_CLN; + rapl_pmu_events_group.attrs = rapl_events_cln_attr; + break; + case 45: /* Sandy Bridge-EP */ + case 62: /* IvyTown */ + rapl_cntr_mask = RAPL_IDX_SRV; + rapl_pmu_events_group.attrs = rapl_events_srv_attr; + break; + + default: + /* unsupported */ + return 0; + } + get_online_cpus(); + + for_each_online_cpu(cpu) { + rapl_cpu_prepare(cpu); + rapl_cpu_init(cpu); + } + + perf_cpu_notifier(rapl_cpu_notifier); + + ret = perf_pmu_register(&rapl_pmu_class, "power", -1); + if (WARN_ON(ret)) { + pr_info("RAPL PMU detected, registration failed (%d), RAPL PMU disabled\n", ret); + put_online_cpus(); + return -1; + } + + pmu = __get_cpu_var(rapl_pmu); + + pr_info("RAPL PMU detected, hw unit 2^-%d Joules," + " API unit is 2^-32 Joules," + " %d fixed counters" + " %llu ms ovfl timer\n", + pmu->hw_unit, + hweight32(rapl_cntr_mask), + ktime_to_ms(pmu->timer_interval)); + + put_online_cpus(); + + return 0; +} +device_initcall(rapl_pmu_init); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 2e069d1288df..e56b07f5c9b6 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -320,6 +320,7 @@ struct perf_event { struct list_head migrate_entry; struct hlist_node hlist_entry; + struct list_head active_entry; int nr_siblings; int group_flags; struct perf_event *group_leader; diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 319eae70fe84..e32251e00e62 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -26,16 +26,13 @@ #include #include +#include struct vm_area_struct; struct mm_struct; struct inode; struct notifier_block; -#ifdef CONFIG_ARCH_SUPPORTS_UPROBES -# include -#endif - #define UPROBE_HANDLER_REMOVE 1 #define UPROBE_HANDLER_MASK 1 @@ -60,6 +57,8 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES +#include + enum uprobe_task_state { UTASK_RUNNING, UTASK_SSTEP, @@ -72,34 +71,27 @@ enum uprobe_task_state { */ struct uprobe_task { enum uprobe_task_state state; - struct arch_uprobe_task autask; + + union { + struct { + struct arch_uprobe_task autask; + unsigned long vaddr; + }; + + struct { + struct callback_head dup_xol_work; + unsigned long dup_xol_addr; + }; + }; + + struct uprobe *active_uprobe; + unsigned long xol_vaddr; struct return_instance *return_instances; unsigned int depth; - struct uprobe *active_uprobe; - - unsigned long xol_vaddr; - unsigned long vaddr; }; -/* - * On a breakpoint hit, thread contests for a slot. It frees the - * slot after singlestep. Currently a fixed number of slots are - * allocated. - */ -struct xol_area { - wait_queue_head_t wq; /* if all slots are busy */ - atomic_t slot_count; /* number of in-use slots */ - unsigned long *bitmap; /* 0 = free slot */ - struct page *page; - - /* - * We keep the vma's vm_start rather than a pointer to the vma - * itself. The probed process or a naughty kernel module could make - * the vma go away, and we must handle that reasonably gracefully. - */ - unsigned long vaddr; /* Page(s) of instruction slots */ -}; +struct xol_area; struct uprobes_state { struct xol_area *xol_area; @@ -109,6 +101,7 @@ extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsign extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern bool __weak is_trap_insn(uprobe_opcode_t *insn); +extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); @@ -120,7 +113,6 @@ extern void uprobe_end_dup_mmap(void); extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_copy_process(struct task_struct *t, unsigned long flags); -extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); extern int uprobe_post_sstep_notifier(struct pt_regs *regs); extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); extern void uprobe_notify_resume(struct pt_regs *regs); @@ -176,10 +168,6 @@ static inline bool uprobe_deny_signal(void) { return false; } -static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) -{ - return 0; -} static inline void uprobe_free_utask(struct task_struct *t) { } diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 959d454f76a1..e244ed412745 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -725,6 +725,7 @@ enum perf_callchain_context { #define PERF_FLAG_FD_NO_GROUP (1U << 0) #define PERF_FLAG_FD_OUTPUT (1U << 1) #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ +#define PERF_FLAG_FD_CLOEXEC (1U << 3) /* O_CLOEXEC */ union perf_mem_data_src { __u64 val; diff --git a/kernel/events/core.c b/kernel/events/core.c index f5744010a8d2..56003c6edfd3 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -119,7 +119,8 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info) #define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\ PERF_FLAG_FD_OUTPUT |\ - PERF_FLAG_PID_CGROUP) + PERF_FLAG_PID_CGROUP |\ + PERF_FLAG_FD_CLOEXEC) /* * branch priv levels that need permission checks @@ -3542,7 +3543,7 @@ static void perf_event_for_each(struct perf_event *event, static int perf_event_period(struct perf_event *event, u64 __user *arg) { struct perf_event_context *ctx = event->ctx; - int ret = 0; + int ret = 0, active; u64 value; if (!is_sampling_event(event)) @@ -3566,6 +3567,20 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) event->attr.sample_period = value; event->hw.sample_period = value; } + + active = (event->state == PERF_EVENT_STATE_ACTIVE); + if (active) { + perf_pmu_disable(ctx->pmu); + event->pmu->stop(event, PERF_EF_UPDATE); + } + + local64_set(&event->hw.period_left, 0); + + if (active) { + event->pmu->start(event, PERF_EF_RELOAD); + perf_pmu_enable(ctx->pmu); + } + unlock: raw_spin_unlock_irq(&ctx->lock); @@ -6670,6 +6685,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, INIT_LIST_HEAD(&event->event_entry); INIT_LIST_HEAD(&event->sibling_list); INIT_LIST_HEAD(&event->rb_entry); + INIT_LIST_HEAD(&event->active_entry); + INIT_HLIST_NODE(&event->hlist_entry); + init_waitqueue_head(&event->waitq); init_irq_work(&event->pending, perf_pending_event); @@ -6980,6 +6998,7 @@ SYSCALL_DEFINE5(perf_event_open, int event_fd; int move_group = 0; int err; + int f_flags = O_RDWR; /* for future expandability... */ if (flags & ~PERF_FLAG_ALL) @@ -7008,7 +7027,10 @@ SYSCALL_DEFINE5(perf_event_open, if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1)) return -EINVAL; - event_fd = get_unused_fd(); + if (flags & PERF_FLAG_FD_CLOEXEC) + f_flags |= O_CLOEXEC; + + event_fd = get_unused_fd_flags(f_flags); if (event_fd < 0) return event_fd; @@ -7130,7 +7152,8 @@ SYSCALL_DEFINE5(perf_event_open, goto err_context; } - event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR); + event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, + f_flags); if (IS_ERR(event_file)) { err = PTR_ERR(event_file); goto err_context; diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index e8b168af135b..146a5792b1d2 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -61,19 +61,20 @@ again: * * kernel user * - * READ ->data_tail READ ->data_head - * smp_mb() (A) smp_rmb() (C) - * WRITE $data READ $data - * smp_wmb() (B) smp_mb() (D) - * STORE ->data_head WRITE ->data_tail + * if (LOAD ->data_tail) { LOAD ->data_head + * (A) smp_rmb() (C) + * STORE $data LOAD $data + * smp_wmb() (B) smp_mb() (D) + * STORE ->data_head STORE ->data_tail + * } * * Where A pairs with D, and B pairs with C. * - * I don't think A needs to be a full barrier because we won't in fact - * write data until we see the store from userspace. So we simply don't - * issue the data WRITE until we observe it. Be conservative for now. + * In our case (A) is a control dependency that separates the load of + * the ->data_tail and the stores of $data. In case ->data_tail + * indicates there is no room in the buffer to store $data we do not. * - * OTOH, D needs to be a full barrier since it separates the data READ + * D needs to be a full barrier since it separates the data READ * from the tail WRITE. * * For B a WMB is sufficient since it separates two WRITEs, and for C @@ -81,7 +82,7 @@ again: * * See perf_output_begin(). */ - smp_wmb(); + smp_wmb(); /* B, matches C */ rb->user_page->data_head = head; /* @@ -144,17 +145,26 @@ int perf_output_begin(struct perf_output_handle *handle, if (!rb->overwrite && unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) goto fail; + + /* + * The above forms a control dependency barrier separating the + * @tail load above from the data stores below. Since the @tail + * load is required to compute the branch to fail below. + * + * A, matches D; the full memory barrier userspace SHOULD issue + * after reading the data and before storing the new tail + * position. + * + * See perf_output_put_handle(). + */ + head += size; } while (local_cmpxchg(&rb->head, offset, head) != offset); /* - * Separate the userpage->tail read from the data stores below. - * Matches the MB userspace SHOULD issue after reading the data - * and before storing the new tail position. - * - * See perf_output_put_handle(). + * We rely on the implied barrier() by local_cmpxchg() to ensure + * none of the data stores below can be lifted up by the compiler. */ - smp_mb(); if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) local_add(rb->watermark, &rb->wakeup); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 24b7d6ca871b..b886a5e7d4ff 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -73,6 +73,17 @@ struct uprobe { struct inode *inode; /* Also hold a ref to inode */ loff_t offset; unsigned long flags; + + /* + * The generic code assumes that it has two members of unknown type + * owned by the arch-specific code: + * + * insn - copy_insn() saves the original instruction here for + * arch_uprobe_analyze_insn(). + * + * ixol - potentially modified instruction to execute out of + * line, copied to xol_area by xol_get_insn_slot(). + */ struct arch_uprobe arch; }; @@ -85,6 +96,29 @@ struct return_instance { struct return_instance *next; /* keep as stack */ }; +/* + * Execute out of line area: anonymous executable mapping installed + * by the probed task to execute the copy of the original instruction + * mangled by set_swbp(). + * + * On a breakpoint hit, thread contests for a slot. It frees the + * slot after singlestep. Currently a fixed number of slots are + * allocated. + */ +struct xol_area { + wait_queue_head_t wq; /* if all slots are busy */ + atomic_t slot_count; /* number of in-use slots */ + unsigned long *bitmap; /* 0 = free slot */ + struct page *page; + + /* + * We keep the vma's vm_start rather than a pointer to the vma + * itself. The probed process or a naughty kernel module could make + * the vma go away, and we must handle that reasonably gracefully. + */ + unsigned long vaddr; /* Page(s) of instruction slots */ +}; + /* * valid_vma: Verify if the specified vma is an executable vma * Relax restrictions while unregistering: vm_flags might have @@ -330,7 +364,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned int __weak set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { - return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); + return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)&auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -529,8 +563,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp) { struct address_space *mapping = uprobe->inode->i_mapping; loff_t offs = uprobe->offset; - void *insn = uprobe->arch.insn; - int size = MAX_UINSN_BYTES; + void *insn = &uprobe->arch.insn; + int size = sizeof(uprobe->arch.insn); int len, err = -EIO; /* Copy only available bytes, -EIO if nothing was read */ @@ -569,7 +603,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, goto out; ret = -ENOTSUPP; - if (is_trap_insn((uprobe_opcode_t *)uprobe->arch.insn)) + if (is_trap_insn((uprobe_opcode_t *)&uprobe->arch.insn)) goto out; ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr); @@ -1264,7 +1298,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe) /* Initialize the slot */ copy_to_page(area->page, xol_vaddr, - uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); + &uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); /* * We probably need flush_icache_user_range() but it needs vma. * This should work on supported architectures too. @@ -1403,12 +1437,10 @@ static void uprobe_warn(struct task_struct *t, const char *msg) static void dup_xol_work(struct callback_head *work) { - kfree(work); - if (current->flags & PF_EXITING) return; - if (!__create_xol_area(current->utask->vaddr)) + if (!__create_xol_area(current->utask->dup_xol_addr)) uprobe_warn(current, "dup xol area"); } @@ -1419,7 +1451,6 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags) { struct uprobe_task *utask = current->utask; struct mm_struct *mm = current->mm; - struct callback_head *work; struct xol_area *area; t->utask = NULL; @@ -1441,14 +1472,9 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags) if (mm == t->mm) return; - /* TODO: move it into the union in uprobe_task */ - work = kmalloc(sizeof(*work), GFP_KERNEL); - if (!work) - return uprobe_warn(t, "dup xol area"); - - t->utask->vaddr = area->vaddr; - init_task_work(work, dup_xol_work); - task_work_add(t, work, true); + t->utask->dup_xol_addr = area->vaddr; + init_task_work(&t->utask->dup_xol_work, dup_xol_work); + task_work_add(t, &t->utask->dup_xol_work, true); } /* diff --git a/tools/Makefile b/tools/Makefile index a9b02008443c..927cd46d36dc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -39,10 +39,10 @@ cpupower: FORCE cgroup firewire guest usb virtio vm net: FORCE $(call descend,$@) -liblk: FORCE - $(call descend,lib/lk) +libapikfs: FORCE + $(call descend,lib/api) -perf: liblk FORCE +perf: libapikfs FORCE $(call descend,$@) selftests: FORCE @@ -80,10 +80,10 @@ cpupower_clean: cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: $(call descend,$(@:_clean=),clean) -liblk_clean: - $(call descend,lib/lk,clean) +libapikfs_clean: + $(call descend,lib/api,clean) -perf_clean: liblk_clean +perf_clean: libapikfs_clean $(call descend,$(@:_clean=),clean) selftests_clean: diff --git a/tools/perf/util/include/asm/bug.h b/tools/include/asm/bug.h similarity index 81% rename from tools/perf/util/include/asm/bug.h rename to tools/include/asm/bug.h index 7fcc6810adc2..9e5f4846967f 100644 --- a/tools/perf/util/include/asm/bug.h +++ b/tools/include/asm/bug.h @@ -1,5 +1,7 @@ -#ifndef _PERF_ASM_GENERIC_BUG_H -#define _PERF_ASM_GENERIC_BUG_H +#ifndef _TOOLS_ASM_BUG_H +#define _TOOLS_ASM_BUG_H + +#include #define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) @@ -19,4 +21,5 @@ __warned = 1; \ unlikely(__ret_warn_once); \ }) -#endif + +#endif /* _TOOLS_ASM_BUG_H */ diff --git a/tools/perf/util/include/linux/compiler.h b/tools/include/linux/compiler.h similarity index 64% rename from tools/perf/util/include/linux/compiler.h rename to tools/include/linux/compiler.h index b003ad7200b2..fbc6665c6d53 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -1,5 +1,5 @@ -#ifndef _PERF_LINUX_COMPILER_H_ -#define _PERF_LINUX_COMPILER_H_ +#ifndef _TOOLS_LINUX_COMPILER_H_ +#define _TOOLS_LINUX_COMPILER_H_ #ifndef __always_inline # define __always_inline inline __attribute__((always_inline)) @@ -27,4 +27,12 @@ # define __weak __attribute__((weak)) #endif +#ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) #endif + +#ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#endif /* _TOOLS_LINUX_COMPILER_H */ diff --git a/tools/lib/lk/Makefile b/tools/lib/api/Makefile similarity index 66% rename from tools/lib/lk/Makefile rename to tools/lib/api/Makefile index 3dba0a4aebbf..ed2f51e11b80 100644 --- a/tools/lib/lk/Makefile +++ b/tools/lib/api/Makefile @@ -1,4 +1,5 @@ include ../../scripts/Makefile.include +include ../../perf/config/utilities.mak # QUIET_CLEAN CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar @@ -7,11 +8,11 @@ AR = $(CROSS_COMPILE)ar LIB_H= LIB_OBJS= -LIB_H += debugfs.h +LIB_H += fs/debugfs.h -LIB_OBJS += $(OUTPUT)debugfs.o +LIB_OBJS += $(OUTPUT)fs/debugfs.o -LIBFILE = liblk.a +LIBFILE = libapikfs.a CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC EXTLIBS = -lelf -lpthread -lrt -lm @@ -25,14 +26,17 @@ $(LIBFILE): $(LIB_OBJS) $(LIB_OBJS): $(LIB_H) -$(OUTPUT)%.o: %.c +libapi_dirs: + $(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/ + +$(OUTPUT)%.o: %.c libapi_dirs $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< -$(OUTPUT)%.s: %.c +$(OUTPUT)%.s: %.c libapi_dirs $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< -$(OUTPUT)%.o: %.S +$(OUTPUT)%.o: %.S libapi_dirs $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< clean: - $(RM) $(LIB_OBJS) $(LIBFILE) + $(call QUIET_CLEAN, libapi) $(RM) $(LIB_OBJS) $(LIBFILE) .PHONY: clean diff --git a/tools/lib/lk/debugfs.c b/tools/lib/api/fs/debugfs.c similarity index 100% rename from tools/lib/lk/debugfs.c rename to tools/lib/api/fs/debugfs.c diff --git a/tools/lib/lk/debugfs.h b/tools/lib/api/fs/debugfs.h similarity index 86% rename from tools/lib/lk/debugfs.h rename to tools/lib/api/fs/debugfs.h index 935c59bdb442..f19d3df9609d 100644 --- a/tools/lib/lk/debugfs.h +++ b/tools/lib/api/fs/debugfs.h @@ -1,5 +1,5 @@ -#ifndef __LK_DEBUGFS_H__ -#define __LK_DEBUGFS_H__ +#ifndef __API_DEBUGFS_H__ +#define __API_DEBUGFS_H__ #define _STR(x) #x #define STR(x) _STR(x) @@ -26,4 +26,4 @@ char *debugfs_mount(const char *mountpoint); extern char debugfs_mountpoint[]; -#endif /* __LK_DEBUGFS_H__ */ +#endif /* __API_DEBUGFS_H__ */ diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c new file mode 100644 index 000000000000..18bc271a4bbc --- /dev/null +++ b/tools/lib/symbol/kallsyms.c @@ -0,0 +1,58 @@ +#include "symbol/kallsyms.h" +#include +#include + +int kallsyms__parse(const char *filename, void *arg, + int (*process_symbol)(void *arg, const char *name, + char type, u64 start)) +{ + char *line = NULL; + size_t n; + int err = -1; + FILE *file = fopen(filename, "r"); + + if (file == NULL) + goto out_failure; + + err = 0; + + while (!feof(file)) { + u64 start; + int line_len, len; + char symbol_type; + char *symbol_name; + + line_len = getline(&line, &n, file); + if (line_len < 0 || !line) + break; + + line[--line_len] = '\0'; /* \n */ + + len = hex2u64(line, &start); + + len++; + if (len + 2 >= line_len) + continue; + + symbol_type = line[len]; + len += 2; + symbol_name = line + len; + len = line_len - len; + + if (len >= KSYM_NAME_LEN) { + err = -1; + break; + } + + err = process_symbol(arg, symbol_name, symbol_type, start); + if (err) + break; + } + + free(line); + fclose(file); + return err; + +out_failure: + return -1; +} diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h new file mode 100644 index 000000000000..6084f5e18b3c --- /dev/null +++ b/tools/lib/symbol/kallsyms.h @@ -0,0 +1,24 @@ +#ifndef __TOOLS_KALLSYMS_H_ +#define __TOOLS_KALLSYMS_H_ 1 + +#include +#include +#include + +#ifndef KSYM_NAME_LEN +#define KSYM_NAME_LEN 256 +#endif + +static inline u8 kallsyms2elf_type(char type) +{ + if (type == 'W') + return STB_WEAK; + + return isupper(type) ? STB_GLOBAL : STB_LOCAL; +} + +int kallsyms__parse(const char *filename, void *arg, + int (*process_symbol)(void *arg, const char *name, + char type, u64 start)); + +#endif /* __TOOLS_KALLSYMS_H_ */ diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index fc1502098595..56d52a33a3df 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -43,6 +43,32 @@ man_dir_SQ = '$(subst ','\'',$(man_dir))' export man_dir man_dir_SQ INSTALL export DESTDIR DESTDIR_SQ +set_plugin_dir := 1 + +# Set plugin_dir to preffered global plugin location +# If we install under $HOME directory we go under +# $(HOME)/.traceevent/plugins +# +# We dont set PLUGIN_DIR in case we install under $HOME +# directory, because by default the code looks under: +# $(HOME)/.traceevent/plugins by default. +# +ifeq ($(plugin_dir),) +ifeq ($(prefix),$(HOME)) +override plugin_dir = $(HOME)/.traceevent/plugins +set_plugin_dir := 0 +else +override plugin_dir = $(prefix)/lib/traceevent/plugins +endif +endif + +ifeq ($(set_plugin_dir),1) +PLUGIN_DIR = -DPLUGIN_DIR="$(DESTDIR)/$(plugin_dir)" +PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' +endif + +include $(if $(BUILD_SRC),$(BUILD_SRC)/)../../scripts/Makefile.include + # copy a bit from Linux kbuild ifeq ("$(origin V)", "command line") @@ -57,18 +83,13 @@ ifeq ("$(origin O)", "command line") endif ifeq ($(BUILD_SRC),) -ifneq ($(BUILD_OUTPUT),) +ifneq ($(OUTPUT),) define build_output - $(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ - BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 + $(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \ + BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1 endef -saved-output := $(BUILD_OUTPUT) -BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd) -$(if $(BUILD_OUTPUT),, \ - $(error output directory "$(saved-output)" does not exist)) - all: sub-make $(MAKECMDGOALS): sub-make @@ -80,7 +101,7 @@ sub-make: force # Leave processing to above invocation of make skip-makefile := 1 -endif # BUILD_OUTPUT +endif # OUTPUT endif # BUILD_SRC # We process the rest of the Makefile if this is the final invocation of make @@ -96,6 +117,7 @@ export prefix bindir src obj # Shell quotes bindir_SQ = $(subst ','\'',$(bindir)) bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) +plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) LIB_FILE = libtraceevent.a libtraceevent.so @@ -114,7 +136,7 @@ export Q VERBOSE EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) -INCLUDES = -I. $(CONFIG_INCLUDES) +INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES) # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -Wall @@ -125,41 +147,14 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = - print_compile = - print_app_build = - print_fpic_compile = - print_shared_lib_compile = - print_plugin_obj_compile = - print_plugin_build = - print_install = else Q = @ - print_compile = echo ' CC '$(OBJ); - print_app_build = echo ' BUILD '$(OBJ); - print_fpic_compile = echo ' CC FPIC '$(OBJ); - print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_obj_compile = echo ' BUILD PLUGIN OBJ '$(OBJ); - print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); - print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); - print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; endif -do_fpic_compile = \ - ($(print_fpic_compile) \ - $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@) - -do_app_build = \ - ($(print_app_build) \ - $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS)) - do_compile_shared_library = \ ($(print_shared_lib_compile) \ $(CC) --shared $^ -o $@) -do_compile_plugin_obj = \ - ($(print_plugin_obj_compile) \ - $(CC) -c $(CFLAGS) -fPIC -o $@ $<) - do_plugin_build = \ ($(print_plugin_build) \ $(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<) @@ -169,23 +164,37 @@ do_build_static_lib = \ $(RM) $@; $(AR) rcs $@ $^) -define do_compile - $(print_compile) \ - $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@; -endef +do_compile = $(QUIET_CC)$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@; $(obj)/%.o: $(src)/%.c - $(Q)$(call do_compile) + $(call do_compile) %.o: $(src)/%.c - $(Q)$(call do_compile) + $(call do_compile) -PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o +PEVENT_LIB_OBJS = event-parse.o +PEVENT_LIB_OBJS += event-plugin.o +PEVENT_LIB_OBJS += trace-seq.o +PEVENT_LIB_OBJS += parse-filter.o +PEVENT_LIB_OBJS += parse-utils.o PEVENT_LIB_OBJS += kbuffer-parse.o -ALL_OBJS = $(PEVENT_LIB_OBJS) +PLUGIN_OBJS = plugin_jbd2.o +PLUGIN_OBJS += plugin_hrtimer.o +PLUGIN_OBJS += plugin_kmem.o +PLUGIN_OBJS += plugin_kvm.o +PLUGIN_OBJS += plugin_mac80211.o +PLUGIN_OBJS += plugin_sched_switch.o +PLUGIN_OBJS += plugin_function.o +PLUGIN_OBJS += plugin_xen.o +PLUGIN_OBJS += plugin_scsi.o +PLUGIN_OBJS += plugin_cfg80211.o -CMD_TARGETS = $(LIB_FILE) +PLUGINS := $(PLUGIN_OBJS:.o=.so) + +ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS) + +CMD_TARGETS = $(LIB_FILE) $(PLUGINS) TARGETS = $(CMD_TARGETS) @@ -195,32 +204,40 @@ all: all_cmd all_cmd: $(CMD_TARGETS) libtraceevent.so: $(PEVENT_LIB_OBJS) - $(Q)$(do_compile_shared_library) + $(QUIET_LINK)$(CC) --shared $^ -o $@ libtraceevent.a: $(PEVENT_LIB_OBJS) - $(Q)$(do_build_static_lib) + $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^ + +plugins: $(PLUGINS) $(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS - $(Q)$(do_fpic_compile) + $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@ + +$(PLUGIN_OBJS): %.o : $(src)/%.c + $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $< + +$(PLUGINS): %.so: %.o + $(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $< define make_version.h - (echo '/* This file is automatically generated. Do not modify. */'; \ - echo \#define VERSION_CODE $(shell \ - expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ - echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ - echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ - echo '#define FILE_VERSION '$(FILE_VERSION); \ - ) > $1 + (echo '/* This file is automatically generated. Do not modify. */'; \ + echo \#define VERSION_CODE $(shell \ + expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ + echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ + echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ + echo '#define FILE_VERSION '$(FILE_VERSION); \ + ) > $1 endef define update_version.h - ($(call make_version.h, $@.tmp); \ - if [ -r $@ ] && cmp -s $@ $@.tmp; then \ - rm -f $@.tmp; \ - else \ - echo ' UPDATE $@'; \ - mv -f $@.tmp $@; \ - fi); + ($(call make_version.h, $@.tmp); \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); endef ep_version.h: force @@ -229,13 +246,13 @@ ep_version.h: force VERSION_FILES = ep_version.h define update_dir - (echo $1 > $@.tmp; \ - if [ -r $@ ] && cmp -s $@ $@.tmp; then \ - rm -f $@.tmp; \ - else \ - echo ' UPDATE $@'; \ - mv -f $@.tmp $@; \ - fi); + (echo $1 > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); endef ## make deps @@ -245,10 +262,10 @@ all_deps := $(all_objs:%.o=.%.d) # let .d file also depends on the source and header files define check_deps - @set -e; $(RM) $@; \ - $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ - sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ - $(RM) $@.$$$$ + @set -e; $(RM) $@; \ + $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + $(RM) $@.$$$$ endef $(all_deps): .%.d: $(src)/%.c @@ -283,27 +300,41 @@ TAGS: force --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' define do_install - $(print_install) \ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ fi; \ $(INSTALL) $1 '$(DESTDIR_SQ)$2' endef -install_lib: all_cmd - $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) +define do_install_plugins + for plugin in $1; do \ + $(call do_install,$$plugin,$(plugin_dir_SQ)); \ + done +endef + +install_lib: all_cmd install_plugins + $(call QUIET_INSTALL, $(LIB_FILE)) \ + $(call do_install,$(LIB_FILE),$(bindir_SQ)) + +install_plugins: $(PLUGINS) + $(call QUIET_INSTALL, trace_plugins) \ + $(call do_install_plugins, $(PLUGINS)) install: install_lib clean: - $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d - $(RM) TRACEEVENT-CFLAGS tags TAGS + $(call QUIET_CLEAN, libtraceevent) \ + $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ + $(RM) TRACEEVENT-CFLAGS tags TAGS endif # skip-makefile -PHONY += force +PHONY += force plugins force: +plugins: + @echo > /dev/null + # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 217c82ee3665..1587ea392ad6 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -2710,7 +2710,6 @@ process_func_handler(struct event_format *event, struct pevent_function_handler struct print_arg *farg; enum event_type type; char *token; - const char *test; int i; arg->type = PRINT_FUNC; @@ -2727,15 +2726,19 @@ process_func_handler(struct event_format *event, struct pevent_function_handler } type = process_arg(event, farg, &token); - if (i < (func->nr_args - 1)) - test = ","; - else - test = ")"; - - if (test_type_token(type, token, EVENT_DELIM, test)) { - free_arg(farg); - free_token(token); - return EVENT_ERROR; + if (i < (func->nr_args - 1)) { + if (type != EVENT_DELIM || strcmp(token, ",") != 0) { + warning("Error: function '%s()' expects %d arguments but event %s only uses %d", + func->name, func->nr_args, + event->name, i + 1); + goto err; + } + } else { + if (type != EVENT_DELIM || strcmp(token, ")") != 0) { + warning("Error: function '%s()' only expects %d arguments but event %s has more", + func->name, func->nr_args, event->name); + goto err; + } } *next_arg = farg; @@ -2747,6 +2750,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler *tok = token; return type; + +err: + free_arg(farg); + free_token(token); + return EVENT_ERROR; } static enum event_type @@ -4099,6 +4107,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event unsigned long long val; struct func_map *func; const char *saveptr; + struct trace_seq p; char *bprint_fmt = NULL; char format[32]; int show_func; @@ -4306,8 +4315,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event format[len] = 0; if (!len_as_arg) len_arg = -1; - print_str_arg(s, data, size, event, + /* Use helper trace_seq */ + trace_seq_init(&p); + print_str_arg(&p, data, size, event, format, len_arg, arg); + trace_seq_terminate(&p); + trace_seq_puts(s, p.buffer); arg = arg->next; break; default: @@ -5116,8 +5129,38 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, return ret; } +static enum pevent_errno +__pevent_parse_event(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, + const char *sys) +{ + int ret = __pevent_parse_format(eventp, pevent, buf, size, sys); + struct event_format *event = *eventp; + + if (event == NULL) + return ret; + + if (pevent && add_event(pevent, event)) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_add_failed; + } + +#define PRINT_ARGS 0 + if (PRINT_ARGS && event->print_fmt.args) + print_args(event->print_fmt.args); + + return 0; + +event_add_failed: + pevent_free_format(event); + return ret; +} + /** * pevent_parse_format - parse the event format + * @pevent: the handle to the pevent + * @eventp: returned format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to @@ -5129,10 +5172,12 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, * * /sys/kernel/debug/tracing/events/.../.../format */ -enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, const char *sys) { - return __pevent_parse_format(eventp, NULL, buf, size, sys); + return __pevent_parse_event(pevent, eventp, buf, size, sys); } /** @@ -5153,25 +5198,7 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event = NULL; - int ret = __pevent_parse_format(&event, pevent, buf, size, sys); - - if (event == NULL) - return ret; - - if (add_event(pevent, event)) { - ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; - goto event_add_failed; - } - -#define PRINT_ARGS 0 - if (PRINT_ARGS && event->print_fmt.args) - print_args(event->print_fmt.args); - - return 0; - -event_add_failed: - pevent_free_format(event); - return ret; + return __pevent_parse_event(pevent, &event, buf, size, sys); } #undef _PE @@ -5203,22 +5230,7 @@ int pevent_strerror(struct pevent *pevent __maybe_unused, idx = errnum - __PEVENT_ERRNO__START - 1; msg = pevent_error_str[idx]; - - switch (errnum) { - case PEVENT_ERRNO__MEM_ALLOC_FAILED: - case PEVENT_ERRNO__PARSE_EVENT_FAILED: - case PEVENT_ERRNO__READ_ID_FAILED: - case PEVENT_ERRNO__READ_FORMAT_FAILED: - case PEVENT_ERRNO__READ_PRINT_FAILED: - case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: - case PEVENT_ERRNO__INVALID_ARG_TYPE: - snprintf(buf, buflen, "%s", msg); - break; - - default: - /* cannot reach here */ - break; - } + snprintf(buf, buflen, "%s", msg); return 0; } @@ -5548,6 +5560,52 @@ int pevent_register_print_function(struct pevent *pevent, return ret; } +/** + * pevent_unregister_print_function - unregister a helper function + * @pevent: the handle to the pevent + * @func: the function to process the helper function + * @name: the name of the helper function + * + * This function removes existing print handler for function @name. + * + * Returns 0 if the handler was removed successully, -1 otherwise. + */ +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name) +{ + struct pevent_function_handler *func_handle; + + func_handle = find_func_handler(pevent, name); + if (func_handle && func_handle->func == func) { + remove_func_handler(pevent, name); + return 0; + } + return -1; +} + +static struct event_format *pevent_search_event(struct pevent *pevent, int id, + const char *sys_name, + const char *event_name) +{ + struct event_format *event; + + if (id >= 0) { + /* search by id */ + event = pevent_find_event(pevent, id); + if (!event) + return NULL; + if (event_name && (strcmp(event_name, event->name) != 0)) + return NULL; + if (sys_name && (strcmp(sys_name, event->system) != 0)) + return NULL; + } else { + event = pevent_find_event_by_name(pevent, sys_name, event_name); + if (!event) + return NULL; + } + return event; +} + /** * pevent_register_event_handler - register a way to parse an event * @pevent: the handle to the pevent @@ -5572,20 +5630,9 @@ int pevent_register_event_handler(struct pevent *pevent, int id, struct event_format *event; struct event_handler *handle; - if (id >= 0) { - /* search by id */ - event = pevent_find_event(pevent, id); - if (!event) - goto not_found; - if (event_name && (strcmp(event_name, event->name) != 0)) - goto not_found; - if (sys_name && (strcmp(sys_name, event->system) != 0)) - goto not_found; - } else { - event = pevent_find_event_by_name(pevent, sys_name, event_name); - if (!event) - goto not_found; - } + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); @@ -5625,6 +5672,79 @@ int pevent_register_event_handler(struct pevent *pevent, int id, return -1; } +static int handle_matches(struct event_handler *handler, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + if (id >= 0 && id != handler->id) + return 0; + + if (event_name && (strcmp(event_name, handler->event_name) != 0)) + return 0; + + if (sys_name && (strcmp(sys_name, handler->sys_name) != 0)) + return 0; + + if (func != handler->func || context != handler->context) + return 0; + + return 1; +} + +/** + * pevent_unregister_event_handler - unregister an existing event handler + * @pevent: the handle to the pevent + * @id: the id of the event to unregister + * @sys_name: the system name the handler belongs to + * @event_name: the name of the event handler + * @func: the function to call to parse the event information + * @context: the data to be passed to @func + * + * This function removes existing event handler (parser). + * + * If @id is >= 0, then it is used to find the event. + * else @sys_name and @event_name are used. + * + * Returns 0 if handler was removed successfully, -1 if event was not found. + */ +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + struct event_format *event; + struct event_handler *handle; + struct event_handler **next; + + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; + + if (event->handler == func && event->context == context) { + pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.", + event->id, event->system, event->name); + + event->handler = NULL; + event->context = NULL; + return 0; + } + +not_found: + for (next = &pevent->handlers; *next; next = &(*next)->next) { + handle = *next; + if (handle_matches(handle, id, sys_name, event_name, + func, context)) + break; + } + + if (!(*next)) + return -1; + + *next = handle->next; + free_handler(handle); + + return 0; +} + /** * pevent_alloc - create a pevent handle */ diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 8d73d2594f65..791c539374c7 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) @@ -57,6 +58,12 @@ struct pevent_record { #endif }; +enum trace_seq_fail { + TRACE_SEQ__GOOD, + TRACE_SEQ__BUFFER_POISONED, + TRACE_SEQ__MEM_ALLOC_FAILED, +}; + /* * Trace sequences are used to allow a function to call several other functions * to create a string of data to use (up to a max of PAGE_SIZE). @@ -67,6 +74,7 @@ struct trace_seq { unsigned int buffer_size; unsigned int len; unsigned int readpos; + enum trace_seq_fail state; }; void trace_seq_init(struct trace_seq *s); @@ -97,7 +105,7 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, void *context); typedef int (*pevent_plugin_load_func)(struct pevent *pevent); -typedef int (*pevent_plugin_unload_func)(void); +typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); struct plugin_option { struct plugin_option *next; @@ -122,7 +130,7 @@ struct plugin_option { * PEVENT_PLUGIN_UNLOADER: (optional) * The function called just before unloading * - * int PEVENT_PLUGIN_UNLOADER(void) + * int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) * * PEVENT_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading @@ -355,12 +363,35 @@ enum pevent_flag { _PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ - _PE(INVALID_ARG_TYPE, "invalid argument type") + _PE(INVALID_ARG_TYPE, "invalid argument type"), \ + _PE(INVALID_EXP_TYPE, "invalid expression type"), \ + _PE(INVALID_OP_TYPE, "invalid operator type"), \ + _PE(INVALID_EVENT_NAME, "invalid event name"), \ + _PE(EVENT_NOT_FOUND, "no event found"), \ + _PE(SYNTAX_ERROR, "syntax error"), \ + _PE(ILLEGAL_RVALUE, "illegal rvalue"), \ + _PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \ + _PE(INVALID_REGEX, "regex did not compute"), \ + _PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \ + _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \ + _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ + _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ + _PE(BAD_FILTER_ARG, "bad arg in filter tree"), \ + _PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \ + _PE(ILLEGAL_TOKEN, "illegal token"), \ + _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ + _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ + _PE(UNKNOWN_TOKEN, "unknown token"), \ + _PE(FILTER_NOT_FOUND, "no filter found"), \ + _PE(NOT_A_NUMBER, "must have number field"), \ + _PE(NO_FILTER, "no filters exists"), \ + _PE(FILTER_MISS, "record does not match to filter") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code enum pevent_errno { PEVENT_ERRNO__SUCCESS = 0, + PEVENT_ERRNO__FILTER_MATCH = PEVENT_ERRNO__SUCCESS, /* * Choose an arbitrary negative big number not to clash with standard @@ -377,6 +408,12 @@ enum pevent_errno { }; #undef _PE +struct plugin_list; + +struct plugin_list *traceevent_load_plugins(struct pevent *pevent); +void traceevent_unload_plugins(struct plugin_list *plugin_list, + struct pevent *pevent); + struct cmdline; struct cmdline_list; struct func_map; @@ -522,6 +559,15 @@ __data2host8(struct pevent *pevent, unsigned long long data) __data2host8(pevent, __val); \ }) +static inline int traceevent_host_bigendian(void) +{ + unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; + unsigned int val; + + memcpy(&val, str, 4); + return val == 0x01020304; +} + /* taken from kernel/trace/trace.h */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, @@ -547,7 +593,9 @@ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long siz enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); -enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, const char *sys); void pevent_free_format(struct event_format *event); @@ -576,10 +624,15 @@ int pevent_print_func_field(struct trace_seq *s, const char *fmt, int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context); int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...); +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name); struct format_field *pevent_find_common_field(struct event_format *event, const char *name); struct format_field *pevent_find_field(struct event_format *event, const char *name); @@ -811,18 +864,22 @@ struct filter_type { struct filter_arg *filter; }; +#define PEVENT_FILTER_ERROR_BUFSZ 1024 + struct event_filter { struct pevent *pevent; int filters; struct filter_type *event_filters; + char error_buffer[PEVENT_FILTER_ERROR_BUFSZ]; }; struct event_filter *pevent_filter_alloc(struct pevent *pevent); -#define FILTER_NONE -2 -#define FILTER_NOEXIST -1 -#define FILTER_MISS 0 -#define FILTER_MATCH 1 +/* for backward compatibility */ +#define FILTER_NONE PEVENT_ERRNO__FILTER_NOT_FOUND +#define FILTER_NOEXIST PEVENT_ERRNO__NO_FILTER +#define FILTER_MISS PEVENT_ERRNO__FILTER_MISS +#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH enum filter_trivial_type { FILTER_TRIVIAL_FALSE, @@ -830,20 +887,21 @@ enum filter_trivial_type { FILTER_TRIVIAL_BOTH, }; -int pevent_filter_add_filter_str(struct event_filter *filter, - const char *filter_str, - char **error_str); +enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str); +enum pevent_errno pevent_filter_match(struct event_filter *filter, + struct pevent_record *record); -int pevent_filter_match(struct event_filter *filter, - struct pevent_record *record); +int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, + char *buf, size_t buflen); int pevent_event_filtered(struct event_filter *filter, int event_id); void pevent_filter_reset(struct event_filter *filter); -void pevent_filter_clear_trivial(struct event_filter *filter, +int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type); void pevent_filter_free(struct event_filter *filter); diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c new file mode 100644 index 000000000000..0c8bf6780e4d --- /dev/null +++ b/tools/lib/traceevent/event-plugin.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "event-parse.h" +#include "event-utils.h" + +#define LOCAL_PLUGIN_DIR ".traceevent/plugins" + +struct plugin_list { + struct plugin_list *next; + char *name; + void *handle; +}; + +static void +load_plugin(struct pevent *pevent, const char *path, + const char *file, void *data) +{ + struct plugin_list **plugin_list = data; + pevent_plugin_load_func func; + struct plugin_list *list; + const char *alias; + char *plugin; + void *handle; + + plugin = malloc(strlen(path) + strlen(file) + 2); + if (!plugin) { + warning("could not allocate plugin memory\n"); + return; + } + + strcpy(plugin, path); + strcat(plugin, "/"); + strcat(plugin, file); + + handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL); + if (!handle) { + warning("could not load plugin '%s'\n%s\n", + plugin, dlerror()); + goto out_free; + } + + alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME); + if (!alias) + alias = file; + + func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME); + if (!func) { + warning("could not find func '%s' in plugin '%s'\n%s\n", + PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror()); + goto out_free; + } + + list = malloc(sizeof(*list)); + if (!list) { + warning("could not allocate plugin memory\n"); + goto out_free; + } + + list->next = *plugin_list; + list->handle = handle; + list->name = plugin; + *plugin_list = list; + + pr_stat("registering plugin: %s", plugin); + func(pevent); + return; + + out_free: + free(plugin); +} + +static void +load_plugins_dir(struct pevent *pevent, const char *suffix, + const char *path, + void (*load_plugin)(struct pevent *pevent, + const char *path, + const char *name, + void *data), + void *data) +{ + struct dirent *dent; + struct stat st; + DIR *dir; + int ret; + + ret = stat(path, &st); + if (ret < 0) + return; + + if (!S_ISDIR(st.st_mode)) + return; + + dir = opendir(path); + if (!dir) + return; + + while ((dent = readdir(dir))) { + const char *name = dent->d_name; + + if (strcmp(name, ".") == 0 || + strcmp(name, "..") == 0) + continue; + + /* Only load plugins that end in suffix */ + if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0) + continue; + + load_plugin(pevent, path, name, data); + } + + closedir(dir); +} + +static void +load_plugins(struct pevent *pevent, const char *suffix, + void (*load_plugin)(struct pevent *pevent, + const char *path, + const char *name, + void *data), + void *data) +{ + char *home; + char *path; + char *envdir; + + /* + * If a system plugin directory was defined, + * check that first. + */ +#ifdef PLUGIN_DIR + load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data); +#endif + + /* + * Next let the environment-set plugin directory + * override the system defaults. + */ + envdir = getenv("TRACEEVENT_PLUGIN_DIR"); + if (envdir) + load_plugins_dir(pevent, suffix, envdir, load_plugin, data); + + /* + * Now let the home directory override the environment + * or system defaults. + */ + home = getenv("HOME"); + if (!home) + return; + + path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); + if (!path) { + warning("could not allocate plugin memory\n"); + return; + } + + strcpy(path, home); + strcat(path, "/"); + strcat(path, LOCAL_PLUGIN_DIR); + + load_plugins_dir(pevent, suffix, path, load_plugin, data); + + free(path); +} + +struct plugin_list* +traceevent_load_plugins(struct pevent *pevent) +{ + struct plugin_list *list = NULL; + + load_plugins(pevent, ".so", load_plugin, &list); + return list; +} + +void +traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent) +{ + pevent_plugin_unload_func func; + struct plugin_list *list; + + while (plugin_list) { + list = plugin_list; + plugin_list = list->next; + func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME); + if (func) + func(pevent); + dlclose(list->handle); + free(list->name); + free(list); + } +} diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h index e76c9acb92cd..d1dc2170e402 100644 --- a/tools/lib/traceevent/event-utils.h +++ b/tools/lib/traceevent/event-utils.h @@ -23,18 +23,14 @@ #include /* Can be overridden */ -void die(const char *fmt, ...); -void *malloc_or_die(unsigned int size); void warning(const char *fmt, ...); void pr_stat(const char *fmt, ...); void vpr_stat(const char *fmt, va_list ap); /* Always available */ -void __die(const char *fmt, ...); void __warning(const char *fmt, ...); void __pr_stat(const char *fmt, ...); -void __vdie(const char *fmt, ...); void __vwarning(const char *fmt, ...); void __vpr_stat(const char *fmt, ...); diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 2500e75583fc..b50234402fc2 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -38,41 +38,31 @@ struct event_list { struct event_format *event; }; -#define MAX_ERR_STR_SIZE 256 - -static void show_error(char **error_str, const char *fmt, ...) +static void show_error(char *error_buf, const char *fmt, ...) { unsigned long long index; const char *input; - char *error; va_list ap; int len; int i; - if (!error_str) - return; - input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; - error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); - if (len) { - strcpy(error, input); - error[len] = '\n'; + strcpy(error_buf, input); + error_buf[len] = '\n'; for (i = 1; i < len && i < index; i++) - error[len+i] = ' '; - error[len + i] = '^'; - error[len + i + 1] = '\n'; + error_buf[len+i] = ' '; + error_buf[len + i] = '^'; + error_buf[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); - vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap); + vsnprintf(error_buf + len, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap); va_end(ap); - - *error_str = error; } static void free_token(char *token) @@ -95,7 +85,11 @@ static enum event_type read_token(char **tok) (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ - *tok = malloc_or_die(3); + *tok = malloc(3); + if (*tok == NULL) { + free_token(token); + return EVENT_ERROR; + } sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ @@ -147,11 +141,13 @@ add_filter_type(struct event_filter *filter, int id) if (filter_type) return filter_type; - filter->event_filters = realloc(filter->event_filters, - sizeof(*filter->event_filters) * - (filter->filters + 1)); - if (!filter->event_filters) - die("Could not allocate filter"); + filter_type = realloc(filter->event_filters, + sizeof(*filter->event_filters) * + (filter->filters + 1)); + if (!filter_type) + return NULL; + + filter->event_filters = filter_type; for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) @@ -182,7 +178,10 @@ struct event_filter *pevent_filter_alloc(struct pevent *pevent) { struct event_filter *filter; - filter = malloc_or_die(sizeof(*filter)); + filter = malloc(sizeof(*filter)); + if (filter == NULL) + return NULL; + memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); @@ -192,12 +191,7 @@ struct event_filter *pevent_filter_alloc(struct pevent *pevent) static struct filter_arg *allocate_arg(void) { - struct filter_arg *arg; - - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - - return arg; + return calloc(1, sizeof(struct filter_arg)); } static void free_arg(struct filter_arg *arg) @@ -242,15 +236,19 @@ static void free_arg(struct filter_arg *arg) free(arg); } -static void add_event(struct event_list **events, +static int add_event(struct event_list **events, struct event_format *event) { struct event_list *list; - list = malloc_or_die(sizeof(*list)); + list = malloc(sizeof(*list)); + if (list == NULL) + return -1; + list->next = *events; *events = list; list->event = event; + return 0; } static int event_match(struct event_format *event, @@ -265,7 +263,7 @@ static int event_match(struct event_format *event, !regexec(ereg, event->name, 0, NULL, 0); } -static int +static enum pevent_errno find_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name) { @@ -273,6 +271,7 @@ find_event(struct pevent *pevent, struct event_list **events, regex_t ereg; regex_t sreg; int match = 0; + int fail = 0; char *reg; int ret; int i; @@ -283,23 +282,31 @@ find_event(struct pevent *pevent, struct event_list **events, sys_name = NULL; } - reg = malloc_or_die(strlen(event_name) + 3); + reg = malloc(strlen(event_name) + 3); + if (reg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) - return -1; + return PEVENT_ERRNO__INVALID_EVENT_NAME; if (sys_name) { - reg = malloc_or_die(strlen(sys_name) + 3); + reg = malloc(strlen(sys_name) + 3); + if (reg == NULL) { + regfree(&ereg); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); - return -1; + return PEVENT_ERRNO__INVALID_EVENT_NAME; } } @@ -307,7 +314,10 @@ find_event(struct pevent *pevent, struct event_list **events, event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; - add_event(events, event); + if (add_event(events, event) < 0) { + fail = 1; + break; + } } } @@ -316,7 +326,9 @@ find_event(struct pevent *pevent, struct event_list **events, regfree(&sreg); if (!match) - return -1; + return PEVENT_ERRNO__EVENT_NOT_FOUND; + if (fail) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; return 0; } @@ -332,14 +344,18 @@ static void free_events(struct event_list *events) } } -static struct filter_arg * +static enum pevent_errno create_arg_item(struct event_format *event, const char *token, - enum event_type type, char **error_str) + enum event_type type, struct filter_arg **parg, char *error_str) { struct format_field *field; struct filter_arg *arg; arg = allocate_arg(); + if (arg == NULL) { + show_error(error_str, "failed to allocate filter arg"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } switch (type) { @@ -349,8 +365,11 @@ create_arg_item(struct event_format *event, const char *token, arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); - if (!arg->value.str) - die("malloc string"); + if (!arg->value.str) { + free_arg(arg); + show_error(error_str, "failed to allocate string filter arg"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } break; case EVENT_ITEM: /* if it is a number, then convert it */ @@ -377,11 +396,11 @@ create_arg_item(struct event_format *event, const char *token, break; default: free_arg(arg); - show_error(error_str, "expected a value but found %s", - token); - return NULL; + show_error(error_str, "expected a value but found %s", token); + return PEVENT_ERRNO__UNEXPECTED_TYPE; } - return arg; + *parg = arg; + return 0; } static struct filter_arg * @@ -390,6 +409,9 @@ create_arg_op(enum filter_op_type btype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + arg->type = FILTER_ARG_OP; arg->op.type = btype; @@ -402,6 +424,9 @@ create_arg_exp(enum filter_exp_type etype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + arg->type = FILTER_ARG_EXP; arg->op.type = etype; @@ -414,6 +439,9 @@ create_arg_cmp(enum filter_exp_type etype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; @@ -421,8 +449,8 @@ create_arg_cmp(enum filter_exp_type etype) return arg; } -static int add_right(struct filter_arg *op, struct filter_arg *arg, - char **error_str) +static enum pevent_errno +add_right(struct filter_arg *op, struct filter_arg *arg, char *error_str) { struct filter_arg *left; char *str; @@ -453,9 +481,8 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, case FILTER_ARG_FIELD: break; default: - show_error(error_str, - "Illegal rvalue"); - return -1; + show_error(error_str, "Illegal rvalue"); + return PEVENT_ERRNO__ILLEGAL_RVALUE; } /* @@ -502,7 +529,7 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, if (left->type != FILTER_ARG_FIELD) { show_error(error_str, "Illegal lvalue for string comparison"); - return -1; + return PEVENT_ERRNO__ILLEGAL_LVALUE; } /* Make sure this is a valid string compare */ @@ -521,25 +548,31 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, show_error(error_str, "RegEx '%s' did not compute", str); - return -1; + return PEVENT_ERRNO__INVALID_REGEX; } break; default: show_error(error_str, "Illegal comparison for string"); - return -1; + return PEVENT_ERRNO__ILLEGAL_STRING_CMP; } op->type = FILTER_ARG_STR; op->str.type = op_type; op->str.field = left->field.field; op->str.val = strdup(str); - if (!op->str.val) - die("malloc string"); + if (!op->str.val) { + show_error(error_str, "Failed to allocate string filter"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } /* * Need a buffer to copy data for tests */ - op->str.buffer = malloc_or_die(op->str.field->size + 1); + op->str.buffer = malloc(op->str.field->size + 1); + if (!op->str.buffer) { + show_error(error_str, "Failed to allocate string filter"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } /* Null terminate this buffer */ op->str.buffer[op->str.field->size] = 0; @@ -557,7 +590,7 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, case FILTER_CMP_NOT_REGEX: show_error(error_str, "Op not allowed with integers"); - return -1; + return PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; default: break; @@ -577,9 +610,8 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, return 0; out_fail: - show_error(error_str, - "Syntax error"); - return -1; + show_error(error_str, "Syntax error"); + return PEVENT_ERRNO__SYNTAX_ERROR; } static struct filter_arg * @@ -592,7 +624,7 @@ rotate_op_right(struct filter_arg *a, struct filter_arg *b) return arg; } -static int add_left(struct filter_arg *op, struct filter_arg *arg) +static enum pevent_errno add_left(struct filter_arg *op, struct filter_arg *arg) { switch (op->type) { case FILTER_ARG_EXP: @@ -611,11 +643,11 @@ static int add_left(struct filter_arg *op, struct filter_arg *arg) /* left arg of compares must be a field */ if (arg->type != FILTER_ARG_FIELD && arg->type != FILTER_ARG_BOOLEAN) - return -1; + return PEVENT_ERRNO__INVALID_ARG_TYPE; op->num.left = arg; break; default: - return -1; + return PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } @@ -728,15 +760,18 @@ enum filter_vals { FILTER_VAL_TRUE, }; -void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, - struct filter_arg *arg) +static enum pevent_errno +reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, + struct filter_arg *arg, char *error_str) { struct filter_arg *other_child; struct filter_arg **ptr; if (parent->type != FILTER_ARG_OP && - arg->type != FILTER_ARG_OP) - die("can not reparent other than OP"); + arg->type != FILTER_ARG_OP) { + show_error(error_str, "can not reparent other than OP"); + return PEVENT_ERRNO__REPARENT_NOT_OP; + } /* Get the sibling */ if (old_child->op.right == arg) { @@ -745,8 +780,10 @@ void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, } else if (old_child->op.left == arg) { ptr = &old_child->op.left; other_child = old_child->op.right; - } else - die("Error in reparent op, find other child"); + } else { + show_error(error_str, "Error in reparent op, find other child"); + return PEVENT_ERRNO__REPARENT_FAILED; + } /* Detach arg from old_child */ *ptr = NULL; @@ -757,23 +794,29 @@ void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, *parent = *arg; /* Free arg without recussion */ free(arg); - return; + return 0; } if (parent->op.right == old_child) ptr = &parent->op.right; else if (parent->op.left == old_child) ptr = &parent->op.left; - else - die("Error in reparent op"); + else { + show_error(error_str, "Error in reparent op"); + return PEVENT_ERRNO__REPARENT_FAILED; + } + *ptr = arg; free_arg(old_child); + return 0; } -enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) +/* Returns either filter_vals (success) or pevent_errno (failfure) */ +static int test_arg(struct filter_arg *parent, struct filter_arg *arg, + char *error_str) { - enum filter_vals lval, rval; + int lval, rval; switch (arg->type) { @@ -788,63 +831,68 @@ enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) return FILTER_VAL_NORM; case FILTER_ARG_EXP: - lval = test_arg(arg, arg->exp.left); + lval = test_arg(arg, arg->exp.left, error_str); if (lval != FILTER_VAL_NORM) return lval; - rval = test_arg(arg, arg->exp.right); + rval = test_arg(arg, arg->exp.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_NUM: - lval = test_arg(arg, arg->num.left); + lval = test_arg(arg, arg->num.left, error_str); if (lval != FILTER_VAL_NORM) return lval; - rval = test_arg(arg, arg->num.right); + rval = test_arg(arg, arg->num.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_OP: if (arg->op.type != FILTER_OP_NOT) { - lval = test_arg(arg, arg->op.left); + lval = test_arg(arg, arg->op.left, error_str); switch (lval) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; - reparent_op_arg(parent, arg, arg->op.right); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.right, + error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; - reparent_op_arg(parent, arg, arg->op.right); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.right, + error_str); + + default: + return lval; } } - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); switch (rval) { case FILTER_VAL_NORM: + default: break; + case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_FALSE; - reparent_op_arg(parent, arg, arg->op.left); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.left, + error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) @@ -852,41 +900,56 @@ enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_TRUE; - reparent_op_arg(parent, arg, arg->op.left); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.left, + error_str); } - return FILTER_VAL_NORM; + return rval; default: - die("bad arg in filter tree"); + show_error(error_str, "bad arg in filter tree"); + return PEVENT_ERRNO__BAD_FILTER_ARG; } return FILTER_VAL_NORM; } /* Remove any unknown event fields */ -static struct filter_arg *collapse_tree(struct filter_arg *arg) +static int collapse_tree(struct filter_arg *arg, + struct filter_arg **arg_collapsed, char *error_str) { - enum filter_vals ret; + int ret; - ret = test_arg(arg, arg); + ret = test_arg(arg, arg, error_str); switch (ret) { case FILTER_VAL_NORM: - return arg; + break; case FILTER_VAL_TRUE: case FILTER_VAL_FALSE: free_arg(arg); arg = allocate_arg(); - arg->type = FILTER_ARG_BOOLEAN; - arg->boolean.value = ret == FILTER_VAL_TRUE; + if (arg) { + arg->type = FILTER_ARG_BOOLEAN; + arg->boolean.value = ret == FILTER_VAL_TRUE; + } else { + show_error(error_str, "Failed to allocate filter arg"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + break; + + default: + /* test_arg() already set the error_str */ + free_arg(arg); + arg = NULL; + break; } - return arg; + *arg_collapsed = arg; + return ret; } -static int +static enum pevent_errno process_filter(struct event_format *event, struct filter_arg **parg, - char **error_str, int not) + char *error_str, int not) { enum event_type type; char *token = NULL; @@ -898,7 +961,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, enum filter_op_type btype; enum filter_exp_type etype; enum filter_cmp_type ctype; - int ret; + enum pevent_errno ret; *parg = NULL; @@ -909,8 +972,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, case EVENT_SQUOTE: case EVENT_DQUOTE: case EVENT_ITEM: - arg = create_arg_item(event, token, type, error_str); - if (!arg) + ret = create_arg_item(event, token, type, &arg, error_str); + if (ret < 0) goto fail; if (!left_item) left_item = arg; @@ -923,20 +986,20 @@ process_filter(struct event_format *event, struct filter_arg **parg, if (not) { arg = NULL; if (current_op) - goto fail_print; + goto fail_syntax; free(token); *parg = current_exp; return 0; } } else - goto fail_print; + goto fail_syntax; arg = NULL; break; case EVENT_DELIM: if (*token == ',') { - show_error(error_str, - "Illegal token ','"); + show_error(error_str, "Illegal token ','"); + ret = PEVENT_ERRNO__ILLEGAL_TOKEN; goto fail; } @@ -944,19 +1007,23 @@ process_filter(struct event_format *event, struct filter_arg **parg, if (left_item) { show_error(error_str, "Open paren can not come after item"); + ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } if (current_exp) { show_error(error_str, "Open paren can not come after expression"); + ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } ret = process_filter(event, &arg, error_str, 0); - if (ret != 1) { - if (ret == 0) + if (ret != PEVENT_ERRNO__UNBALANCED_PAREN) { + if (ret == 0) { show_error(error_str, "Unbalanced number of '('"); + ret = PEVENT_ERRNO__UNBALANCED_PAREN; + } goto fail; } ret = 0; @@ -964,7 +1031,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, /* A not wants just one expression */ if (not) { if (current_op) - goto fail_print; + goto fail_syntax; *parg = arg; return 0; } @@ -979,19 +1046,19 @@ process_filter(struct event_format *event, struct filter_arg **parg, } else { /* ')' */ if (!current_op && !current_exp) - goto fail_print; + goto fail_syntax; /* Make sure everything is finished at this level */ if (current_exp && !check_op_done(current_exp)) - goto fail_print; + goto fail_syntax; if (current_op && !check_op_done(current_op)) - goto fail_print; + goto fail_syntax; if (current_op) *parg = current_op; else *parg = current_exp; - return 1; + return PEVENT_ERRNO__UNBALANCED_PAREN; } break; @@ -1003,21 +1070,22 @@ process_filter(struct event_format *event, struct filter_arg **parg, case OP_BOOL: /* Logic ops need a left expression */ if (!current_exp && !current_op) - goto fail_print; + goto fail_syntax; /* fall through */ case OP_NOT: /* logic only processes ops and exp */ if (left_item) - goto fail_print; + goto fail_syntax; break; case OP_EXP: case OP_CMP: if (!left_item) - goto fail_print; + goto fail_syntax; break; case OP_NONE: show_error(error_str, "Unknown op token %s", token); + ret = PEVENT_ERRNO__UNKNOWN_TOKEN; goto fail; } @@ -1025,6 +1093,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, switch (op_type) { case OP_BOOL: arg = create_arg_op(btype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_left(arg, current_op); else @@ -1035,6 +1105,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, case OP_NOT: arg = create_arg_op(btype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) @@ -1054,6 +1126,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, arg = create_arg_exp(etype); else arg = create_arg_cmp(ctype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); @@ -1062,7 +1136,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, ret = add_left(arg, left_item); if (ret < 0) { arg = NULL; - goto fail_print; + goto fail_syntax; } current_exp = arg; break; @@ -1071,57 +1145,64 @@ process_filter(struct event_format *event, struct filter_arg **parg, } arg = NULL; if (ret < 0) - goto fail_print; + goto fail_syntax; break; case EVENT_NONE: break; + case EVENT_ERROR: + goto fail_alloc; default: - goto fail_print; + goto fail_syntax; } } while (type != EVENT_NONE); if (!current_op && !current_exp) - goto fail_print; + goto fail_syntax; if (!current_op) current_op = current_exp; - current_op = collapse_tree(current_op); + ret = collapse_tree(current_op, parg, error_str); + if (ret < 0) + goto fail; *parg = current_op; return 0; - fail_print: + fail_alloc: + show_error(error_str, "failed to allocate filter arg"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto fail; + fail_syntax: show_error(error_str, "Syntax error"); + ret = PEVENT_ERRNO__SYNTAX_ERROR; fail: free_arg(current_op); free_arg(current_exp); free_arg(arg); free(token); - return -1; + return ret; } -static int +static enum pevent_errno process_event(struct event_format *event, const char *filter_str, - struct filter_arg **parg, char **error_str) + struct filter_arg **parg, char *error_str) { int ret; pevent_buffer_init(filter_str, strlen(filter_str)); ret = process_filter(event, parg, error_str, 0); - if (ret == 1) { - show_error(error_str, - "Unbalanced number of ')'"); - return -1; - } if (ret < 0) return ret; /* If parg is NULL, then make it into FALSE */ if (!*parg) { *parg = allocate_arg(); + if (*parg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + (*parg)->type = FILTER_ARG_BOOLEAN; (*parg)->boolean.value = FILTER_FALSE; } @@ -1129,13 +1210,13 @@ process_event(struct event_format *event, const char *filter_str, return 0; } -static int filter_event(struct event_filter *filter, - struct event_format *event, - const char *filter_str, char **error_str) +static enum pevent_errno +filter_event(struct event_filter *filter, struct event_format *event, + const char *filter_str, char *error_str) { struct filter_type *filter_type; struct filter_arg *arg; - int ret; + enum pevent_errno ret; if (filter_str) { ret = process_event(event, filter_str, &arg, error_str); @@ -1145,11 +1226,17 @@ static int filter_event(struct event_filter *filter, } else { /* just add a TRUE arg */ arg = allocate_arg(); + if (arg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_TRUE; } filter_type = add_filter_type(filter, event->id); + if (filter_type == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + if (filter_type->filter) free_arg(filter_type->filter); filter_type->filter = arg; @@ -1157,22 +1244,24 @@ static int filter_event(struct event_filter *filter, return 0; } +static void filter_init_error_buf(struct event_filter *filter) +{ + /* clear buffer to reset show error */ + pevent_buffer_init("", 0); + filter->error_buffer[0] = '\0'; +} + /** * pevent_filter_add_filter_str - add a new filter * @filter: the event filter to add to * @filter_str: the filter string that contains the filter - * @error_str: string containing reason for failed filter * - * Returns 0 if the filter was successfully added - * -1 if there was an error. - * - * On error, if @error_str points to a string pointer, - * it is set to the reason that the filter failed. - * This string must be freed with "free". + * Returns 0 if the filter was successfully added or a + * negative error code. Use pevent_filter_strerror() to see + * actual error message in case of error. */ -int pevent_filter_add_filter_str(struct event_filter *filter, - const char *filter_str, - char **error_str) +enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str) { struct pevent *pevent = filter->pevent; struct event_list *event; @@ -1183,15 +1272,11 @@ int pevent_filter_add_filter_str(struct event_filter *filter, char *event_name = NULL; char *sys_name = NULL; char *sp; - int rtn = 0; + enum pevent_errno rtn = 0; /* PEVENT_ERRNO__SUCCESS */ int len; int ret; - /* clear buffer to reset show error */ - pevent_buffer_init("", 0); - - if (error_str) - *error_str = NULL; + filter_init_error_buf(filter); filter_start = strchr(filter_str, ':'); if (filter_start) @@ -1199,7 +1284,6 @@ int pevent_filter_add_filter_str(struct event_filter *filter, else len = strlen(filter_str); - do { next_event = strchr(filter_str, ','); if (next_event && @@ -1210,7 +1294,12 @@ int pevent_filter_add_filter_str(struct event_filter *filter, else len = strlen(filter_str); - this_event = malloc_or_die(len + 1); + this_event = malloc(len + 1); + if (this_event == NULL) { + /* This can only happen when events is NULL, but still */ + free_events(events); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } memcpy(this_event, filter_str, len); this_event[len] = 0; @@ -1223,27 +1312,18 @@ int pevent_filter_add_filter_str(struct event_filter *filter, event_name = strtok_r(NULL, "/", &sp); if (!sys_name) { - show_error(error_str, "No filter found"); /* This can only happen when events is NULL, but still */ free_events(events); free(this_event); - return -1; + return PEVENT_ERRNO__FILTER_NOT_FOUND; } /* Find this event */ ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); if (ret < 0) { - if (event_name) - show_error(error_str, - "No event found under '%s.%s'", - sys_name, event_name); - else - show_error(error_str, - "No event found under '%s'", - sys_name); free_events(events); free(this_event); - return -1; + return ret; } free(this_event); } while (filter_str); @@ -1255,7 +1335,7 @@ int pevent_filter_add_filter_str(struct event_filter *filter, /* filter starts here */ for (event = events; event; event = event->next) { ret = filter_event(filter, event->event, filter_start, - error_str); + filter->error_buffer); /* Failures are returned if a parse error happened */ if (ret < 0) rtn = ret; @@ -1263,8 +1343,10 @@ int pevent_filter_add_filter_str(struct event_filter *filter, if (ret >= 0 && pevent->test_filters) { char *test; test = pevent_filter_make_string(filter, event->event->id); - printf(" '%s: %s'\n", event->event->name, test); - free(test); + if (test) { + printf(" '%s: %s'\n", event->event->name, test); + free(test); + } } } @@ -1281,6 +1363,32 @@ static void free_filter_type(struct filter_type *filter_type) free_arg(filter_type->filter); } +/** + * pevent_filter_strerror - fill error message in a buffer + * @filter: the event filter contains error + * @err: the error code + * @buf: the buffer to be filled in + * @buflen: the size of the buffer + * + * Returns 0 if message was filled successfully, -1 if error + */ +int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, + char *buf, size_t buflen) +{ + if (err <= __PEVENT_ERRNO__START || err >= __PEVENT_ERRNO__END) + return -1; + + if (strlen(filter->error_buffer) > 0) { + size_t len = snprintf(buf, buflen, "%s", filter->error_buffer); + + if (len > buflen) + return -1; + return 0; + } + + return pevent_strerror(filter->pevent, err, buf, buflen); +} + /** * pevent_filter_remove_event - remove a filter for an event * @filter: the event filter to remove from @@ -1374,6 +1482,9 @@ static int copy_filter_type(struct event_filter *filter, if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { /* Add trivial event */ arg = allocate_arg(); + if (arg == NULL) + return -1; + arg->type = FILTER_ARG_BOOLEAN; if (strcmp(str, "TRUE") == 0) arg->boolean.value = 1; @@ -1381,6 +1492,9 @@ static int copy_filter_type(struct event_filter *filter, arg->boolean.value = 0; filter_type = add_filter_type(filter, event->id); + if (filter_type == NULL) + return -1; + filter_type->filter = arg; free(str); @@ -1482,8 +1596,10 @@ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source * @type: remove only true, false, or both * * Removes filters that only contain a TRUE or FALES boolean arg. + * + * Returns 0 on success and -1 if there was a problem. */ -void pevent_filter_clear_trivial(struct event_filter *filter, +int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type) { struct filter_type *filter_type; @@ -1492,13 +1608,15 @@ void pevent_filter_clear_trivial(struct event_filter *filter, int i; if (!filter->filters) - return; + return 0; /* * Two steps, first get all ids with trivial filters. * then remove those ids. */ for (i = 0; i < filter->filters; i++) { + int *new_ids; + filter_type = &filter->event_filters[i]; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) continue; @@ -1513,19 +1631,24 @@ void pevent_filter_clear_trivial(struct event_filter *filter, break; } - ids = realloc(ids, sizeof(*ids) * (count + 1)); - if (!ids) - die("Can't allocate ids"); + new_ids = realloc(ids, sizeof(*ids) * (count + 1)); + if (!new_ids) { + free(ids); + return -1; + } + + ids = new_ids; ids[count++] = filter_type->event_id; } if (!count) - return; + return 0; for (i = 0; i < count; i++) pevent_filter_remove_event(filter, ids[i]); free(ids); + return 0; } /** @@ -1565,8 +1688,8 @@ int pevent_filter_event_has_trivial(struct event_filter *filter, } } -static int test_filter(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record); +static int test_filter(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err); static const char * get_comm(struct event_format *event, struct pevent_record *record) @@ -1612,15 +1735,24 @@ get_value(struct event_format *event, } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record); +get_arg_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err); static unsigned long long -get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +get_exp_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; - lval = get_arg_value(event, arg->exp.left, record); - rval = get_arg_value(event, arg->exp.right, record); + lval = get_arg_value(event, arg->exp.left, record, err); + rval = get_arg_value(event, arg->exp.right, record, err); + + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } switch (arg->exp.type) { case FILTER_EXP_ADD: @@ -1655,39 +1787,51 @@ get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_ case FILTER_EXP_NOT: default: - die("error in exp"); + if (!*err) + *err = PEVENT_ERRNO__INVALID_EXP_TYPE; } return 0; } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +get_arg_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { switch (arg->type) { case FILTER_ARG_FIELD: return get_value(event, arg->field.field, record); case FILTER_ARG_VALUE: - if (arg->value.type != FILTER_NUMBER) - die("must have number field!"); + if (arg->value.type != FILTER_NUMBER) { + if (!*err) + *err = PEVENT_ERRNO__NOT_A_NUMBER; + } return arg->value.val; case FILTER_ARG_EXP: - return get_exp_value(event, arg, record); + return get_exp_value(event, arg, record, err); default: - die("oops in filter"); + if (!*err) + *err = PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } -static int test_num(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_num(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; - lval = get_arg_value(event, arg->num.left, record); - rval = get_arg_value(event, arg->num.right, record); + lval = get_arg_value(event, arg->num.left, record, err); + rval = get_arg_value(event, arg->num.right, record, err); + + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } switch (arg->num.type) { case FILTER_CMP_EQ: @@ -1709,7 +1853,8 @@ static int test_num(struct event_format *event, return lval <= rval; default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; return 0; } } @@ -1756,8 +1901,8 @@ static const char *get_field_str(struct filter_arg *arg, struct pevent_record *r return val; } -static int test_str(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_str(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { const char *val; @@ -1781,48 +1926,57 @@ static int test_str(struct event_format *event, return regexec(&arg->str.reg, val, 0, NULL, 0); default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__ILLEGAL_STRING_CMP; return 0; } } -static int test_op(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_op(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { switch (arg->op.type) { case FILTER_OP_AND: - return test_filter(event, arg->op.left, record) && - test_filter(event, arg->op.right, record); + return test_filter(event, arg->op.left, record, err) && + test_filter(event, arg->op.right, record, err); case FILTER_OP_OR: - return test_filter(event, arg->op.left, record) || - test_filter(event, arg->op.right, record); + return test_filter(event, arg->op.left, record, err) || + test_filter(event, arg->op.right, record, err); case FILTER_OP_NOT: - return !test_filter(event, arg->op.right, record); + return !test_filter(event, arg->op.right, record, err); default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__INVALID_OP_TYPE; return 0; } } -static int test_filter(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_filter(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } + switch (arg->type) { case FILTER_ARG_BOOLEAN: /* easy case */ return arg->boolean.value; case FILTER_ARG_OP: - return test_op(event, arg, record); + return test_op(event, arg, record, err); case FILTER_ARG_NUM: - return test_num(event, arg, record); + return test_num(event, arg, record, err); case FILTER_ARG_STR: - return test_str(event, arg, record); + return test_str(event, arg, record, err); case FILTER_ARG_EXP: case FILTER_ARG_VALUE: @@ -1831,11 +1985,11 @@ static int test_filter(struct event_format *event, * Expressions, fields and values evaluate * to true if they return non zero */ - return !!get_arg_value(event, arg, record); + return !!get_arg_value(event, arg, record, err); default: - die("oops!"); - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__INVALID_ARG_TYPE; return 0; } } @@ -1848,8 +2002,7 @@ static int test_filter(struct event_format *event, * Returns 1 if filter found for @event_id * otherwise 0; */ -int pevent_event_filtered(struct event_filter *filter, - int event_id) +int pevent_event_filtered(struct event_filter *filter, int event_id) { struct filter_type *filter_type; @@ -1866,31 +2019,38 @@ int pevent_event_filtered(struct event_filter *filter, * @filter: filter struct with filter information * @record: the record to test against the filter * - * Returns: - * 1 - filter found for event and @record matches - * 0 - filter found for event and @record does not match - * -1 - no filter found for @record's event - * -2 - if no filters exist + * Returns: match result or error code (prefixed with PEVENT_ERRNO__) + * FILTER_MATCH - filter found for event and @record matches + * FILTER_MISS - filter found for event and @record does not match + * FILTER_NOT_FOUND - no filter found for @record's event + * NO_FILTER - if no filters exist + * otherwise - error occurred during test */ -int pevent_filter_match(struct event_filter *filter, - struct pevent_record *record) +enum pevent_errno pevent_filter_match(struct event_filter *filter, + struct pevent_record *record) { struct pevent *pevent = filter->pevent; struct filter_type *filter_type; int event_id; + int ret; + enum pevent_errno err = 0; + + filter_init_error_buf(filter); if (!filter->filters) - return FILTER_NONE; + return PEVENT_ERRNO__NO_FILTER; event_id = pevent_data_type(pevent, record); filter_type = find_filter_type(filter, event_id); - if (!filter_type) - return FILTER_NOEXIST; + return PEVENT_ERRNO__FILTER_NOT_FOUND; - return test_filter(filter_type->event, filter_type->filter, record) ? - FILTER_MATCH : FILTER_MISS; + ret = test_filter(filter_type->event, filter_type->filter, record, &err); + if (err) + return err; + + return ret ? PEVENT_ERRNO__FILTER_MATCH : PEVENT_ERRNO__FILTER_MISS; } static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) @@ -1902,7 +2062,6 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) int left_val = -1; int right_val = -1; int val; - int len; switch (arg->op.type) { case FILTER_OP_AND: @@ -1949,11 +2108,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) default: break; } - str = malloc_or_die(6); - if (val) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, val ? "TRUE" : "FALSE"); break; } } @@ -1971,10 +2126,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) break; } - len = strlen(left) + strlen(right) + strlen(op) + 10; - str = malloc_or_die(len); - snprintf(str, len, "(%s) %s (%s)", - left, op, right); + asprintf(&str, "(%s) %s (%s)", left, op, right); break; case FILTER_OP_NOT: @@ -1990,16 +2142,10 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) right_val = 0; if (right_val >= 0) { /* just return the opposite */ - str = malloc_or_die(6); - if (right_val) - strcpy(str, "FALSE"); - else - strcpy(str, "TRUE"); + asprintf(&str, right_val ? "FALSE" : "TRUE"); break; } - len = strlen(right) + strlen(op) + 3; - str = malloc_or_die(len); - snprintf(str, len, "%s(%s)", op, right); + asprintf(&str, "%s(%s)", op, right); break; default: @@ -2013,11 +2159,9 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; + char *str = NULL; - str = malloc_or_die(30); - - snprintf(str, 30, "%lld", arg->value.val); + asprintf(&str, "%lld", arg->value.val); return str; } @@ -2033,7 +2177,6 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *op; char *str = NULL; - int len; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); @@ -2072,12 +2215,11 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) op = "^"; break; default: - die("oops in exp"); + op = "[ERROR IN EXPRESSION TYPE]"; + break; } - len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; - str = malloc_or_die(len); - snprintf(str, len, "%s %s %s", lstr, op, rstr); + asprintf(&str, "%s %s %s", lstr, op, rstr); out: free(lstr); free(rstr); @@ -2091,7 +2233,6 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *str = NULL; char *op = NULL; - int len; lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); @@ -2122,10 +2263,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "<="; - len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; - str = malloc_or_die(len); - sprintf(str, "%s %s %s", lstr, op, rstr); - + asprintf(&str, "%s %s %s", lstr, op, rstr); break; default: @@ -2143,7 +2281,6 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *op = NULL; - int len; switch (arg->str.type) { case FILTER_CMP_MATCH: @@ -2161,12 +2298,8 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "!~"; - len = strlen(arg->str.field->name) + strlen(op) + - strlen(arg->str.val) + 6; - str = malloc_or_die(len); - snprintf(str, len, "%s %s \"%s\"", - arg->str.field->name, - op, arg->str.val); + asprintf(&str, "%s %s \"%s\"", + arg->str.field->name, op, arg->str.val); break; default: @@ -2178,15 +2311,11 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; + char *str = NULL; switch (arg->type) { case FILTER_ARG_BOOLEAN: - str = malloc_or_die(6); - if (arg->boolean.value) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE"); return str; case FILTER_ARG_OP: @@ -2221,7 +2350,7 @@ static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) * * Returns a string that displays the filter contents. * This string must be freed with free(str). - * NULL is returned if no filter is found. + * NULL is returned if no filter is found or allocation failed. */ char * pevent_filter_make_string(struct event_filter *filter, int event_id) diff --git a/tools/lib/traceevent/parse-utils.c b/tools/lib/traceevent/parse-utils.c index bba701cf10e6..eda07fa31dca 100644 --- a/tools/lib/traceevent/parse-utils.c +++ b/tools/lib/traceevent/parse-utils.c @@ -25,40 +25,6 @@ #define __weak __attribute__((weak)) -void __vdie(const char *fmt, va_list ap) -{ - int ret = errno; - - if (errno) - perror("trace-cmd"); - else - ret = -1; - - fprintf(stderr, " "); - vfprintf(stderr, fmt, ap); - - fprintf(stderr, "\n"); - exit(ret); -} - -void __die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - __vdie(fmt, ap); - va_end(ap); -} - -void __weak die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - __vdie(fmt, ap); - va_end(ap); -} - void __vwarning(const char *fmt, va_list ap) { if (errno) @@ -117,13 +83,3 @@ void __weak pr_stat(const char *fmt, ...) __vpr_stat(fmt, ap); va_end(ap); } - -void __weak *malloc_or_die(unsigned int size) -{ - void *data; - - data = malloc(size); - if (!data) - die("malloc"); - return data; -} diff --git a/tools/lib/traceevent/plugin_cfg80211.c b/tools/lib/traceevent/plugin_cfg80211.c new file mode 100644 index 000000000000..c066b25905f8 --- /dev/null +++ b/tools/lib/traceevent/plugin_cfg80211.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include "event-parse.h" + +static unsigned long long +process___le16_to_cpup(struct trace_seq *s, + unsigned long long *args) +{ + uint16_t *val = (uint16_t *) (unsigned long) args[0]; + return val ? (long long) le16toh(*val) : 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process___le16_to_cpup, + PEVENT_FUNC_ARG_INT, + "__le16_to_cpup", + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_VOID); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process___le16_to_cpup, + "__le16_to_cpup"); +} diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c new file mode 100644 index 000000000000..80ba4ff1fe84 --- /dev/null +++ b/tools/lib/traceevent/plugin_function.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" +#include "event-utils.h" + +static struct func_stack { + int size; + char **stack; +} *fstack; + +static int cpus = -1; + +#define STK_BLK 10 + +static void add_child(struct func_stack *stack, const char *child, int pos) +{ + int i; + + if (!child) + return; + + if (pos < stack->size) + free(stack->stack[pos]); + else { + char **ptr; + + ptr = realloc(stack->stack, sizeof(char *) * + (stack->size + STK_BLK)); + if (!ptr) { + warning("could not allocate plugin memory\n"); + return; + } + + stack->stack = ptr; + + for (i = stack->size; i < stack->size + STK_BLK; i++) + stack->stack[i] = NULL; + stack->size += STK_BLK; + } + + stack->stack[pos] = strdup(child); +} + +static int add_and_get_index(const char *parent, const char *child, int cpu) +{ + int i; + + if (cpu < 0) + return 0; + + if (cpu > cpus) { + struct func_stack *ptr; + + ptr = realloc(fstack, sizeof(*fstack) * (cpu + 1)); + if (!ptr) { + warning("could not allocate plugin memory\n"); + return 0; + } + + fstack = ptr; + + /* Account for holes in the cpu count */ + for (i = cpus + 1; i <= cpu; i++) + memset(&fstack[i], 0, sizeof(fstack[i])); + cpus = cpu; + } + + for (i = 0; i < fstack[cpu].size && fstack[cpu].stack[i]; i++) { + if (strcmp(parent, fstack[cpu].stack[i]) == 0) { + add_child(&fstack[cpu], child, i+1); + return i; + } + } + + /* Not found */ + add_child(&fstack[cpu], parent, 0); + add_child(&fstack[cpu], child, 1); + return 0; +} + +static int function_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + struct pevent *pevent = event->pevent; + unsigned long long function; + unsigned long long pfunction; + const char *func; + const char *parent; + int index; + + if (pevent_get_field_val(s, event, "ip", record, &function, 1)) + return trace_seq_putc(s, '!'); + + func = pevent_find_function(pevent, function); + + if (pevent_get_field_val(s, event, "parent_ip", record, &pfunction, 1)) + return trace_seq_putc(s, '!'); + + parent = pevent_find_function(pevent, pfunction); + + index = add_and_get_index(parent, func, record->cpu); + + trace_seq_printf(s, "%*s", index*3, ""); + + if (func) + trace_seq_printf(s, "%s", func); + else + trace_seq_printf(s, "0x%llx", function); + + trace_seq_printf(s, " <-- "); + if (parent) + trace_seq_printf(s, "%s", parent); + else + trace_seq_printf(s, "0x%llx", pfunction); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "ftrace", "function", + function_handler, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + int i, x; + + pevent_unregister_event_handler(pevent, -1, "ftrace", "function", + function_handler, NULL); + + for (i = 0; i <= cpus; i++) { + for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++) + free(fstack[i].stack[x]); + free(fstack[i].stack); + } + + free(fstack); + fstack = NULL; + cpus = -1; +} diff --git a/tools/lib/traceevent/plugin_hrtimer.c b/tools/lib/traceevent/plugin_hrtimer.c new file mode 100644 index 000000000000..12bf14cc1152 --- /dev/null +++ b/tools/lib/traceevent/plugin_hrtimer.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * Copyright (C) 2009 Johannes Berg + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static int timer_expire_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + trace_seq_printf(s, "hrtimer="); + + if (pevent_print_num_field(s, "0x%llx", event, "timer", + record, 0) == -1) + pevent_print_num_field(s, "0x%llx", event, "hrtimer", + record, 1); + + trace_seq_printf(s, " now="); + + pevent_print_num_field(s, "%llu", event, "now", record, 1); + + pevent_print_func_field(s, " function=%s", event, "function", + record, 0); + return 0; +} + +static int timer_start_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + trace_seq_printf(s, "hrtimer="); + + if (pevent_print_num_field(s, "0x%llx", event, "timer", + record, 0) == -1) + pevent_print_num_field(s, "0x%llx", event, "hrtimer", + record, 1); + + pevent_print_func_field(s, " function=%s", event, "function", + record, 0); + + trace_seq_printf(s, " expires="); + pevent_print_num_field(s, "%llu", event, "expires", record, 1); + + trace_seq_printf(s, " softexpires="); + pevent_print_num_field(s, "%llu", event, "softexpires", record, 1); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, + "timer", "hrtimer_expire_entry", + timer_expire_handler, NULL); + + pevent_register_event_handler(pevent, -1, "timer", "hrtimer_start", + timer_start_handler, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, + "timer", "hrtimer_expire_entry", + timer_expire_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_start", + timer_start_handler, NULL); +} diff --git a/tools/lib/traceevent/plugin_jbd2.c b/tools/lib/traceevent/plugin_jbd2.c new file mode 100644 index 000000000000..0db714c721be --- /dev/null +++ b/tools/lib/traceevent/plugin_jbd2.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) + +static unsigned long long +process_jbd2_dev_to_name(struct trace_seq *s, + unsigned long long *args) +{ + unsigned int dev = args[0]; + + trace_seq_printf(s, "%d:%d", MAJOR(dev), MINOR(dev)); + return 0; +} + +static unsigned long long +process_jiffies_to_msecs(struct trace_seq *s, + unsigned long long *args) +{ + unsigned long long jiffies = args[0]; + + trace_seq_printf(s, "%lld", jiffies); + return jiffies; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_jbd2_dev_to_name, + PEVENT_FUNC_ARG_STRING, + "jbd2_dev_to_name", + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + + pevent_register_print_function(pevent, + process_jiffies_to_msecs, + PEVENT_FUNC_ARG_LONG, + "jiffies_to_msecs", + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_VOID); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_jbd2_dev_to_name, + "jbd2_dev_to_name"); + + pevent_unregister_print_function(pevent, process_jiffies_to_msecs, + "jiffies_to_msecs"); +} diff --git a/tools/lib/traceevent/plugin_kmem.c b/tools/lib/traceevent/plugin_kmem.c new file mode 100644 index 000000000000..70650ff48d78 --- /dev/null +++ b/tools/lib/traceevent/plugin_kmem.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static int call_site_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val, addr; + void *data = record->data; + const char *func; + + field = pevent_find_field(event, "call_site"); + if (!field) + return 1; + + if (pevent_read_number_field(field, data, &val)) + return 1; + + func = pevent_find_function(event->pevent, val); + if (!func) + return 1; + + addr = pevent_find_function_address(event->pevent, val); + + trace_seq_printf(s, "(%s+0x%x) ", func, (int)(val - addr)); + return 1; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "kmem", "kfree", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmalloc", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmalloc_node", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", + "kmem_cache_alloc_node", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_free", + call_site_handler, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "kmem", "kfree", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc_node", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", + "kmem_cache_alloc_node", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_free", + call_site_handler, NULL); +} diff --git a/tools/lib/traceevent/plugin_kvm.c b/tools/lib/traceevent/plugin_kvm.c new file mode 100644 index 000000000000..9e0e8c61b43b --- /dev/null +++ b/tools/lib/traceevent/plugin_kvm.c @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include + +#include "event-parse.h" + +#ifdef HAVE_UDIS86 + +#include + +static ud_t ud; + +static void init_disassembler(void) +{ + ud_init(&ud); + ud_set_syntax(&ud, UD_SYN_ATT); +} + +static const char *disassemble(unsigned char *insn, int len, uint64_t rip, + int cr0_pe, int eflags_vm, + int cs_d, int cs_l) +{ + int mode; + + if (!cr0_pe) + mode = 16; + else if (eflags_vm) + mode = 16; + else if (cs_l) + mode = 64; + else if (cs_d) + mode = 32; + else + mode = 16; + + ud_set_pc(&ud, rip); + ud_set_mode(&ud, mode); + ud_set_input_buffer(&ud, insn, len); + ud_disassemble(&ud); + return ud_insn_asm(&ud); +} + +#else + +static void init_disassembler(void) +{ +} + +static const char *disassemble(unsigned char *insn, int len, uint64_t rip, + int cr0_pe, int eflags_vm, + int cs_d, int cs_l) +{ + static char out[15*3+1]; + int i; + + for (i = 0; i < len; ++i) + sprintf(out + i * 3, "%02x ", insn[i]); + out[len*3-1] = '\0'; + return out; +} + +#endif + + +#define VMX_EXIT_REASONS \ + _ER(EXCEPTION_NMI, 0) \ + _ER(EXTERNAL_INTERRUPT, 1) \ + _ER(TRIPLE_FAULT, 2) \ + _ER(PENDING_INTERRUPT, 7) \ + _ER(NMI_WINDOW, 8) \ + _ER(TASK_SWITCH, 9) \ + _ER(CPUID, 10) \ + _ER(HLT, 12) \ + _ER(INVD, 13) \ + _ER(INVLPG, 14) \ + _ER(RDPMC, 15) \ + _ER(RDTSC, 16) \ + _ER(VMCALL, 18) \ + _ER(VMCLEAR, 19) \ + _ER(VMLAUNCH, 20) \ + _ER(VMPTRLD, 21) \ + _ER(VMPTRST, 22) \ + _ER(VMREAD, 23) \ + _ER(VMRESUME, 24) \ + _ER(VMWRITE, 25) \ + _ER(VMOFF, 26) \ + _ER(VMON, 27) \ + _ER(CR_ACCESS, 28) \ + _ER(DR_ACCESS, 29) \ + _ER(IO_INSTRUCTION, 30) \ + _ER(MSR_READ, 31) \ + _ER(MSR_WRITE, 32) \ + _ER(MWAIT_INSTRUCTION, 36) \ + _ER(MONITOR_INSTRUCTION, 39) \ + _ER(PAUSE_INSTRUCTION, 40) \ + _ER(MCE_DURING_VMENTRY, 41) \ + _ER(TPR_BELOW_THRESHOLD, 43) \ + _ER(APIC_ACCESS, 44) \ + _ER(EOI_INDUCED, 45) \ + _ER(EPT_VIOLATION, 48) \ + _ER(EPT_MISCONFIG, 49) \ + _ER(INVEPT, 50) \ + _ER(PREEMPTION_TIMER, 52) \ + _ER(WBINVD, 54) \ + _ER(XSETBV, 55) \ + _ER(APIC_WRITE, 56) \ + _ER(INVPCID, 58) + +#define SVM_EXIT_REASONS \ + _ER(EXIT_READ_CR0, 0x000) \ + _ER(EXIT_READ_CR3, 0x003) \ + _ER(EXIT_READ_CR4, 0x004) \ + _ER(EXIT_READ_CR8, 0x008) \ + _ER(EXIT_WRITE_CR0, 0x010) \ + _ER(EXIT_WRITE_CR3, 0x013) \ + _ER(EXIT_WRITE_CR4, 0x014) \ + _ER(EXIT_WRITE_CR8, 0x018) \ + _ER(EXIT_READ_DR0, 0x020) \ + _ER(EXIT_READ_DR1, 0x021) \ + _ER(EXIT_READ_DR2, 0x022) \ + _ER(EXIT_READ_DR3, 0x023) \ + _ER(EXIT_READ_DR4, 0x024) \ + _ER(EXIT_READ_DR5, 0x025) \ + _ER(EXIT_READ_DR6, 0x026) \ + _ER(EXIT_READ_DR7, 0x027) \ + _ER(EXIT_WRITE_DR0, 0x030) \ + _ER(EXIT_WRITE_DR1, 0x031) \ + _ER(EXIT_WRITE_DR2, 0x032) \ + _ER(EXIT_WRITE_DR3, 0x033) \ + _ER(EXIT_WRITE_DR4, 0x034) \ + _ER(EXIT_WRITE_DR5, 0x035) \ + _ER(EXIT_WRITE_DR6, 0x036) \ + _ER(EXIT_WRITE_DR7, 0x037) \ + _ER(EXIT_EXCP_BASE, 0x040) \ + _ER(EXIT_INTR, 0x060) \ + _ER(EXIT_NMI, 0x061) \ + _ER(EXIT_SMI, 0x062) \ + _ER(EXIT_INIT, 0x063) \ + _ER(EXIT_VINTR, 0x064) \ + _ER(EXIT_CR0_SEL_WRITE, 0x065) \ + _ER(EXIT_IDTR_READ, 0x066) \ + _ER(EXIT_GDTR_READ, 0x067) \ + _ER(EXIT_LDTR_READ, 0x068) \ + _ER(EXIT_TR_READ, 0x069) \ + _ER(EXIT_IDTR_WRITE, 0x06a) \ + _ER(EXIT_GDTR_WRITE, 0x06b) \ + _ER(EXIT_LDTR_WRITE, 0x06c) \ + _ER(EXIT_TR_WRITE, 0x06d) \ + _ER(EXIT_RDTSC, 0x06e) \ + _ER(EXIT_RDPMC, 0x06f) \ + _ER(EXIT_PUSHF, 0x070) \ + _ER(EXIT_POPF, 0x071) \ + _ER(EXIT_CPUID, 0x072) \ + _ER(EXIT_RSM, 0x073) \ + _ER(EXIT_IRET, 0x074) \ + _ER(EXIT_SWINT, 0x075) \ + _ER(EXIT_INVD, 0x076) \ + _ER(EXIT_PAUSE, 0x077) \ + _ER(EXIT_HLT, 0x078) \ + _ER(EXIT_INVLPG, 0x079) \ + _ER(EXIT_INVLPGA, 0x07a) \ + _ER(EXIT_IOIO, 0x07b) \ + _ER(EXIT_MSR, 0x07c) \ + _ER(EXIT_TASK_SWITCH, 0x07d) \ + _ER(EXIT_FERR_FREEZE, 0x07e) \ + _ER(EXIT_SHUTDOWN, 0x07f) \ + _ER(EXIT_VMRUN, 0x080) \ + _ER(EXIT_VMMCALL, 0x081) \ + _ER(EXIT_VMLOAD, 0x082) \ + _ER(EXIT_VMSAVE, 0x083) \ + _ER(EXIT_STGI, 0x084) \ + _ER(EXIT_CLGI, 0x085) \ + _ER(EXIT_SKINIT, 0x086) \ + _ER(EXIT_RDTSCP, 0x087) \ + _ER(EXIT_ICEBP, 0x088) \ + _ER(EXIT_WBINVD, 0x089) \ + _ER(EXIT_MONITOR, 0x08a) \ + _ER(EXIT_MWAIT, 0x08b) \ + _ER(EXIT_MWAIT_COND, 0x08c) \ + _ER(EXIT_NPF, 0x400) \ + _ER(EXIT_ERR, -1) + +#define _ER(reason, val) { #reason, val }, +struct str_values { + const char *str; + int val; +}; + +static struct str_values vmx_exit_reasons[] = { + VMX_EXIT_REASONS + { NULL, -1} +}; + +static struct str_values svm_exit_reasons[] = { + SVM_EXIT_REASONS + { NULL, -1} +}; + +static struct isa_exit_reasons { + unsigned isa; + struct str_values *strings; +} isa_exit_reasons[] = { + { .isa = 1, .strings = vmx_exit_reasons }, + { .isa = 2, .strings = svm_exit_reasons }, + { } +}; + +static const char *find_exit_reason(unsigned isa, int val) +{ + struct str_values *strings = NULL; + int i; + + for (i = 0; isa_exit_reasons[i].strings; ++i) + if (isa_exit_reasons[i].isa == isa) { + strings = isa_exit_reasons[i].strings; + break; + } + if (!strings) + return "UNKNOWN-ISA"; + for (i = 0; strings[i].val >= 0; i++) + if (strings[i].val == val) + break; + if (strings[i].str) + return strings[i].str; + return "UNKNOWN"; +} + +static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long isa; + unsigned long long val; + unsigned long long info1 = 0, info2 = 0; + + if (pevent_get_field_val(s, event, "exit_reason", record, &val, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0) + isa = 1; + + trace_seq_printf(s, "reason %s", find_exit_reason(isa, val)); + + pevent_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1); + + if (pevent_get_field_val(s, event, "info1", record, &info1, 0) >= 0 + && pevent_get_field_val(s, event, "info2", record, &info2, 0) >= 0) + trace_seq_printf(s, " info %llx %llx", info1, info2); + + return 0; +} + +#define KVM_EMUL_INSN_F_CR0_PE (1 << 0) +#define KVM_EMUL_INSN_F_EFL_VM (1 << 1) +#define KVM_EMUL_INSN_F_CS_D (1 << 2) +#define KVM_EMUL_INSN_F_CS_L (1 << 3) + +static int kvm_emulate_insn_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long rip, csbase, len, flags, failed; + int llen; + uint8_t *insn; + const char *disasm; + + if (pevent_get_field_val(s, event, "rip", record, &rip, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "csbase", record, &csbase, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "len", record, &len, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "flags", record, &flags, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "failed", record, &failed, 1) < 0) + return -1; + + insn = pevent_get_field_raw(s, event, "insn", record, &llen, 1); + if (!insn) + return -1; + + disasm = disassemble(insn, len, rip, + flags & KVM_EMUL_INSN_F_CR0_PE, + flags & KVM_EMUL_INSN_F_EFL_VM, + flags & KVM_EMUL_INSN_F_CS_D, + flags & KVM_EMUL_INSN_F_CS_L); + + trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm, + failed ? " FAIL" : ""); + return 0; +} + +union kvm_mmu_page_role { + unsigned word; + struct { + unsigned glevels:4; + unsigned level:4; + unsigned quadrant:2; + unsigned pad_for_nice_hex_output:6; + unsigned direct:1; + unsigned access:3; + unsigned invalid:1; + unsigned cr4_pge:1; + unsigned nxe:1; + }; +}; + +static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long val; + static const char *access_str[] = { + "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" + }; + union kvm_mmu_page_role role; + + if (pevent_get_field_val(s, event, "role", record, &val, 1) < 0) + return -1; + + role.word = (int)val; + + /* + * We can only use the structure if file is of the same + * endianess. + */ + if (pevent_is_file_bigendian(event->pevent) == + pevent_is_host_bigendian(event->pevent)) { + + trace_seq_printf(s, "%u/%u q%u%s %s%s %spge %snxe", + role.level, + role.glevels, + role.quadrant, + role.direct ? " direct" : "", + access_str[role.access], + role.invalid ? " invalid" : "", + role.cr4_pge ? "" : "!", + role.nxe ? "" : "!"); + } else + trace_seq_printf(s, "WORD: %08x", role.word); + + pevent_print_num_field(s, " root %u ", event, + "root_count", record, 1); + + if (pevent_get_field_val(s, event, "unsync", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "%s%c", val ? "unsync" : "sync", 0); + return 0; +} + +static int kvm_mmu_get_page_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long val; + + if (pevent_get_field_val(s, event, "created", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "%s ", val ? "new" : "existing"); + + if (pevent_get_field_val(s, event, "gfn", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "sp gfn %llx ", val); + return kvm_mmu_print_role(s, record, event, context); +} + +#define PT_WRITABLE_SHIFT 1 +#define PT_WRITABLE_MASK (1ULL << PT_WRITABLE_SHIFT) + +static unsigned long long +process_is_writable_pte(struct trace_seq *s, unsigned long long *args) +{ + unsigned long pte = args[0]; + return pte & PT_WRITABLE_MASK; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + init_disassembler(); + + pevent_register_event_handler(pevent, -1, "kvm", "kvm_exit", + kvm_exit_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", + kvm_emulate_insn_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", + kvm_mmu_get_page_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, + "kvmmmu", "kvm_mmu_unsync_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", + "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, + NULL); + + pevent_register_print_function(pevent, + process_is_writable_pte, + PEVENT_FUNC_ARG_INT, + "is_writable_pte", + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_VOID); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_exit", + kvm_exit_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", + kvm_emulate_insn_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", + kvm_mmu_get_page_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, + "kvmmmu", "kvm_mmu_unsync_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", + "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, + NULL); + + pevent_unregister_print_function(pevent, process_is_writable_pte, + "is_writable_pte"); +} diff --git a/tools/lib/traceevent/plugin_mac80211.c b/tools/lib/traceevent/plugin_mac80211.c new file mode 100644 index 000000000000..7e15a0f1c2fd --- /dev/null +++ b/tools/lib/traceevent/plugin_mac80211.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2009 Johannes Berg + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +#define INDENT 65 + +static void print_string(struct trace_seq *s, struct event_format *event, + const char *name, const void *data) +{ + struct format_field *f = pevent_find_field(event, name); + int offset; + int length; + + if (!f) { + trace_seq_printf(s, "NOTFOUND:%s", name); + return; + } + + offset = f->offset; + length = f->size; + + if (!strncmp(f->type, "__data_loc", 10)) { + unsigned long long v; + if (pevent_read_number_field(f, data, &v)) { + trace_seq_printf(s, "invalid_data_loc"); + return; + } + offset = v & 0xffff; + length = v >> 16; + } + + trace_seq_printf(s, "%.*s", length, (char *)data + offset); +} + +#define SF(fn) pevent_print_num_field(s, fn ":%d", event, fn, record, 0) +#define SFX(fn) pevent_print_num_field(s, fn ":%#x", event, fn, record, 0) +#define SP() trace_seq_putc(s, ' ') + +static int drv_bss_info_changed(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + void *data = record->data; + + print_string(s, event, "wiphy_name", data); + trace_seq_printf(s, " vif:"); + print_string(s, event, "vif_name", data); + pevent_print_num_field(s, "(%d)", event, "vif_type", record, 1); + + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("assoc"); SP(); + SF("aid"); SP(); + SF("cts"); SP(); + SF("shortpre"); SP(); + SF("shortslot"); SP(); + SF("dtimper"); SP(); + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("bcnint"); SP(); + SFX("assoc_cap"); SP(); + SFX("basic_rates"); SP(); + SF("enable_beacon"); + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("ht_operation_mode"); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "mac80211", + "drv_bss_info_changed", + drv_bss_info_changed, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "mac80211", + "drv_bss_info_changed", + drv_bss_info_changed, NULL); +} diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c new file mode 100644 index 000000000000..f1ce60065258 --- /dev/null +++ b/tools/lib/traceevent/plugin_sched_switch.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static void write_state(struct trace_seq *s, int val) +{ + const char states[] = "SDTtZXxW"; + int found = 0; + int i; + + for (i = 0; i < (sizeof(states) - 1); i++) { + if (!(val & (1 << i))) + continue; + + if (found) + trace_seq_putc(s, '|'); + + found = 1; + trace_seq_putc(s, states[i]); + } + + if (!found) + trace_seq_putc(s, 'R'); +} + +static void write_and_save_comm(struct format_field *field, + struct pevent_record *record, + struct trace_seq *s, int pid) +{ + const char *comm; + int len; + + comm = (char *)(record->data + field->offset); + len = s->len; + trace_seq_printf(s, "%.*s", + field->size, comm); + + /* make sure the comm has a \0 at the end. */ + trace_seq_terminate(s); + comm = &s->buffer[len]; + + /* Help out the comm to ids. This will handle dups */ + pevent_register_comm(field->event->pevent, comm, pid); +} + +static int sched_wakeup_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val; + + if (pevent_get_field_val(s, event, "pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld", val); + + if (pevent_get_field_val(s, event, "prio", record, &val, 0) == 0) + trace_seq_printf(s, " [%lld]", val); + + if (pevent_get_field_val(s, event, "success", record, &val, 1) == 0) + trace_seq_printf(s, " success=%lld", val); + + if (pevent_get_field_val(s, event, "target_cpu", record, &val, 0) == 0) + trace_seq_printf(s, " CPU:%03llu", val); + + return 0; +} + +static int sched_switch_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val; + + if (pevent_get_field_val(s, event, "prev_pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "prev_comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld ", val); + + if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0) + trace_seq_printf(s, "[%lld] ", val); + + if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0) + write_state(s, val); + + trace_seq_puts(s, " ==> "); + + if (pevent_get_field_val(s, event, "next_pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "next_comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld", val); + + if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0) + trace_seq_printf(s, " [%lld]", val); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "sched", "sched_switch", + sched_switch_handler, NULL); + + pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup", + sched_wakeup_handler, NULL); + + pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup_new", + sched_wakeup_handler, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "sched", "sched_switch", + sched_switch_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup", + sched_wakeup_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup_new", + sched_wakeup_handler, NULL); +} diff --git a/tools/lib/traceevent/plugin_scsi.c b/tools/lib/traceevent/plugin_scsi.c new file mode 100644 index 000000000000..eda326fc8620 --- /dev/null +++ b/tools/lib/traceevent/plugin_scsi.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include "event-parse.h" + +typedef unsigned long sector_t; +typedef uint64_t u64; +typedef unsigned int u32; + +/* + * SCSI opcodes + */ +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define INITIALIZE_ELEMENT_STATUS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define READ_FORMAT_CAPACITIES 0x23 +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define POSITION_TO_ELEMENT 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define UNMAP 0x42 +#define READ_TOC 0x43 +#define READ_HEADER 0x44 +#define GET_EVENT_STATUS_NOTIFICATION 0x4a +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define XDWRITEREAD_10 0x53 +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define VARIABLE_LENGTH_CMD 0x7f +#define REPORT_LUNS 0xa0 +#define SECURITY_PROTOCOL_IN 0xa2 +#define MAINTENANCE_IN 0xa3 +#define MAINTENANCE_OUT 0xa4 +#define MOVE_MEDIUM 0xa5 +#define EXCHANGE_MEDIUM 0xa6 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define READ_MEDIA_SERIAL_NUMBER 0xab +#define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define SECURITY_PROTOCOL_OUT 0xb5 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea +#define EXTENDED_COPY 0x83 +#define RECEIVE_COPY_RESULTS 0x84 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 +#define READ_16 0x88 +#define WRITE_16 0x8a +#define READ_ATTRIBUTE 0x8c +#define WRITE_ATTRIBUTE 0x8d +#define VERIFY_16 0x8f +#define SYNCHRONIZE_CACHE_16 0x91 +#define WRITE_SAME_16 0x93 +#define SERVICE_ACTION_IN 0x9e +/* values for service action in */ +#define SAI_READ_CAPACITY_16 0x10 +#define SAI_GET_LBA_STATUS 0x12 +/* values for VARIABLE_LENGTH_CMD service action codes + * see spc4r17 Section D.3.5, table D.7 and D.8 */ +#define VLC_SA_RECEIVE_CREDENTIAL 0x1800 +/* values for maintenance in */ +#define MI_REPORT_IDENTIFYING_INFORMATION 0x05 +#define MI_REPORT_TARGET_PGS 0x0a +#define MI_REPORT_ALIASES 0x0b +#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c +#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d +#define MI_REPORT_PRIORITY 0x0e +#define MI_REPORT_TIMESTAMP 0x0f +#define MI_MANAGEMENT_PROTOCOL_IN 0x10 +/* value for MI_REPORT_TARGET_PGS ext header */ +#define MI_EXT_HDR_PARAM_FMT 0x20 +/* values for maintenance out */ +#define MO_SET_IDENTIFYING_INFORMATION 0x06 +#define MO_SET_TARGET_PGS 0x0a +#define MO_CHANGE_ALIASES 0x0b +#define MO_SET_PRIORITY 0x0e +#define MO_SET_TIMESTAMP 0x0f +#define MO_MANAGEMENT_PROTOCOL_OUT 0x10 +/* values for variable length command */ +#define XDREAD_32 0x03 +#define XDWRITE_32 0x04 +#define XPWRITE_32 0x06 +#define XDWRITEREAD_32 0x07 +#define READ_32 0x09 +#define VERIFY_32 0x0a +#define WRITE_32 0x0b +#define WRITE_SAME_32 0x0d + +#define SERVICE_ACTION16(cdb) (cdb[1] & 0x1f) +#define SERVICE_ACTION32(cdb) ((cdb[8] << 8) | cdb[9]) + +static const char * +scsi_trace_misc(struct trace_seq *, unsigned char *, int); + +static const char * +scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((cdb[1] & 0x1F) << 16); + lba |= (cdb[2] << 8); + lba |= cdb[3]; + txlen = cdb[4]; + + trace_seq_printf(p, "lba=%llu txlen=%llu", + (unsigned long long)lba, (unsigned long long)txlen); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[7] << 8); + txlen |= cdb[8]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[6] << 24); + txlen |= (cdb[7] << 16); + txlen |= (cdb[8] << 8); + txlen |= cdb[9]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + txlen |= (cdb[10] << 24); + txlen |= (cdb[11] << 16); + txlen |= (cdb[12] << 8); + txlen |= cdb[13]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME_16) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0, txlen = 0; + u32 ei_lbrt = 0; + + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + cmd = "READ"; + break; + case VERIFY_32: + cmd = "VERIFY"; + break; + case WRITE_32: + cmd = "WRITE"; + break; + case WRITE_SAME_32: + cmd = "WRITE_SAME"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[12] << 56); + lba |= ((u64)cdb[13] << 48); + lba |= ((u64)cdb[14] << 40); + lba |= ((u64)cdb[15] << 32); + lba |= (cdb[16] << 24); + lba |= (cdb[17] << 16); + lba |= (cdb[18] << 8); + lba |= cdb[19]; + ei_lbrt |= (cdb[20] << 24); + ei_lbrt |= (cdb[21] << 16); + ei_lbrt |= (cdb[22] << 8); + ei_lbrt |= cdb[23]; + txlen |= (cdb[28] << 24); + txlen |= (cdb[29] << 16); + txlen |= (cdb[30] << 8); + txlen |= cdb[31]; + + trace_seq_printf(p, "%s_32 lba=%llu txlen=%llu protect=%u ei_lbrt=%u", + cmd, (unsigned long long)lba, + (unsigned long long)txlen, cdb[10] >> 5, ei_lbrt); + + if (SERVICE_ACTION32(cdb) == WRITE_SAME_32) + trace_seq_printf(p, " unmap=%u", cdb[10] >> 3 & 1); + +out: + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + unsigned int regions = cdb[7] << 8 | cdb[8]; + + trace_seq_printf(p, "regions=%u", (regions - 8) / 16); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0; + u32 alloc_len = 0; + + switch (SERVICE_ACTION16(cdb)) { + case SAI_READ_CAPACITY_16: + cmd = "READ_CAPACITY_16"; + break; + case SAI_GET_LBA_STATUS: + cmd = "GET_LBA_STATUS"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + alloc_len |= (cdb[10] << 24); + alloc_len |= (cdb[11] << 16); + alloc_len |= (cdb[12] << 8); + alloc_len |= cdb[13]; + + trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, + (unsigned long long)lba, alloc_len); + +out: + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + case VERIFY_32: + case WRITE_32: + case WRITE_SAME_32: + return scsi_trace_rw32(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} + +static const char * +scsi_trace_misc(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + + trace_seq_printf(p, "-"); + trace_seq_putc(p, 0); + return ret; +} + +const char * +scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (cdb[0]) { + case READ_6: + case WRITE_6: + return scsi_trace_rw6(p, cdb, len); + case READ_10: + case VERIFY: + case WRITE_10: + case WRITE_SAME: + return scsi_trace_rw10(p, cdb, len); + case READ_12: + case VERIFY_12: + case WRITE_12: + return scsi_trace_rw12(p, cdb, len); + case READ_16: + case VERIFY_16: + case WRITE_16: + case WRITE_SAME_16: + return scsi_trace_rw16(p, cdb, len); + case UNMAP: + return scsi_trace_unmap(p, cdb, len); + case SERVICE_ACTION_IN: + return scsi_trace_service_action_in(p, cdb, len); + case VARIABLE_LENGTH_CMD: + return scsi_trace_varlen(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} + +unsigned long long process_scsi_trace_parse_cdb(struct trace_seq *s, + unsigned long long *args) +{ + scsi_trace_parse_cdb(s, (unsigned char *) (unsigned long) args[1], args[2]); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_scsi_trace_parse_cdb, + PEVENT_FUNC_ARG_STRING, + "scsi_trace_parse_cdb", + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_scsi_trace_parse_cdb, + "scsi_trace_parse_cdb"); +} diff --git a/tools/lib/traceevent/plugin_xen.c b/tools/lib/traceevent/plugin_xen.c new file mode 100644 index 000000000000..3a413eaada68 --- /dev/null +++ b/tools/lib/traceevent/plugin_xen.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include "event-parse.h" + +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op_compat 6 +#define __HYPERVISOR_dom0_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_acm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_tmem_op 38 + +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + +#define N(x) [__HYPERVISOR_##x] = "("#x")" +static const char *xen_hypercall_names[] = { + N(set_trap_table), + N(mmu_update), + N(set_gdt), + N(stack_switch), + N(set_callbacks), + N(fpu_taskswitch), + N(sched_op_compat), + N(dom0_op), + N(set_debugreg), + N(get_debugreg), + N(update_descriptor), + N(memory_op), + N(multicall), + N(update_va_mapping), + N(set_timer_op), + N(event_channel_op_compat), + N(xen_version), + N(console_io), + N(physdev_op_compat), + N(grant_table_op), + N(vm_assist), + N(update_va_mapping_otherdomain), + N(iret), + N(vcpu_op), + N(set_segment_base), + N(mmuext_op), + N(acm_op), + N(nmi_op), + N(sched_op), + N(callback_op), + N(xenoprof_op), + N(event_channel_op), + N(physdev_op), + N(hvm_op), + +/* Architecture-specific hypercall definitions. */ + N(arch_0), + N(arch_1), + N(arch_2), + N(arch_3), + N(arch_4), + N(arch_5), + N(arch_6), + N(arch_7), +}; +#undef N + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static const char *xen_hypercall_name(unsigned op) +{ + if (op < ARRAY_SIZE(xen_hypercall_names) && + xen_hypercall_names[op] != NULL) + return xen_hypercall_names[op]; + + return ""; +} + +unsigned long long process_xen_hypercall_name(struct trace_seq *s, + unsigned long long *args) +{ + unsigned int op = args[0]; + + trace_seq_printf(s, "%s", xen_hypercall_name(op)); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_xen_hypercall_name, + PEVENT_FUNC_ARG_STRING, + "xen_hypercall_name", + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_xen_hypercall_name, + "xen_hypercall_name"); +} diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c index d7f2e68bc5b9..ec3bd16a5488 100644 --- a/tools/lib/traceevent/trace-seq.c +++ b/tools/lib/traceevent/trace-seq.c @@ -22,6 +22,7 @@ #include #include +#include #include "event-parse.h" #include "event-utils.h" @@ -32,10 +33,21 @@ #define TRACE_SEQ_POISON ((void *)0xdeadbeef) #define TRACE_SEQ_CHECK(s) \ do { \ - if ((s)->buffer == TRACE_SEQ_POISON) \ - die("Usage of trace_seq after it was destroyed"); \ + if (WARN_ONCE((s)->buffer == TRACE_SEQ_POISON, \ + "Usage of trace_seq after it was destroyed")) \ + (s)->state = TRACE_SEQ__BUFFER_POISONED; \ } while (0) +#define TRACE_SEQ_CHECK_RET_N(s, n) \ +do { \ + TRACE_SEQ_CHECK(s); \ + if ((s)->state != TRACE_SEQ__GOOD) \ + return n; \ +} while (0) + +#define TRACE_SEQ_CHECK_RET(s) TRACE_SEQ_CHECK_RET_N(s, ) +#define TRACE_SEQ_CHECK_RET0(s) TRACE_SEQ_CHECK_RET_N(s, 0) + /** * trace_seq_init - initialize the trace_seq structure * @s: a pointer to the trace_seq structure to initialize @@ -45,7 +57,11 @@ void trace_seq_init(struct trace_seq *s) s->len = 0; s->readpos = 0; s->buffer_size = TRACE_SEQ_BUF_SIZE; - s->buffer = malloc_or_die(s->buffer_size); + s->buffer = malloc(s->buffer_size); + if (s->buffer != NULL) + s->state = TRACE_SEQ__GOOD; + else + s->state = TRACE_SEQ__MEM_ALLOC_FAILED; } /** @@ -71,17 +87,23 @@ void trace_seq_destroy(struct trace_seq *s) { if (!s) return; - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET(s); free(s->buffer); s->buffer = TRACE_SEQ_POISON; } static void expand_buffer(struct trace_seq *s) { + char *buf; + + buf = realloc(s->buffer, s->buffer_size + TRACE_SEQ_BUF_SIZE); + if (WARN_ONCE(!buf, "Can't allocate trace_seq buffer memory")) { + s->state = TRACE_SEQ__MEM_ALLOC_FAILED; + return; + } + + s->buffer = buf; s->buffer_size += TRACE_SEQ_BUF_SIZE; - s->buffer = realloc(s->buffer, s->buffer_size); - if (!s->buffer) - die("Can't allocate trace_seq buffer memory"); } /** @@ -105,9 +127,9 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) int len; int ret; - TRACE_SEQ_CHECK(s); - try_again: + TRACE_SEQ_CHECK_RET0(s); + len = (s->buffer_size - 1) - s->len; va_start(ap, fmt); @@ -141,9 +163,9 @@ trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) int len; int ret; - TRACE_SEQ_CHECK(s); - try_again: + TRACE_SEQ_CHECK_RET0(s); + len = (s->buffer_size - 1) - s->len; ret = vsnprintf(s->buffer + s->len, len, fmt, args); @@ -172,13 +194,15 @@ int trace_seq_puts(struct trace_seq *s, const char *str) { int len; - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET0(s); len = strlen(str); while (len > ((s->buffer_size - 1) - s->len)) expand_buffer(s); + TRACE_SEQ_CHECK_RET0(s); + memcpy(s->buffer + s->len, str, len); s->len += len; @@ -187,11 +211,13 @@ int trace_seq_puts(struct trace_seq *s, const char *str) int trace_seq_putc(struct trace_seq *s, unsigned char c) { - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET0(s); while (s->len >= (s->buffer_size - 1)) expand_buffer(s); + TRACE_SEQ_CHECK_RET0(s); + s->buffer[s->len++] = c; return 1; @@ -199,7 +225,7 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c) void trace_seq_terminate(struct trace_seq *s) { - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET(s); /* There's always one character left on the buffer */ s->buffer[s->len] = 0; @@ -208,5 +234,16 @@ void trace_seq_terminate(struct trace_seq *s) int trace_seq_do_printf(struct trace_seq *s) { TRACE_SEQ_CHECK(s); - return printf("%.*s", s->len, s->buffer); + + switch (s->state) { + case TRACE_SEQ__GOOD: + return printf("%.*s", s->len, s->buffer); + case TRACE_SEQ__BUFFER_POISONED: + puts("Usage of trace_seq after it was destroyed"); + break; + case TRACE_SEQ__MEM_ALLOC_FAILED: + puts("Can't allocate trace_seq buffer memory"); + break; + } + return -1; } diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt index 5032a142853e..ac6ecbb3e669 100644 --- a/tools/perf/Documentation/perf-archive.txt +++ b/tools/perf/Documentation/perf-archive.txt @@ -12,9 +12,9 @@ SYNOPSIS DESCRIPTION ----------- -This command runs runs perf-buildid-list --with-hits, and collects the files -with the buildids found so that analysis of perf.data contents can be possible -on another machine. +This command runs perf-buildid-list --with-hits, and collects the files with the +buildids found so that analysis of perf.data contents can be possible on another +machine. SEE ALSO diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 6a06cefe9642..52276a6d2b75 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -10,9 +10,9 @@ SYNOPSIS [verse] 'perf kvm' [--host] [--guest] [--guestmount= [--guestkallsyms= --guestmodules= | --guestvmlinux=]] - {top|record|report|diff|buildid-list} + {top|record|report|diff|buildid-list} [] 'perf kvm' [--host] [--guest] [--guestkallsyms= --guestmodules= - | --guestvmlinux=] {top|record|report|diff|buildid-list|stat} + | --guestvmlinux=] {top|record|report|diff|buildid-list|stat} [] 'perf kvm stat [record|report|live] [] DESCRIPTION @@ -24,10 +24,17 @@ There are a couple of variants of perf kvm: of an arbitrary workload. 'perf kvm record ' to record the performance counter profile - of an arbitrary workload and save it into a perf data file. If both - --host and --guest are input, the perf data file name is perf.data.kvm. - If there is no --host but --guest, the file name is perf.data.guest. - If there is no --guest but --host, the file name is perf.data.host. + of an arbitrary workload and save it into a perf data file. We set the + default behavior of perf kvm as --guest, so if neither --host nor --guest + is input, the perf data file name is perf.data.guest. If --host is input, + the perf data file name is perf.data.kvm. If you want to record data into + perf.data.host, please input --host --no-guest. The behaviors are shown as + following: + Default('') -> perf.data.guest + --host -> perf.data.kvm + --guest -> perf.data.guest + --host --guest -> perf.data.kvm + --host --no-guest -> perf.data.host 'perf kvm report' to display the performance counter profile information recorded via perf kvm record. @@ -37,7 +44,9 @@ There are a couple of variants of perf kvm: 'perf kvm buildid-list' to display the buildids found in a perf data file, so that other tools can be used to fetch packages with matching symbol tables - for use by perf report. + for use by perf report. As buildid is read from /sys/kernel/notes in os, then + if you want to list the buildid for guest, please make sure your perf data file + was captured with --guestmount in perf kvm record. 'perf kvm stat ' to run a command and gather performance counter statistics. @@ -58,14 +67,14 @@ There are a couple of variants of perf kvm: OPTIONS ------- -i:: ---input=:: +--input=:: Input file name. -o:: ---output:: +--output=:: Output file name. ---host=:: +--host:: Collect host side performance profile. ---guest=:: +--guest:: Collect guest side performance profile. --guestmount=:: Guest os root file system mount directory. Users mounts guest os @@ -84,6 +93,9 @@ OPTIONS kernel module information. Users copy it out from guest os. --guestvmlinux=:: Guest os kernel vmlinux. +-v:: +--verbose:: + Be more verbose (show counter open errors, etc). STAT REPORT OPTIONS ------------------- diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 43b42c4f4a91..c71b0f36d9e8 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -57,6 +57,8 @@ OPTIONS -t:: --tid=:: Record events on existing thread ID (comma separated list). + This option also disables inheritance by default. Enable it by adding + --inherit. -u:: --uid=:: @@ -66,8 +68,7 @@ OPTIONS --realtime=:: Collect data with this RT SCHED_FIFO priority. --D:: ---no-delay:: +--no-buffering:: Collect data without buffering. -c:: @@ -201,11 +202,16 @@ abort events and some memory events in precise mode on modern Intel CPUs. --transaction:: Record transaction flags for transaction related events. ---force-per-cpu:: -Force the use of per-cpu mmaps. By default, when tasks are specified (i.e. -p, --t or -u options) per-thread mmaps are created. This option overrides that and -forces per-cpu mmaps. A side-effect of that is that inheritance is -automatically enabled. Add the -i option also to disable inheritance. +--per-thread:: +Use per-thread mmaps. By default per-cpu mmaps are created. This option +overrides that and uses per-thread mmaps. A side-effect of that is that +inheritance is automatically disabled. --per-thread is ignored with a warning +if combined with -a or -C options. + +-D:: +--delay=:: +After starting the program, wait msecs before measuring. This is useful to +filter out the startup phase of the program, which is often very different. SEE ALSO -------- diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 10a279871251..8eab8a4bdeb8 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -237,6 +237,15 @@ OPTIONS Do not show entries which have an overhead under that percent. (Default: 0). +--header:: + Show header information in the perf.data file. This includes + various information like hostname, OS and perf version, cpu/mem + info, perf command line, event list and so on. Currently only + --stdio output supports this feature. + +--header-only:: + Show only perf.data header (forces --stdio). + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-annotate[1] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index e9cbfcddfa3f..05f9a0a6784c 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -115,7 +115,7 @@ OPTIONS -f:: --fields:: Comma separated list of fields to print. Options are: - comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff. + comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline. Field list can be prepended with the type, trace, sw or hw, to indicate to which event type the field list applies. e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace @@ -203,6 +203,18 @@ OPTIONS --show-kernel-path:: Try to resolve the path of [kernel.kallsyms] +--show-task-events + Display task related events (e.g. FORK, COMM, EXIT). + +--show-mmap-events + Display mmap related events (e.g. MMAP, MMAP2). + +--header + Show perf.data header. + +--header-only + Show only perf.data header. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script-perl[1], diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 80c7da6732f2..29ee857c09c6 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -133,7 +133,7 @@ use --per-core in addition to -a. (system-wide). The output includes the core number and the number of online logical processors on that physical processor. -D msecs:: ---initial-delay msecs:: +--delay msecs:: After starting the program, wait msecs before measuring. This is useful to filter out the startup phase of the program, which is often very different. diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 3ff8bd4f0b4d..bc5990c33dc0 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -8,8 +8,7 @@ perf-timechart - Tool to visualize total system behavior during a workload SYNOPSIS -------- [verse] -'perf timechart' record -'perf timechart' [] +'perf timechart' [] {record} [] DESCRIPTION ----------- @@ -21,8 +20,8 @@ There are two variants of perf timechart: 'perf timechart' to turn a trace into a Scalable Vector Graphics file, that can be viewed with popular SVG viewers such as 'Inkscape'. -OPTIONS -------- +TIMECHART OPTIONS +----------------- -o:: --output=:: Select the output file (default: output.svg) @@ -35,6 +34,9 @@ OPTIONS -P:: --power-only:: Only output the CPU power section of the diagram +-T:: +--tasks-only:: + Don't output processor state transitions -p:: --process:: Select the processes to display, by name or PID @@ -54,6 +56,38 @@ $ perf timechart Written 10.2 seconds of trace to output.svg. +Record system-wide timechart: + + $ perf timechart record + + then generate timechart and highlight 'gcc' tasks: + + $ perf timechart --highlight gcc + +-n:: +--proc-num:: + Print task info for at least given number of tasks. +-t:: +--topology:: + Sort CPUs according to topology. +--highlight=:: + Highlight tasks (using different color) that run more than given + duration or tasks with given name. If number is given it's interpreted + as number of nanoseconds. If non-numeric string is given it's + interpreted as task name. + +RECORD OPTIONS +-------------- +-P:: +--power-only:: + Record only power-related events +-T:: +--tasks-only:: + Record only tasks-related events +-g:: +--callchain:: + Do call-graph (stack chain/backtrace) recording + SEE ALSO -------- linkperf:perf-record[1] diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 7de01dd79688..cdd8d4946dba 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -50,7 +50,6 @@ Default is to monitor all CPUS. --count-filter=:: Only display functions with more events than this. --g:: --group:: Put the counters into a counter group. @@ -143,12 +142,12 @@ Default is to monitor all CPUS. --asm-raw:: Show raw instruction encoding of assembly instructions. --G:: +-g:: Enables call-graph (stack chain/backtrace) recording. --call-graph:: Setup and enable call-graph (stack chain/backtrace) recording, - implies -G. + implies -g. --max-stack:: Set the stack depth limit when parsing the callchain, anything diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 025de796067c..f41572d0dd76 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -1,7 +1,11 @@ tools/perf tools/scripts tools/lib/traceevent -tools/lib/lk +tools/lib/api +tools/lib/symbol/kallsyms.c +tools/lib/symbol/kallsyms.h +tools/include/asm/bug.h +tools/include/linux/compiler.h include/linux/const.h include/linux/perf_event.h include/linux/rbtree.h diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 4835618a5608..cb2e5868c8e8 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -60,8 +60,11 @@ endef # # Needed if no target specified: +# (Except for tags and TAGS targets. The reason is that the +# Makefile does not treat tags/TAGS as targets but as files +# and thus won't rebuilt them once they are in place.) # -all: +all tags TAGS: $(print_msg) $(make) @@ -71,9 +74,17 @@ all: clean: $(make) +# +# The build-test target is not really parallel, don't print the jobs info: +# +build-test: + @$(MAKE) -f tests/make --no-print-directory + # # All other targets get passed through: # %: $(print_msg) $(make) + +.PHONY: tags TAGS diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 7fc8f179cae7..7257e7e9e38a 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -76,6 +76,7 @@ $(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar +PKG_CONFIG = $(CROSS_COMPILE)pkg-config RM = rm -f LN = ln -f @@ -86,7 +87,7 @@ FLEX = flex BISON = bison STRIP = strip -LK_DIR = $(srctree)/tools/lib/lk/ +LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ # include config/Makefile by default and rule out @@ -105,7 +106,7 @@ ifeq ($(config),1) include config/Makefile endif -export prefix bindir sharedir sysconfdir +export prefix bindir sharedir sysconfdir DESTDIR # sparse is architecture-neutral, which means that we need to tell it # explicitly what architecture to check for. Fix this up for yours.. @@ -127,20 +128,20 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) ifneq ($(subdir),) - LK_PATH=$(OUTPUT)/../lib/lk/ + LIB_PATH=$(OUTPUT)/../lib/api/ else - LK_PATH=$(OUTPUT) + LIB_PATH=$(OUTPUT) endif else TE_PATH=$(TRACE_EVENT_DIR) - LK_PATH=$(LK_DIR) + LIB_PATH=$(LIB_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a export LIBTRACEEVENT -LIBLK = $(LK_PATH)liblk.a -export LIBLK +LIBAPIKFS = $(LIB_PATH)libapikfs.a +export LIBAPIKFS # python extension build directories PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ @@ -151,7 +152,7 @@ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) -PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPIKFS) $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \ @@ -202,6 +203,7 @@ $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c LIB_FILE=$(OUTPUT)libperf.a +LIB_H += ../lib/symbol/kallsyms.h LIB_H += ../../include/uapi/linux/perf_event.h LIB_H += ../../include/linux/rbtree.h LIB_H += ../../include/linux/list.h @@ -210,7 +212,7 @@ LIB_H += ../../include/linux/hash.h LIB_H += ../../include/linux/stringify.h LIB_H += util/include/linux/bitmap.h LIB_H += util/include/linux/bitops.h -LIB_H += util/include/linux/compiler.h +LIB_H += ../include/linux/compiler.h LIB_H += util/include/linux/const.h LIB_H += util/include/linux/ctype.h LIB_H += util/include/linux/kernel.h @@ -225,7 +227,7 @@ LIB_H += util/include/linux/string.h LIB_H += util/include/linux/types.h LIB_H += util/include/linux/linkage.h LIB_H += util/include/asm/asm-offsets.h -LIB_H += util/include/asm/bug.h +LIB_H += ../include/asm/bug.h LIB_H += util/include/asm/byteorder.h LIB_H += util/include/asm/hweight.h LIB_H += util/include/asm/swab.h @@ -312,6 +314,7 @@ LIB_OBJS += $(OUTPUT)util/evlist.o LIB_OBJS += $(OUTPUT)util/evsel.o LIB_OBJS += $(OUTPUT)util/exec_cmd.o LIB_OBJS += $(OUTPUT)util/help.o +LIB_OBJS += $(OUTPUT)util/kallsyms.o LIB_OBJS += $(OUTPUT)util/levenshtein.o LIB_OBJS += $(OUTPUT)util/parse-options.o LIB_OBJS += $(OUTPUT)util/parse-events.o @@ -353,6 +356,7 @@ LIB_OBJS += $(OUTPUT)util/pmu-bison.o LIB_OBJS += $(OUTPUT)util/trace-event-read.o LIB_OBJS += $(OUTPUT)util/trace-event-info.o LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o +LIB_OBJS += $(OUTPUT)util/trace-event.o LIB_OBJS += $(OUTPUT)util/svghelper.o LIB_OBJS += $(OUTPUT)util/sort.o LIB_OBJS += $(OUTPUT)util/hist.o @@ -438,7 +442,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o BUILTIN_OBJS += $(OUTPUT)builtin-mem.o -PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) +PERFLIBS = $(LIB_FILE) $(LIBAPIKFS) $(LIBTRACEEVENT) # We choose to avoid "if .. else if .. else .. endif endif" # because maintaining the nesting to match is a pain. If @@ -486,6 +490,7 @@ ifndef NO_SLANG LIB_OBJS += $(OUTPUT)ui/browsers/hists.o LIB_OBJS += $(OUTPUT)ui/browsers/map.o LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o + LIB_OBJS += $(OUTPUT)ui/browsers/header.o LIB_OBJS += $(OUTPUT)ui/tui/setup.o LIB_OBJS += $(OUTPUT)ui/tui/util.o LIB_OBJS += $(OUTPUT)ui/tui/helpline.o @@ -671,6 +676,9 @@ $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< @@ -710,26 +718,33 @@ $(LIB_FILE): $(LIB_OBJS) # libtraceevent.a TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch]) -$(LIBTRACEEVENT): $(TE_SOURCES) - $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) CFLAGS="-g -Wall $(EXTRA_CFLAGS)" libtraceevent.a +LIBTRACEEVENT_FLAGS = $(QUIET_SUBDIR1) O=$(OUTPUT) +LIBTRACEEVENT_FLAGS += CFLAGS="-g -Wall $(EXTRA_CFLAGS)" +LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ) + +$(LIBTRACEEVENT): $(TE_SOURCES) $(OUTPUT)PERF-CFLAGS + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) libtraceevent.a plugins $(LIBTRACEEVENT)-clean: $(call QUIET_CLEAN, libtraceevent) @$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null -LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) +install-traceevent-plugins: $(LIBTRACEEVENT) + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins + +LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch]) # if subdir is set, we've been called from above so target has been built # already -$(LIBLK): $(LIBLK_SOURCES) +$(LIBAPIKFS): $(LIBAPIKFS_SOURCES) ifeq ($(subdir),) - $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a + $(QUIET_SUBDIR0)$(LIB_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libapikfs.a endif -$(LIBLK)-clean: +$(LIBAPIKFS)-clean: ifeq ($(subdir),) - $(call QUIET_CLEAN, liblk) - @$(MAKE) -C $(LK_DIR) O=$(OUTPUT) clean >/dev/null + $(call QUIET_CLEAN, libapikfs) + @$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null endif help: @@ -785,7 +800,7 @@ cscope: ### Detect prefix changes TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ - $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) + $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):$(plugindir_SQ) $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS @FLAGS='$(TRACK_CFLAGS)'; \ @@ -840,16 +855,16 @@ ifndef NO_LIBPYTHON $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \ $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' endif - $(call QUIET_INSTALL, bash_completion-script) \ + $(call QUIET_INSTALL, perf_completion-script) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \ - $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' + $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' $(call QUIET_INSTALL, tests) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' -install: install-bin try-install-man +install: install-bin try-install-man install-traceevent-plugins install-python_ext: $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' @@ -868,12 +883,11 @@ config-clean: $(call QUIET_CLEAN, config) @$(MAKE) -C config/feature-checks clean >/dev/null -clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean +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-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)util/*-bison* $(OUTPUT)util/*-flex* - $(call QUIET_CLEAN, Documentation) - @$(MAKE) -C Documentation O=$(OUTPUT) clean >/dev/null + $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(python-clean) # diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index aacef07ebf31..42faf369211c 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -154,8 +154,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, } if (lookup_path(buf)) goto out; - free(buf); - buf = NULL; + zfree(&buf); } if (!strcmp(arch, "arm")) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 4087ab19823c..0da603b79b61 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -69,15 +69,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, if (he == NULL) return -ENOMEM; - ret = 0; - if (he->ms.sym != NULL) { - struct annotation *notes = symbol__annotation(he->ms.sym); - if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) - return -ENOMEM; - - ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - } - + ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); evsel->hists.stats.total_period += sample->period; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); return ret; @@ -188,8 +180,7 @@ find_next: * symbol, free he->ms.sym->src to signal we already * processed this symbol. */ - free(notes->src); - notes->src = NULL; + zfree(¬es->src); } } } @@ -241,7 +232,7 @@ static int __cmd_annotate(struct perf_annotate *ann) perf_session__fprintf_dsos(session, stdout); total_nr_samples = 0; - list_for_each_entry(pos, &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { struct hists *hists = &pos->hists; u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; @@ -373,7 +364,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) if (argc) { /* - * Special case: if there's an argument left then assume tha + * Special case: if there's an argument left then assume that * it's a symbol filter: */ if (argc > 1) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3b67ea2444bd..a77e31246c00 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -356,9 +356,10 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel, { struct perf_evsel *e; - list_for_each_entry(e, &evlist->entries, node) + evlist__for_each(evlist, e) { if (perf_evsel__match2(evsel, e)) return e; + } return NULL; } @@ -367,7 +368,7 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { struct hists *hists = &evsel->hists; hists__collapse_resort(hists, NULL); @@ -614,7 +615,7 @@ static void data_process(void) struct perf_evsel *evsel_base; bool first = true; - list_for_each_entry(evsel_base, &evlist_base->entries, node) { + evlist__for_each(evlist_base, evsel_base) { struct data__file *d; int i; @@ -654,7 +655,7 @@ static void data__free(struct data__file *d) for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) { struct diff_hpp_fmt *fmt = &d->fmt[col]; - free(fmt->header); + zfree(&fmt->header); } } @@ -769,6 +770,81 @@ static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size) return ret; } +static int __hpp__color_compare(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp, struct hist_entry *he, + int comparison_method) +{ + struct diff_hpp_fmt *dfmt = + container_of(fmt, struct diff_hpp_fmt, fmt); + struct hist_entry *pair = get_pair_fmt(he, dfmt); + double diff; + s64 wdiff; + char pfmt[20] = " "; + + if (!pair) + goto dummy_print; + + switch (comparison_method) { + case COMPUTE_DELTA: + if (pair->diff.computed) + diff = pair->diff.period_ratio_delta; + else + diff = compute_delta(he, pair); + + if (fabs(diff) < 0.01) + goto dummy_print; + scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); + return percent_color_snprintf(hpp->buf, hpp->size, + pfmt, diff); + case COMPUTE_RATIO: + if (he->dummy) + goto dummy_print; + if (pair->diff.computed) + diff = pair->diff.period_ratio; + else + diff = compute_ratio(he, pair); + + scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width); + return value_color_snprintf(hpp->buf, hpp->size, + pfmt, diff); + case COMPUTE_WEIGHTED_DIFF: + if (he->dummy) + goto dummy_print; + if (pair->diff.computed) + wdiff = pair->diff.wdiff; + else + wdiff = compute_wdiff(he, pair); + + scnprintf(pfmt, 20, "%%14ld", dfmt->header_width); + return color_snprintf(hpp->buf, hpp->size, + get_percent_color(wdiff), + pfmt, wdiff); + default: + BUG_ON(1); + } +dummy_print: + return scnprintf(hpp->buf, hpp->size, "%*s", + dfmt->header_width, pfmt); +} + +static int hpp__color_delta(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp, struct hist_entry *he) +{ + return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA); +} + +static int hpp__color_ratio(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp, struct hist_entry *he) +{ + return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO); +} + +static int hpp__color_wdiff(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp, struct hist_entry *he) +{ + return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF); +} + static void hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) { @@ -940,8 +1016,22 @@ static void data__hpp_register(struct data__file *d, int idx) fmt->entry = hpp__entry_global; /* TODO more colors */ - if (idx == PERF_HPP_DIFF__BASELINE) + switch (idx) { + case PERF_HPP_DIFF__BASELINE: fmt->color = hpp__color_baseline; + break; + case PERF_HPP_DIFF__DELTA: + fmt->color = hpp__color_delta; + break; + case PERF_HPP_DIFF__RATIO: + fmt->color = hpp__color_ratio; + break; + case PERF_HPP_DIFF__WEIGHTED_DIFF: + fmt->color = hpp__color_wdiff; + break; + default: + break; + } init_header(d, dfmt); perf_hpp__column_register(fmt); @@ -1000,8 +1090,7 @@ static int data_init(int argc, const char **argv) data__files_cnt = argc; use_default = false; } - } else if (symbol_conf.default_guest_vmlinux_name || - symbol_conf.default_guest_kallsyms) { + } else if (perf_guest) { defaults[0] = "perf.data.host"; defaults[1] = "perf.data.guest"; } diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 20b0f12763b0..c99e0de7e54a 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -29,7 +29,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details if (session == NULL) return -ENOMEM; - list_for_each_entry(pos, &session->evlist->entries, node) + evlist__for_each(session->evlist, pos) perf_evsel__fprintf(pos, details, stdout); perf_session__delete(session); diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6a2508589460..b3466018bbd7 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -22,14 +22,13 @@ #include struct perf_inject { - struct perf_tool tool; - bool build_ids; - bool sched_stat; - const char *input_name; - int pipe_output, - output; - u64 bytes_written; - struct list_head samples; + struct perf_tool tool; + bool build_ids; + bool sched_stat; + const char *input_name; + struct perf_data_file output; + u64 bytes_written; + struct list_head samples; }; struct event_entry { @@ -42,21 +41,14 @@ static int perf_event__repipe_synth(struct perf_tool *tool, union perf_event *event) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - uint32_t size; - void *buf = event; + ssize_t size; - size = event->header.size; - - while (size) { - int ret = write(inject->output, buf, size); - if (ret < 0) - return -errno; - - size -= ret; - buf += ret; - inject->bytes_written += ret; - } + size = perf_data_file__write(&inject->output, event, + event->header.size); + if (size < 0) + return -errno; + inject->bytes_written += size; return 0; } @@ -80,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool, if (ret) return ret; - if (!inject->pipe_output) + if (&inject->output.is_pipe) return 0; return perf_event__repipe_synth(tool, event); @@ -355,6 +347,7 @@ static int __cmd_inject(struct perf_inject *inject) .path = inject->input_name, .mode = PERF_DATA_MODE_READ, }; + struct perf_data_file *file_out = &inject->output; signal(SIGINT, sig_handler); @@ -376,7 +369,7 @@ static int __cmd_inject(struct perf_inject *inject) inject->tool.ordered_samples = true; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { const char *name = perf_evsel__name(evsel); if (!strcmp(name, "sched:sched_switch")) { @@ -391,14 +384,14 @@ static int __cmd_inject(struct perf_inject *inject) } } - if (!inject->pipe_output) - lseek(inject->output, session->header.data_offset, SEEK_SET); + if (!file_out->is_pipe) + lseek(file_out->fd, session->header.data_offset, SEEK_SET); ret = perf_session__process_events(session, &inject->tool); - if (!inject->pipe_output) { + if (!file_out->is_pipe) { session->header.data_size = inject->bytes_written; - perf_session__write_header(session, session->evlist, inject->output, true); + perf_session__write_header(session, session->evlist, file_out->fd, true); } perf_session__delete(session); @@ -427,14 +420,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples), + .output = { + .path = "-", + .mode = PERF_DATA_MODE_WRITE, + }, }; - const char *output_name = "-"; const struct option options[] = { OPT_BOOLEAN('b', "build-ids", &inject.build_ids, "Inject build-ids into the output stream"), OPT_STRING('i', "input", &inject.input_name, "file", "input file name"), - OPT_STRING('o', "output", &output_name, "file", + OPT_STRING('o', "output", &inject.output.path, "file", "output file name"), OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, "Merge sched-stat and sched-switch for getting events " @@ -456,16 +452,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) if (argc) usage_with_options(inject_usage, options); - if (!strcmp(output_name, "-")) { - inject.pipe_output = 1; - inject.output = STDOUT_FILENO; - } else { - inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, - S_IRUSR | S_IWUSR); - if (inject.output < 0) { - perror("failed to create output file"); - return -1; - } + if (perf_data_file__open(&inject.output)) { + perror("failed to create output file"); + return -1; } if (symbol__init() < 0) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index f8bf5f244d77..a7350519c63f 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -13,7 +13,7 @@ #include "util/parse-options.h" #include "util/trace-event.h" #include "util/debug.h" -#include +#include #include "util/tool.h" #include "util/stat.h" #include "util/top.h" @@ -89,7 +89,7 @@ struct exit_reasons_table { struct perf_kvm_stat { struct perf_tool tool; - struct perf_record_opts opts; + struct record_opts opts; struct perf_evlist *evlist; struct perf_session *session; @@ -1158,9 +1158,7 @@ out: if (kvm->timerfd >= 0) close(kvm->timerfd); - if (pollfds) - free(pollfds); - + free(pollfds); return err; } @@ -1176,7 +1174,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) * Note: exclude_{guest,host} do not apply here. * This command processes KVM tracepoints from host only */ - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { struct perf_event_attr *attr = &pos->attr; /* make sure these *are* set */ @@ -1232,7 +1230,7 @@ static int read_events(struct perf_kvm_stat *kvm) .ordered_samples = true, }; struct perf_data_file file = { - .path = input_name, + .path = kvm->file_name, .mode = PERF_DATA_MODE_READ, }; @@ -1558,10 +1556,8 @@ out: if (kvm->session) perf_session__delete(kvm->session); kvm->session = NULL; - if (kvm->evlist) { - perf_evlist__delete_maps(kvm->evlist); + if (kvm->evlist) perf_evlist__delete(kvm->evlist); - } return err; } @@ -1690,6 +1686,8 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) "file", "file saving guest os /proc/kallsyms"), OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, "file", "file saving guest os /proc/modules"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show counter open errors, etc)"), OPT_END() }; @@ -1711,12 +1709,7 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) perf_guest = 1; if (!file_name) { - if (perf_host && !perf_guest) - file_name = strdup("perf.data.host"); - else if (!perf_host && perf_guest) - file_name = strdup("perf.data.guest"); - else - file_name = strdup("perf.data.kvm"); + file_name = get_filename_for_perf_kvm(); if (!file_name) { pr_err("Failed to allocate memory for filename\n"); diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 31c00f186da1..2e3ade69a58e 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -62,7 +62,6 @@ static int dump_raw_samples(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel __maybe_unused, struct machine *machine) { struct perf_mem *mem = container_of(tool, struct perf_mem, tool); @@ -112,10 +111,10 @@ dump_raw_samples(struct perf_tool *tool, static int process_sample_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel, + struct perf_evsel *evsel __maybe_unused, struct machine *machine) { - return dump_raw_samples(tool, event, sample, evsel, machine); + return dump_raw_samples(tool, event, sample, machine); } static int report_raw_events(struct perf_mem *mem) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 6ea9e85bdc00..78948882e3de 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@ #include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" -#include +#include #include "util/parse-options.h" #include "util/probe-finder.h" #include "util/probe-event.h" @@ -59,7 +59,7 @@ static struct { struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; - const char *target; + char *target; int max_probe_points; struct strfilter *filter; } params; @@ -98,7 +98,10 @@ static int set_target(const char *ptr) * short module name. */ if (!params.target && ptr && *ptr == '/') { - params.target = ptr; + params.target = strdup(ptr); + if (!params.target) + return -ENOMEM; + found = 1; buf = ptr + (strlen(ptr) - 3); @@ -116,6 +119,9 @@ static int parse_probe_event_argv(int argc, const char **argv) char *buf; found_target = set_target(argv[0]); + if (found_target < 0) + return found_target; + if (found_target && argc == 1) return 0; @@ -169,6 +175,7 @@ static int opt_set_target(const struct option *opt, const char *str, int unset __maybe_unused) { int ret = -ENOENT; + char *tmp; if (str && !params.target) { if (!strcmp(opt->long_name, "exec")) @@ -180,7 +187,19 @@ static int opt_set_target(const struct option *opt, const char *str, else return ret; - params.target = str; + /* Expand given path to absolute path, except for modulename */ + if (params.uprobes || strchr(str, '/')) { + tmp = realpath(str, NULL); + if (!tmp) { + pr_warning("Failed to get the absolute path of %s: %m\n", str); + return ret; + } + } else { + tmp = strdup(str); + if (!tmp) + return -ENOMEM; + } + params.target = tmp; ret = 0; } @@ -204,7 +223,6 @@ static int opt_show_lines(const struct option *opt __maybe_unused, params.show_lines = true; ret = parse_line_range_desc(str, ¶ms.line_range); - INIT_LIST_HEAD(¶ms.line_range.line_list); return ret; } @@ -250,7 +268,28 @@ static int opt_set_filter(const struct option *opt __maybe_unused, return 0; } -int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) +static void init_params(void) +{ + line_range__init(¶ms.line_range); +} + +static void cleanup_params(void) +{ + int i; + + for (i = 0; i < params.nevents; i++) + clear_perf_probe_event(params.events + i); + if (params.dellist) + strlist__delete(params.dellist); + line_range__clear(¶ms.line_range); + free(params.target); + if (params.filter) + strfilter__delete(params.filter); + memset(¶ms, 0, sizeof(params)); +} + +static int +__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const probe_usage[] = { "perf probe [] 'PROBEDEF' ['PROBEDEF' ...]", @@ -404,6 +443,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) ret = show_available_funcs(params.target, params.filter, params.uprobes); strfilter__delete(params.filter); + params.filter = NULL; if (ret < 0) pr_err(" Error: Failed to show functions." " (%d)\n", ret); @@ -411,7 +451,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) } #ifdef HAVE_DWARF_SUPPORT - if (params.show_lines && !params.uprobes) { + if (params.show_lines) { if (params.mod_events) { pr_err(" Error: Don't use --line with" " --add/--del.\n"); @@ -443,6 +483,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) params.filter, params.show_ext_vars); strfilter__delete(params.filter); + params.filter = NULL; if (ret < 0) pr_err(" Error: Failed to show vars. (%d)\n", ret); return ret; @@ -451,7 +492,6 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) if (params.dellist) { ret = del_perf_probe_events(params.dellist); - strlist__delete(params.dellist); if (ret < 0) { pr_err(" Error: Failed to delete events. (%d)\n", ret); return ret; @@ -470,3 +510,14 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) } return 0; } + +int cmd_probe(int argc, const char **argv, const char *prefix) +{ + int ret; + + init_params(); + ret = __cmd_probe(argc, argv, prefix); + cleanup_params(); + + return ret; +} diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 7c8020a32784..3c394bf16fa8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -62,9 +62,9 @@ static void __handle_on_exit_funcs(void) } #endif -struct perf_record { +struct record { struct perf_tool tool; - struct perf_record_opts opts; + struct record_opts opts; u64 bytes_written; struct perf_data_file file; struct perf_evlist *evlist; @@ -76,46 +76,27 @@ struct perf_record { long samples; }; -static int do_write_output(struct perf_record *rec, void *buf, size_t size) +static int record__write(struct record *rec, void *bf, size_t size) { - struct perf_data_file *file = &rec->file; - - while (size) { - ssize_t ret = write(file->fd, buf, size); - - if (ret < 0) { - pr_err("failed to write perf data, error: %m\n"); - return -1; - } - - size -= ret; - buf += ret; - - rec->bytes_written += ret; + if (perf_data_file__write(rec->session->file, bf, size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; } + rec->bytes_written += size; return 0; } -static int write_output(struct perf_record *rec, void *buf, size_t size) -{ - return do_write_output(rec, buf, size); -} - static int process_synthesized_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct perf_record *rec = container_of(tool, struct perf_record, tool); - if (write_output(rec, event, event->header.size) < 0) - return -1; - - return 0; + struct record *rec = container_of(tool, struct record, tool); + return record__write(rec, event, event->header.size); } -static int perf_record__mmap_read(struct perf_record *rec, - struct perf_mmap *md) +static int record__mmap_read(struct record *rec, struct perf_mmap *md) { unsigned int head = perf_mmap__read_head(md); unsigned int old = md->prev; @@ -136,7 +117,7 @@ static int perf_record__mmap_read(struct perf_record *rec, size = md->mask + 1 - (old & md->mask); old += size; - if (write_output(rec, buf, size) < 0) { + if (record__write(rec, buf, size) < 0) { rc = -1; goto out; } @@ -146,7 +127,7 @@ static int perf_record__mmap_read(struct perf_record *rec, size = head - old; old += size; - if (write_output(rec, buf, size) < 0) { + if (record__write(rec, buf, size) < 0) { rc = -1; goto out; } @@ -171,9 +152,9 @@ static void sig_handler(int sig) signr = sig; } -static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg) +static void record__sig_exit(int exit_status __maybe_unused, void *arg) { - struct perf_record *rec = arg; + struct record *rec = arg; int status; if (rec->evlist->workload.pid > 0) { @@ -191,18 +172,18 @@ static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg) signal(signr, SIG_DFL); } -static int perf_record__open(struct perf_record *rec) +static int record__open(struct record *rec) { char msg[512]; struct perf_evsel *pos; struct perf_evlist *evlist = rec->evlist; struct perf_session *session = rec->session; - struct perf_record_opts *opts = &rec->opts; + struct record_opts *opts = &rec->opts; int rc = 0; perf_evlist__config(evlist, opts); - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { try_again: if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) { @@ -232,7 +213,7 @@ try_again: "Consider increasing " "/proc/sys/kernel/perf_event_mlock_kb,\n" "or try again with a smaller value of -m/--mmap_pages.\n" - "(current value: %d)\n", opts->mmap_pages); + "(current value: %u)\n", opts->mmap_pages); rc = -errno; } else { pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); @@ -247,7 +228,7 @@ out: return rc; } -static int process_buildids(struct perf_record *rec) +static int process_buildids(struct record *rec) { struct perf_data_file *file = &rec->file; struct perf_session *session = rec->session; @@ -262,9 +243,9 @@ static int process_buildids(struct perf_record *rec) size, &build_id__mark_dso_hit_ops); } -static void perf_record__exit(int status, void *arg) +static void record__exit(int status, void *arg) { - struct perf_record *rec = arg; + struct record *rec = arg; struct perf_data_file *file = &rec->file; if (status != 0) @@ -320,14 +301,14 @@ static struct perf_event_header finished_round_event = { .type = PERF_RECORD_FINISHED_ROUND, }; -static int perf_record__mmap_read_all(struct perf_record *rec) +static int record__mmap_read_all(struct record *rec) { int i; int rc = 0; for (i = 0; i < rec->evlist->nr_mmaps; i++) { if (rec->evlist->mmap[i].base) { - if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { + if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { rc = -1; goto out; } @@ -335,16 +316,14 @@ static int perf_record__mmap_read_all(struct perf_record *rec) } if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) - rc = write_output(rec, &finished_round_event, - sizeof(finished_round_event)); + rc = record__write(rec, &finished_round_event, sizeof(finished_round_event)); out: return rc; } -static void perf_record__init_features(struct perf_record *rec) +static void record__init_features(struct record *rec) { - struct perf_evlist *evsel_list = rec->evlist; struct perf_session *session = rec->session; int feat; @@ -354,32 +333,46 @@ static void perf_record__init_features(struct perf_record *rec) if (rec->no_buildid) perf_header__clear_feat(&session->header, HEADER_BUILD_ID); - if (!have_tracepoints(&evsel_list->entries)) + if (!have_tracepoints(&rec->evlist->entries)) perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); if (!rec->opts.branch_stack) perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); } -static int __cmd_record(struct perf_record *rec, int argc, const char **argv) +static volatile int workload_exec_errno; + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 + * if the fork fails, since we asked by setting its + * want_signal to true. + */ +static void workload_exec_failed_signal(int signo, siginfo_t *info, + void *ucontext __maybe_unused) +{ + workload_exec_errno = info->si_value.sival_int; + done = 1; + signr = signo; + child_finished = 1; +} + +static int __cmd_record(struct record *rec, int argc, const char **argv) { int err; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; struct perf_tool *tool = &rec->tool; - struct perf_record_opts *opts = &rec->opts; - struct perf_evlist *evsel_list = rec->evlist; + struct record_opts *opts = &rec->opts; struct perf_data_file *file = &rec->file; struct perf_session *session; bool disabled = false; rec->progname = argv[0]; - on_exit(perf_record__sig_exit, rec); + on_exit(record__sig_exit, rec); signal(SIGCHLD, sig_handler); signal(SIGINT, sig_handler); - signal(SIGUSR1, sig_handler); signal(SIGTERM, sig_handler); session = perf_session__new(file, false, NULL); @@ -390,37 +383,37 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->session = session; - perf_record__init_features(rec); + record__init_features(rec); if (forks) { - err = perf_evlist__prepare_workload(evsel_list, &opts->target, + err = perf_evlist__prepare_workload(rec->evlist, &opts->target, argv, file->is_pipe, - true); + workload_exec_failed_signal); if (err < 0) { pr_err("Couldn't run the workload!\n"); goto out_delete_session; } } - if (perf_record__open(rec) != 0) { + if (record__open(rec) != 0) { err = -1; goto out_delete_session; } - if (!evsel_list->nr_groups) + if (!rec->evlist->nr_groups) perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); /* - * perf_session__delete(session) will be called at perf_record__exit() + * perf_session__delete(session) will be called at record__exit() */ - on_exit(perf_record__exit, rec); + on_exit(record__exit, rec); if (file->is_pipe) { err = perf_header__write_pipe(file->fd); if (err < 0) goto out_delete_session; } else { - err = perf_session__write_header(session, evsel_list, + err = perf_session__write_header(session, rec->evlist, file->fd, false); if (err < 0) goto out_delete_session; @@ -444,7 +437,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } - if (have_tracepoints(&evsel_list->entries)) { + if (have_tracepoints(&rec->evlist->entries)) { /* * FIXME err <= 0 here actually means that * there were no tracepoints so its not really @@ -453,7 +446,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * return this more properly and also * propagate errors that now are calling die() */ - err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list, + err = perf_event__synthesize_tracing_data(tool, file->fd, rec->evlist, process_synthesized_event); if (err <= 0) { pr_err("Couldn't record tracing data.\n"); @@ -485,7 +478,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_event__synthesize_guest_os, tool); } - err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads, + err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, process_synthesized_event, opts->sample_address); if (err != 0) goto out_delete_session; @@ -506,19 +499,24 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * (apart from group members) have enable_on_exec=1 set, * so don't spoil it by prematurely enabling them. */ - if (!target__none(&opts->target)) - perf_evlist__enable(evsel_list); + if (!target__none(&opts->target) && !opts->initial_delay) + perf_evlist__enable(rec->evlist); /* * Let the child rip */ if (forks) - perf_evlist__start_workload(evsel_list); + perf_evlist__start_workload(rec->evlist); + + if (opts->initial_delay) { + usleep(opts->initial_delay * 1000); + perf_evlist__enable(rec->evlist); + } for (;;) { int hits = rec->samples; - if (perf_record__mmap_read_all(rec) < 0) { + if (record__mmap_read_all(rec) < 0) { err = -1; goto out_delete_session; } @@ -526,7 +524,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) if (hits == rec->samples) { if (done) break; - err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); + err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); waking++; } @@ -536,11 +534,19 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * disable events in this case. */ if (done && !disabled && !target__none(&opts->target)) { - perf_evlist__disable(evsel_list); + perf_evlist__disable(rec->evlist); disabled = true; } } + if (forks && workload_exec_errno) { + char msg[512]; + const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); + pr_err("Workload failed: %s\n", emsg); + err = -1; + goto out_delete_session; + } + if (quiet || signr == SIGUSR1) return 0; @@ -677,7 +683,7 @@ static int get_stack_size(char *str, unsigned long *_size) } #endif /* HAVE_LIBUNWIND_SUPPORT */ -int record_parse_callchain(const char *arg, struct perf_record_opts *opts) +int record_parse_callchain(const char *arg, struct record_opts *opts) { char *tok, *name, *saveptr = NULL; char *buf; @@ -733,7 +739,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) return ret; } -static void callchain_debug(struct perf_record_opts *opts) +static void callchain_debug(struct record_opts *opts) { pr_debug("callchain: type %d\n", opts->call_graph); @@ -746,7 +752,7 @@ int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset) { - struct perf_record_opts *opts = opt->value; + struct record_opts *opts = opt->value; int ret; /* --no-call-graph */ @@ -767,7 +773,7 @@ int record_callchain_opt(const struct option *opt, const char *arg __maybe_unused, int unset __maybe_unused) { - struct perf_record_opts *opts = opt->value; + struct record_opts *opts = opt->value; if (opts->call_graph == CALLCHAIN_NONE) opts->call_graph = CALLCHAIN_FP; @@ -783,8 +789,8 @@ static const char * const record_usage[] = { }; /* - * XXX Ideally would be local to cmd_record() and passed to a perf_record__new - * because we need to have access to it in perf_record__exit, that is called + * XXX Ideally would be local to cmd_record() and passed to a record__new + * because we need to have access to it in record__exit, that is called * after cmd_record() exits, but since record_options need to be accessible to * builtin-script, leave it here. * @@ -792,7 +798,7 @@ static const char * const record_usage[] = { * * Just say no to tons of global variables, sigh. */ -static struct perf_record record = { +static struct record record = { .opts = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, @@ -800,6 +806,7 @@ static struct perf_record record = { .freq = 4000, .target = { .uses_mmap = true, + .default_per_cpu = true, }, }, }; @@ -815,7 +822,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp"; /* * XXX Will stay a global variable till we fix builtin-script.c to stop messing * with it and switch to use the library functions in perf_evlist that came - * from builtin-record.c, i.e. use perf_record_opts, + * from builtin-record.c, i.e. use record_opts, * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', * using pipes, etc. */ @@ -831,7 +838,7 @@ const struct option record_options[] = { "record events on existing thread id"), OPT_INTEGER('r', "realtime", &record.realtime_prio, "collect data with this RT SCHED_FIFO priority"), - OPT_BOOLEAN('D', "no-delay", &record.opts.no_delay, + OPT_BOOLEAN(0, "no-buffering", &record.opts.no_buffering, "collect data without buffering"), OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples, "collect raw sample records from all opened counters"), @@ -842,8 +849,9 @@ const struct option record_options[] = { OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), OPT_STRING('o', "output", &record.file.path, "file", "output file name"), - OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, - "child tasks do not inherit counters"), + OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit, + &record.opts.no_inherit_set, + "child tasks do not inherit counters"), OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", "number of mmap data pages", @@ -874,6 +882,8 @@ const struct option record_options[] = { OPT_CALLBACK('G', "cgroup", &record.evlist, "name", "monitor event in cgroup name only", parse_cgroups), + OPT_UINTEGER('D', "delay", &record.opts.initial_delay, + "ms to wait before starting measurement after program start"), OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", "user to profile"), @@ -888,24 +898,21 @@ const struct option record_options[] = { "sample by weight (on special events only)"), OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, "sample transaction flags (special events only)"), - OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu, - "force the use of per-cpu mmaps"), + OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, + "use per-thread mmaps"), OPT_END() }; int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) { int err = -ENOMEM; - struct perf_evlist *evsel_list; - struct perf_record *rec = &record; + struct record *rec = &record; char errbuf[BUFSIZ]; - evsel_list = perf_evlist__new(); - if (evsel_list == NULL) + rec->evlist = perf_evlist__new(); + if (rec->evlist == NULL) return -ENOMEM; - rec->evlist = evsel_list; - argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc && target__none(&rec->opts.target)) @@ -932,12 +939,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (rec->no_buildid_cache || rec->no_buildid) disable_buildid_cache(); - if (evsel_list->nr_entries == 0 && - perf_evlist__add_default(evsel_list) < 0) { + if (rec->evlist->nr_entries == 0 && + perf_evlist__add_default(rec->evlist) < 0) { pr_err("Not enough memory for event selector list\n"); goto out_symbol_exit; } + if (rec->opts.target.tid && !rec->opts.no_inherit_set) + rec->opts.no_inherit = true; + err = target__validate(&rec->opts.target); if (err) { target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); @@ -956,20 +966,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) } err = -ENOMEM; - if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) + if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); - if (perf_record_opts__config(&rec->opts)) { + if (record_opts__config(&rec->opts)) { err = -EINVAL; - goto out_free_fd; + goto out_symbol_exit; } err = __cmd_record(&record, argc, argv); - - perf_evlist__munmap(evsel_list); - perf_evlist__close(evsel_list); -out_free_fd: - perf_evlist__delete_maps(evsel_list); out_symbol_exit: symbol__exit(); return err; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8cf8e66ba594..3c53ec268fbc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -39,7 +39,7 @@ #include #include -struct perf_report { +struct report { struct perf_tool tool; struct perf_session *session; bool force, use_tui, use_gtk, use_stdio; @@ -49,6 +49,8 @@ struct perf_report { bool show_threads; bool inverted_callchain; bool mem_mode; + bool header; + bool header_only; int max_stack; struct perf_read_values show_threads_values; const char *pretty_printing_style; @@ -58,14 +60,14 @@ struct perf_report { DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); }; -static int perf_report_config(const char *var, const char *value, void *cb) +static int report__config(const char *var, const char *value, void *cb) { if (!strcmp(var, "report.group")) { symbol_conf.event_group = perf_config_bool(var, value); return 0; } if (!strcmp(var, "report.percent-limit")) { - struct perf_report *rep = cb; + struct report *rep = cb; rep->min_percent = strtof(value, NULL); return 0; } @@ -73,31 +75,22 @@ static int perf_report_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } -static int perf_report__add_mem_hist_entry(struct perf_tool *tool, - struct addr_location *al, - struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine, - union perf_event *event) +static int report__add_mem_hist_entry(struct perf_tool *tool, struct addr_location *al, + struct perf_sample *sample, struct perf_evsel *evsel, + union perf_event *event) { - struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct report *rep = container_of(tool, struct report, tool); struct symbol *parent = NULL; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - int err = 0; struct hist_entry *he; struct mem_info *mi, *mx; uint64_t cost; + int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - if ((sort__has_parent || symbol_conf.use_callchain) && - sample->callchain) { - err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al, - rep->max_stack); - if (err) - return err; - } + if (err) + return err; - mi = machine__resolve_mem(machine, al->thread, sample, cpumode); + mi = machine__resolve_mem(al->machine, al->thread, sample, cpumode); if (!mi) return -ENOMEM; @@ -120,77 +113,36 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, if (!he) return -ENOMEM; - /* - * In the TUI browser, we are doing integrated annotation, - * so we don't allocate the extra space needed because the stdio - * code will not use it. - */ - if (sort__has_sym && he->ms.sym && use_browser > 0) { - struct annotation *notes = symbol__annotation(he->ms.sym); + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + if (err) + goto out; - assert(evsel != NULL); - - if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) - goto out; - - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - if (err) - goto out; - } - - if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { - struct annotation *notes; - - mx = he->mem_info; - - notes = symbol__annotation(mx->daddr.sym); - if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) - goto out; - - err = symbol__inc_addr_samples(mx->daddr.sym, - mx->daddr.map, - evsel->idx, - mx->daddr.al_addr); - if (err) - goto out; - } + mx = he->mem_info; + err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); + if (err) + goto out; evsel->hists.stats.total_period += cost; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); - err = 0; - - if (symbol_conf.use_callchain) { - err = callchain_append(he->callchain, - &callchain_cursor, - sample->period); - } + err = hist_entry__append_callchain(he, sample); out: return err; } -static int perf_report__add_branch_hist_entry(struct perf_tool *tool, - struct addr_location *al, - struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine) +static int report__add_branch_hist_entry(struct perf_tool *tool, struct addr_location *al, + struct perf_sample *sample, struct perf_evsel *evsel) { - struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct report *rep = container_of(tool, struct report, tool); struct symbol *parent = NULL; - int err = 0; unsigned i; struct hist_entry *he; struct branch_info *bi, *bx; + int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - if ((sort__has_parent || symbol_conf.use_callchain) - && sample->callchain) { - err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al, - rep->max_stack); - if (err) - return err; - } + if (err) + return err; - bi = machine__resolve_bstack(machine, al->thread, + bi = machine__resolve_bstack(al->machine, al->thread, sample->branch_stack); if (!bi) return -ENOMEM; @@ -212,35 +164,15 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, 1, 1, 0); if (he) { - struct annotation *notes; bx = he->branch_info; - if (bx->from.sym && use_browser == 1 && sort__has_sym) { - notes = symbol__annotation(bx->from.sym); - if (!notes->src - && symbol__alloc_hist(bx->from.sym) < 0) - goto out; + err = addr_map_symbol__inc_samples(&bx->from, evsel->idx); + if (err) + goto out; - err = symbol__inc_addr_samples(bx->from.sym, - bx->from.map, - evsel->idx, - bx->from.al_addr); - if (err) - goto out; - } + err = addr_map_symbol__inc_samples(&bx->to, evsel->idx); + if (err) + goto out; - if (bx->to.sym && use_browser == 1 && sort__has_sym) { - notes = symbol__annotation(bx->to.sym); - if (!notes->src - && symbol__alloc_hist(bx->to.sym) < 0) - goto out; - - err = symbol__inc_addr_samples(bx->to.sym, - bx->to.map, - evsel->idx, - bx->to.al_addr); - if (err) - goto out; - } evsel->hists.stats.total_period += 1; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); } else @@ -252,24 +184,16 @@ out: return err; } -static int perf_evsel__add_hist_entry(struct perf_tool *tool, - struct perf_evsel *evsel, - struct addr_location *al, - struct perf_sample *sample, - struct machine *machine) +static int report__add_hist_entry(struct perf_tool *tool, struct perf_evsel *evsel, + struct addr_location *al, struct perf_sample *sample) { - struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct report *rep = container_of(tool, struct report, tool); struct symbol *parent = NULL; - int err = 0; struct hist_entry *he; + int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { - err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al, - rep->max_stack); - if (err) - return err; - } + if (err) + return err; he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, sample->period, sample->weight, @@ -277,30 +201,11 @@ static int perf_evsel__add_hist_entry(struct perf_tool *tool, if (he == NULL) return -ENOMEM; - if (symbol_conf.use_callchain) { - err = callchain_append(he->callchain, - &callchain_cursor, - sample->period); - if (err) - return err; - } - /* - * Only in the TUI browser we are doing integrated annotation, - * so we don't allocated the extra space needed because the stdio - * code will not use it. - */ - if (he->ms.sym != NULL && use_browser == 1 && sort__has_sym) { - struct annotation *notes = symbol__annotation(he->ms.sym); - - assert(evsel != NULL); - - err = -ENOMEM; - if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) - goto out; - - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - } + err = hist_entry__append_callchain(he, sample); + if (err) + goto out; + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); evsel->hists.stats.total_period += sample->period; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); out: @@ -314,13 +219,13 @@ static int process_sample_event(struct perf_tool *tool, struct perf_evsel *evsel, struct machine *machine) { - struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct report *rep = container_of(tool, struct report, tool); struct addr_location al; int ret; if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { - fprintf(stderr, "problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %d event, skipping it.\n", + event->header.type); return -1; } @@ -331,21 +236,18 @@ static int process_sample_event(struct perf_tool *tool, return 0; if (sort__mode == SORT_MODE__BRANCH) { - ret = perf_report__add_branch_hist_entry(tool, &al, sample, - evsel, machine); + ret = report__add_branch_hist_entry(tool, &al, sample, evsel); if (ret < 0) pr_debug("problem adding lbr entry, skipping event\n"); } else if (rep->mem_mode == 1) { - ret = perf_report__add_mem_hist_entry(tool, &al, sample, - evsel, machine, event); + ret = report__add_mem_hist_entry(tool, &al, sample, evsel, event); if (ret < 0) pr_debug("problem adding mem entry, skipping event\n"); } else { if (al.map != NULL) al.map->dso->hit = 1; - ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample, - machine); + ret = report__add_hist_entry(tool, evsel, &al, sample); if (ret < 0) pr_debug("problem incrementing symbol period, skipping event\n"); } @@ -358,7 +260,7 @@ static int process_read_event(struct perf_tool *tool, struct perf_evsel *evsel, struct machine *machine __maybe_unused) { - struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct report *rep = container_of(tool, struct report, tool); if (rep->show_threads) { const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; @@ -377,7 +279,7 @@ static int process_read_event(struct perf_tool *tool, } /* For pipe mode, sample_type is not currently set */ -static int perf_report__setup_sample_type(struct perf_report *rep) +static int report__setup_sample_type(struct report *rep) { struct perf_session *session = rep->session; u64 sample_type = perf_evlist__combined_sample_type(session->evlist); @@ -422,8 +324,7 @@ static void sig_handler(int sig __maybe_unused) session_done = 1; } -static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, - struct hists *hists, +static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report *rep, const char *evname, FILE *fp) { size_t ret; @@ -460,12 +361,12 @@ static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, } static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, - struct perf_report *rep, + struct report *rep, const char *help) { struct perf_evsel *pos; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { struct hists *hists = &pos->hists; const char *evname = perf_evsel__name(pos); @@ -473,7 +374,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, !perf_evsel__is_group_leader(pos)) continue; - hists__fprintf_nr_sample_events(rep, hists, evname, stdout); + hists__fprintf_nr_sample_events(hists, rep, evname, stdout); hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout); fprintf(stdout, "\n\n"); } @@ -493,43 +394,11 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, return 0; } -static int __cmd_report(struct perf_report *rep) +static void report__warn_kptr_restrict(const struct report *rep) { - int ret = -EINVAL; - u64 nr_samples; - struct perf_session *session = rep->session; - struct perf_evsel *pos; - struct map *kernel_map; - struct kmap *kernel_kmap; - const char *help = "For a higher level overview, try: perf report --sort comm,dso"; - struct ui_progress prog; - struct perf_data_file *file = session->file; + struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION]; + struct kmap *kernel_kmap = map__kmap(kernel_map); - signal(SIGINT, sig_handler); - - if (rep->cpu_list) { - ret = perf_session__cpu_bitmap(session, rep->cpu_list, - rep->cpu_bitmap); - if (ret) - return ret; - } - - if (use_browser <= 0) - perf_session__fprintf_info(session, stdout, rep->show_full_info); - - if (rep->show_threads) - perf_read_values_init(&rep->show_threads_values); - - ret = perf_report__setup_sample_type(rep); - if (ret) - return ret; - - ret = perf_session__process_events(session, &rep->tool); - if (ret) - return ret; - - kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; - kernel_kmap = map__kmap(kernel_map); if (kernel_map == NULL || (kernel_map->dso->hit && (kernel_kmap->ref_reloc_sym == NULL || @@ -552,26 +421,73 @@ static int __cmd_report(struct perf_report *rep) "Samples in kernel modules can't be resolved as well.\n\n", desc); } +} - if (verbose > 3) - perf_session__fprintf(session, stdout); +static int report__gtk_browse_hists(struct report *rep, const char *help) +{ + int (*hist_browser)(struct perf_evlist *evlist, const char *help, + struct hist_browser_timer *timer, float min_pcnt); - if (verbose > 2) - perf_session__fprintf_dsos(session, stdout); + hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists"); - if (dump_trace) { - perf_session__fprintf_nr_events(session, stdout); - return 0; + if (hist_browser == NULL) { + ui__error("GTK browser not found!\n"); + return -1; } - nr_samples = 0; - list_for_each_entry(pos, &session->evlist->entries, node) + return hist_browser(rep->session->evlist, help, NULL, rep->min_percent); +} + +static int report__browse_hists(struct report *rep) +{ + int ret; + struct perf_session *session = rep->session; + struct perf_evlist *evlist = session->evlist; + const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + + switch (use_browser) { + case 1: + ret = perf_evlist__tui_browse_hists(evlist, help, NULL, + rep->min_percent, + &session->header.env); + /* + * Usually "ret" is the last pressed key, and we only + * care if the key notifies us to switch data file. + */ + if (ret != K_SWITCH_INPUT_DATA) + ret = 0; + break; + case 2: + ret = report__gtk_browse_hists(rep, help); + break; + default: + ret = perf_evlist__tty_browse_hists(evlist, rep, help); + break; + } + + return ret; +} + +static u64 report__collapse_hists(struct report *rep) +{ + struct ui_progress prog; + struct perf_evsel *pos; + u64 nr_samples = 0; + /* + * Count number of histogram entries to use when showing progress, + * reusing nr_samples variable. + */ + evlist__for_each(rep->session->evlist, pos) nr_samples += pos->hists.nr_entries; ui_progress__init(&prog, nr_samples, "Merging related events..."); - + /* + * Count total number of samples, will be used to check if this + * session had any. + */ nr_samples = 0; - list_for_each_entry(pos, &session->evlist->entries, node) { + + evlist__for_each(rep->session->evlist, pos) { struct hists *hists = &pos->hists; if (pos->idx == 0) @@ -589,8 +505,57 @@ static int __cmd_report(struct perf_report *rep) hists__link(leader_hists, hists); } } + ui_progress__finish(); + return nr_samples; +} + +static int __cmd_report(struct report *rep) +{ + int ret; + u64 nr_samples; + struct perf_session *session = rep->session; + struct perf_evsel *pos; + struct perf_data_file *file = session->file; + + signal(SIGINT, sig_handler); + + if (rep->cpu_list) { + ret = perf_session__cpu_bitmap(session, rep->cpu_list, + rep->cpu_bitmap); + if (ret) + return ret; + } + + if (rep->show_threads) + perf_read_values_init(&rep->show_threads_values); + + ret = report__setup_sample_type(rep); + if (ret) + return ret; + + ret = perf_session__process_events(session, &rep->tool); + if (ret) + return ret; + + report__warn_kptr_restrict(rep); + + if (use_browser == 0) { + if (verbose > 3) + perf_session__fprintf(session, stdout); + + if (verbose > 2) + perf_session__fprintf_dsos(session, stdout); + + if (dump_trace) { + perf_session__fprintf_nr_events(session, stdout); + return 0; + } + } + + nr_samples = report__collapse_hists(rep); + if (session_done()) return 0; @@ -599,47 +564,16 @@ static int __cmd_report(struct perf_report *rep) return 0; } - list_for_each_entry(pos, &session->evlist->entries, node) + evlist__for_each(session->evlist, pos) hists__output_resort(&pos->hists); - if (use_browser > 0) { - if (use_browser == 1) { - ret = perf_evlist__tui_browse_hists(session->evlist, - help, NULL, - rep->min_percent, - &session->header.env); - /* - * Usually "ret" is the last pressed key, and we only - * care if the key notifies us to switch data file. - */ - if (ret != K_SWITCH_INPUT_DATA) - ret = 0; - - } else if (use_browser == 2) { - int (*hist_browser)(struct perf_evlist *, - const char *, - struct hist_browser_timer *, - float min_pcnt); - - hist_browser = dlsym(perf_gtk_handle, - "perf_evlist__gtk_browse_hists"); - if (hist_browser == NULL) { - ui__error("GTK browser not found!\n"); - return ret; - } - hist_browser(session->evlist, help, NULL, - rep->min_percent); - } - } else - perf_evlist__tty_browse_hists(session->evlist, rep, help); - - return ret; + return report__browse_hists(rep); } static int parse_callchain_opt(const struct option *opt, const char *arg, int unset) { - struct perf_report *rep = (struct perf_report *)opt->value; + struct report *rep = (struct report *)opt->value; char *tok, *tok2; char *endptr; @@ -721,7 +655,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) return -1; setup: if (callchain_register_param(&callchain_param) < 0) { - fprintf(stderr, "Can't register callchain params\n"); + pr_err("Can't register callchain params\n"); return -1; } return 0; @@ -759,7 +693,7 @@ static int parse_percent_limit(const struct option *opt, const char *str, int unset __maybe_unused) { - struct perf_report *rep = opt->value; + struct report *rep = opt->value; rep->min_percent = strtof(str, NULL); return 0; @@ -777,7 +711,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "perf report []", NULL }; - struct perf_report report = { + struct report report = { .tool = { .sample = process_sample_event, .mmap = perf_event__process_mmap, @@ -820,6 +754,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"), OPT_BOOLEAN(0, "stdio", &report.use_stdio, "Use the stdio interface"), + OPT_BOOLEAN(0, "header", &report.header, "Show data header."), + OPT_BOOLEAN(0, "header-only", &report.header_only, + "Show only data header."), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," " dso_to, dso_from, symbol_to, symbol_from, mispredict," @@ -890,7 +827,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) .mode = PERF_DATA_MODE_READ, }; - perf_config(perf_report_config, &report); + perf_config(report__config, &report); argc = parse_options(argc, argv, options, report_usage, 0); @@ -940,7 +877,7 @@ repeat: } if (report.mem_mode) { if (sort__mode == SORT_MODE__BRANCH) { - fprintf(stderr, "branch and mem mode incompatible\n"); + pr_err("branch and mem mode incompatible\n"); goto error; } sort__mode = SORT_MODE__MEMORY; @@ -963,6 +900,10 @@ repeat: goto error; } + /* Force tty output for header output. */ + if (report.header || report.header_only) + use_browser = 0; + if (strcmp(input_name, "-") != 0) setup_browser(true); else { @@ -970,6 +911,16 @@ repeat: perf_hpp__init(); } + if (report.header || report.header_only) { + perf_session__fprintf_info(session, stdout, + report.show_full_info); + if (report.header_only) + return 0; + } else if (use_browser == 0) { + fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", + stdout); + } + /* * Only in the TUI browser we are doing integrated annotation, * so don't allocate extra space that won't be used in the stdio diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 0f3c65518a2c..6a76a07b6789 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -469,7 +469,7 @@ static void *thread_func(void *ctx) char comm2[22]; int fd; - free(parms); + zfree(&parms); sprintf(comm2, ":%s", this_task->comm); prctl(PR_SET_NAME, comm2); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index baf17989a216..9e9c91f5b7fa 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -43,6 +43,7 @@ enum perf_output_field { PERF_OUTPUT_DSO = 1U << 9, PERF_OUTPUT_ADDR = 1U << 10, PERF_OUTPUT_SYMOFFSET = 1U << 11, + PERF_OUTPUT_SRCLINE = 1U << 12, }; struct output_option { @@ -61,6 +62,7 @@ struct output_option { {.str = "dso", .field = PERF_OUTPUT_DSO}, {.str = "addr", .field = PERF_OUTPUT_ADDR}, {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, + {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, }; /* default set to maintain compatibility with current format */ @@ -210,6 +212,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, "to DSO.\n"); return -EINVAL; } + if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { + pr_err("Display of source line number requested but sample IP is not\n" + "selected. Hence, no address to lookup the source line number.\n"); + return -EINVAL; + } if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", @@ -245,6 +252,9 @@ static void set_print_ip_opts(struct perf_event_attr *attr) if (PRINT_FIELD(SYMOFFSET)) output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; + + if (PRINT_FIELD(SRCLINE)) + output[type].print_ip_opts |= PRINT_IP_OPT_SRCLINE; } /* @@ -280,6 +290,30 @@ static int perf_session__check_output_opt(struct perf_session *session) set_print_ip_opts(&evsel->attr); } + /* + * set default for tracepoints to print symbols only + * if callchains are present + */ + if (symbol_conf.use_callchain && + !output[PERF_TYPE_TRACEPOINT].user_set) { + struct perf_event_attr *attr; + + j = PERF_TYPE_TRACEPOINT; + evsel = perf_session__find_first_evtype(session, j); + if (evsel == NULL) + goto out; + + attr = &evsel->attr; + + if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { + output[j].fields |= PERF_OUTPUT_IP; + output[j].fields |= PERF_OUTPUT_SYM; + output[j].fields |= PERF_OUTPUT_DSO; + set_print_ip_opts(attr); + } + } + +out: return 0; } @@ -288,7 +322,6 @@ static void print_sample_start(struct perf_sample *sample, struct perf_evsel *evsel) { struct perf_event_attr *attr = &evsel->attr; - const char *evname = NULL; unsigned long secs; unsigned long usecs; unsigned long long nsecs; @@ -323,11 +356,6 @@ static void print_sample_start(struct perf_sample *sample, usecs = nsecs / NSECS_PER_USEC; printf("%5lu.%06lu: ", secs, usecs); } - - if (PRINT_FIELD(EVNAME)) { - evname = perf_evsel__name(evsel); - printf("%s: ", evname ? evname : "[unknown]"); - } } static bool is_bts_event(struct perf_event_attr *attr) @@ -395,8 +423,8 @@ static void print_sample_addr(union perf_event *event, static void print_sample_bts(union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, - struct thread *thread) + struct thread *thread, + struct addr_location *al) { struct perf_event_attr *attr = &evsel->attr; @@ -406,7 +434,7 @@ static void print_sample_bts(union perf_event *event, printf(" "); else printf("\n"); - perf_evsel__print_ip(evsel, event, sample, machine, + perf_evsel__print_ip(evsel, sample, al, output[attr->type].print_ip_opts, PERF_MAX_STACK_DEPTH); } @@ -417,15 +445,14 @@ static void print_sample_bts(union perf_event *event, if (PRINT_FIELD(ADDR) || ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && !output[attr->type].user_set)) - print_sample_addr(event, sample, machine, thread, attr); + print_sample_addr(event, sample, al->machine, thread, attr); printf("\n"); } static void process_event(union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel, struct machine *machine, - struct thread *thread, - struct addr_location *al __maybe_unused) + struct perf_evsel *evsel, struct thread *thread, + struct addr_location *al) { struct perf_event_attr *attr = &evsel->attr; @@ -434,8 +461,13 @@ static void process_event(union perf_event *event, struct perf_sample *sample, print_sample_start(sample, thread, evsel); + if (PRINT_FIELD(EVNAME)) { + const char *evname = perf_evsel__name(evsel); + printf("%s: ", evname ? evname : "[unknown]"); + } + if (is_bts_event(attr)) { - print_sample_bts(event, sample, evsel, machine, thread); + print_sample_bts(event, sample, evsel, thread, al); return; } @@ -443,7 +475,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample, event_format__print(evsel->tp_format, sample->cpu, sample->raw_data, sample->raw_size); if (PRINT_FIELD(ADDR)) - print_sample_addr(event, sample, machine, thread, attr); + print_sample_addr(event, sample, al->machine, thread, attr); if (PRINT_FIELD(IP)) { if (!symbol_conf.use_callchain) @@ -451,7 +483,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample, else printf("\n"); - perf_evsel__print_ip(evsel, event, sample, machine, + perf_evsel__print_ip(evsel, sample, al, output[attr->type].print_ip_opts, PERF_MAX_STACK_DEPTH); } @@ -540,7 +572,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) return 0; - scripting_ops->process_event(event, sample, evsel, machine, thread, &al); + scripting_ops->process_event(event, sample, evsel, thread, &al); evsel->hists.stats.total_period += sample->period; return 0; @@ -549,6 +581,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, struct perf_script { struct perf_tool tool; struct perf_session *session; + bool show_task_events; + bool show_mmap_events; }; static int process_attr(struct perf_tool *tool, union perf_event *event, @@ -569,7 +603,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, if (evsel->attr.type >= PERF_TYPE_MAX) return 0; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (pos->attr.type == evsel->attr.type && pos != evsel) return 0; } @@ -579,6 +613,163 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, return perf_evsel__check_attr(evsel, scr->session); } +static int process_comm_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + int ret = -1; + + thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); + if (thread == NULL) { + pr_debug("problem processing COMM event, skipping it.\n"); + return -1; + } + + if (perf_event__process_comm(tool, event, sample, machine) < 0) + goto out; + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->comm.tid; + sample->pid = event->comm.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + ret = 0; + +out: + return ret; +} + +static int process_fork_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_fork(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); + if (thread == NULL) { + pr_debug("problem processing FORK event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = event->fork.time; + sample->tid = event->fork.tid; + sample->pid = event->fork.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} +static int process_exit_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); + if (thread == NULL) { + pr_debug("problem processing EXIT event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->comm.tid; + sample->pid = event->comm.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + if (perf_event__process_exit(tool, event, sample, machine) < 0) + return -1; + + return 0; +} + +static int process_mmap_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_mmap(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid); + if (thread == NULL) { + pr_debug("problem processing MMAP event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->mmap.tid; + sample->pid = event->mmap.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} + +static int process_mmap2_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_mmap2(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid); + if (thread == NULL) { + pr_debug("problem processing MMAP2 event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->mmap2.tid; + sample->pid = event->mmap2.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} + static void sig_handler(int sig __maybe_unused) { session_done = 1; @@ -590,6 +781,17 @@ static int __cmd_script(struct perf_script *script) signal(SIGINT, sig_handler); + /* override event processing functions */ + if (script->show_task_events) { + script->tool.comm = process_comm_event; + script->tool.fork = process_fork_event; + script->tool.exit = process_exit_event; + } + if (script->show_mmap_events) { + script->tool.mmap = process_mmap_event; + script->tool.mmap2 = process_mmap2_event; + } + ret = perf_session__process_events(script->session, &script->tool); if (debug_mode) @@ -900,9 +1102,9 @@ static struct script_desc *script_desc__new(const char *name) static void script_desc__delete(struct script_desc *s) { - free(s->name); - free(s->half_liner); - free(s->args); + zfree(&s->name); + zfree(&s->half_liner); + zfree(&s->args); free(s); } @@ -1107,8 +1309,7 @@ static int check_ev_match(char *dir_name, char *scriptname, snprintf(evname, len + 1, "%s", p); match = 0; - list_for_each_entry(pos, - &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { if (!strcmp(perf_evsel__name(pos), evname)) { match = 1; break; @@ -1290,6 +1491,8 @@ static int have_cmd(int argc, const char **argv) 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; char *rec_script_path = NULL; char *rep_script_path = NULL; struct perf_session *session; @@ -1328,6 +1531,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('d', "debug-mode", &debug_mode, "do various checks like samples ordering and lost events"), + OPT_BOOLEAN(0, "header", &header, "Show data header."), + OPT_BOOLEAN(0, "header-only", &header_only, "Show only data header."), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, @@ -1352,6 +1557,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) "display extended information from perf.data file"), OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, "Show the path of [kernel.kallsyms]"), + OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events, + "Show the fork/comm/exit events"), + OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, + "Show the mmap events"), OPT_END() }; const char * const script_usage[] = { @@ -1540,6 +1749,12 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) if (session == NULL) return -ENOMEM; + if (header || header_only) { + perf_session__fprintf_info(session, stdout, show_full_info); + if (header_only) + return 0; + } + script.session = session; if (cpu_list) { @@ -1547,9 +1762,6 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) return -1; } - if (!script_name && !generate_script_lang) - perf_session__fprintf_info(session, stdout, show_full_info); - if (!no_callchain) symbol_conf.use_callchain = true; else @@ -1588,7 +1800,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) return -1; } - err = scripting_ops->generate_script(session->pevent, + err = scripting_ops->generate_script(session->tevent.pevent, "perf-script"); goto out; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index ee0d565f83e3..8b0e1c9234d9 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -138,6 +138,7 @@ static const char *post_cmd = NULL; static bool sync_run = false; static unsigned int interval = 0; static unsigned int initial_delay = 0; +static unsigned int unit_width = 4; /* strlen("unit") */ static bool forever = false; static struct timespec ref_time; static struct cpu_map *aggr_map; @@ -184,8 +185,7 @@ static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) { - free(evsel->priv); - evsel->priv = NULL; + zfree(&evsel->priv); } static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) @@ -207,15 +207,14 @@ static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) { - free(evsel->prev_raw_counts); - evsel->prev_raw_counts = NULL; + zfree(&evsel->prev_raw_counts); } static void perf_evlist__free_stats(struct perf_evlist *evlist) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { perf_evsel__free_stat_priv(evsel); perf_evsel__free_counts(evsel); perf_evsel__free_prev_raw_counts(evsel); @@ -226,7 +225,7 @@ static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (perf_evsel__alloc_stat_priv(evsel) < 0 || perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 || (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) @@ -260,7 +259,7 @@ static void perf_stat__reset_stats(struct perf_evlist *evlist) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { perf_evsel__reset_stat_priv(evsel); perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel)); } @@ -327,13 +326,13 @@ static struct perf_evsel *nth_evsel(int n) /* Assumes this only called when evsel_list does not change anymore. */ if (!array) { - list_for_each_entry(ev, &evsel_list->entries, node) + evlist__for_each(evsel_list, ev) array_len++; array = malloc(array_len * sizeof(void *)); if (!array) exit(ENOMEM); j = 0; - list_for_each_entry(ev, &evsel_list->entries, node) + evlist__for_each(evsel_list, ev) array[j++] = ev; } if (n < array_len) @@ -441,13 +440,13 @@ static void print_interval(void) char prefix[64]; if (aggr_mode == AGGR_GLOBAL) { - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { ps = counter->priv; memset(ps->res_stats, 0, sizeof(ps->res_stats)); read_counter_aggr(counter); } } else { - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { ps = counter->priv; memset(ps->res_stats, 0, sizeof(ps->res_stats)); read_counter(counter); @@ -461,17 +460,17 @@ static void print_interval(void) if (num_print_interval == 0 && !csv_output) { switch (aggr_mode) { case AGGR_SOCKET: - fprintf(output, "# time socket cpus counts events\n"); + fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); break; case AGGR_CORE: - fprintf(output, "# time core cpus counts events\n"); + fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit"); break; case AGGR_NONE: - fprintf(output, "# time CPU counts events\n"); + fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit"); break; case AGGR_GLOBAL: default: - fprintf(output, "# time counts events\n"); + fprintf(output, "# time counts %*s events\n", unit_width, "unit"); } } @@ -484,12 +483,12 @@ static void print_interval(void) print_aggr(prefix); break; case AGGR_NONE: - list_for_each_entry(counter, &evsel_list->entries, node) + evlist__for_each(evsel_list, counter) print_counter(counter, prefix); break; case AGGR_GLOBAL: default: - list_for_each_entry(counter, &evsel_list->entries, node) + evlist__for_each(evsel_list, counter) print_counter_aggr(counter, prefix); } @@ -505,17 +504,31 @@ static void handle_initial_delay(void) nthreads = thread_map__nr(evsel_list->threads); usleep(initial_delay * 1000); - list_for_each_entry(counter, &evsel_list->entries, node) + evlist__for_each(evsel_list, counter) perf_evsel__enable(counter, ncpus, nthreads); } } +static volatile int workload_exec_errno; + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 + * if the fork fails, since we asked by setting its + * want_signal to true. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *info, + void *ucontext __maybe_unused) +{ + workload_exec_errno = info->si_value.sival_int; +} + static int __run_perf_stat(int argc, const char **argv) { char msg[512]; unsigned long long t0, t1; struct perf_evsel *counter; struct timespec ts; + size_t l; int status = 0; const bool forks = (argc > 0); @@ -528,8 +541,8 @@ static int __run_perf_stat(int argc, const char **argv) } if (forks) { - if (perf_evlist__prepare_workload(evsel_list, &target, argv, - false, false) < 0) { + if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, + workload_exec_failed_signal) < 0) { perror("failed to prepare workload"); return -1; } @@ -539,7 +552,7 @@ static int __run_perf_stat(int argc, const char **argv) if (group) perf_evlist__set_leader(evsel_list); - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { if (create_perf_stat_counter(counter) < 0) { /* * PPC returns ENXIO for HW counters until 2.6.37 @@ -565,6 +578,10 @@ static int __run_perf_stat(int argc, const char **argv) return -1; } counter->supported = true; + + l = strlen(counter->unit); + if (l > unit_width) + unit_width = l; } if (perf_evlist__apply_filters(evsel_list)) { @@ -590,6 +607,13 @@ static int __run_perf_stat(int argc, const char **argv) } } wait(&status); + + if (workload_exec_errno) { + const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); + pr_err("Workload failed: %s\n", emsg); + return -1; + } + if (WIFSIGNALED(status)) psignal(WTERMSIG(status), argv[0]); } else { @@ -606,13 +630,13 @@ static int __run_perf_stat(int argc, const char **argv) update_stats(&walltime_nsecs_stats, t1 - t0); if (aggr_mode == AGGR_GLOBAL) { - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { read_counter_aggr(counter); perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), thread_map__nr(evsel_list->threads)); } } else { - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { read_counter(counter); perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); } @@ -621,7 +645,7 @@ static int __run_perf_stat(int argc, const char **argv) return WEXITSTATUS(status); } -static int run_perf_stat(int argc __maybe_unused, const char **argv) +static int run_perf_stat(int argc, const char **argv) { int ret; @@ -704,14 +728,25 @@ 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) { double msecs = avg / 1e6; - const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; + const char *fmt_v, *fmt_n; char name[25]; + fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; + fmt_n = csv_output ? "%s" : "%-25s"; + aggr_printout(evsel, cpu, nr); scnprintf(name, sizeof(name), "%s%s", perf_evsel__name(evsel), csv_output ? "" : " (msec)"); - fprintf(output, fmt, msecs, csv_sep, name); + + fprintf(output, fmt_v, msecs, csv_sep); + + if (csv_output) + fprintf(output, "%s%s", evsel->unit, csv_sep); + else + fprintf(output, "%-*s%s", unit_width, evsel->unit, csv_sep); + + fprintf(output, fmt_n, name); if (evsel->cgrp) fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -908,21 +943,31 @@ static void print_ll_cache_misses(int cpu, static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) { double total, ratio = 0.0, total2; + double sc = evsel->scale; const char *fmt; - if (csv_output) - fmt = "%.0f%s%s"; - else if (big_num) - fmt = "%'18.0f%s%-25s"; - else - fmt = "%18.0f%s%-25s"; + if (csv_output) { + fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; + } else { + if (big_num) + fmt = sc != 1.0 ? "%'18.2f%s" : "%'18.0f%s"; + else + fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; + } aggr_printout(evsel, cpu, nr); if (aggr_mode == AGGR_GLOBAL) cpu = 0; - fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); + fprintf(output, fmt, avg, csv_sep); + + if (evsel->unit) + fprintf(output, "%-*s%s", + csv_output ? 0 : unit_width, + evsel->unit, csv_sep); + + fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel)); if (evsel->cgrp) fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -941,7 +986,10 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) if (total && avg) { ratio = total / avg; - fprintf(output, "\n # %5.2f stalled cycles per insn", ratio); + fprintf(output, "\n"); + if (aggr_mode == AGGR_NONE) + fprintf(output, " "); + fprintf(output, " # %5.2f stalled cycles per insn", ratio); } } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && @@ -1061,6 +1109,7 @@ static void print_aggr(char *prefix) { struct perf_evsel *counter; int cpu, cpu2, s, s2, id, nr; + double uval; u64 ena, run, val; if (!(aggr_map || aggr_get_id)) @@ -1068,7 +1117,7 @@ static void print_aggr(char *prefix) for (s = 0; s < aggr_map->nr; s++) { id = aggr_map->map[s]; - list_for_each_entry(counter, &evsel_list->entries, node) { + evlist__for_each(evsel_list, counter) { val = ena = run = 0; nr = 0; for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { @@ -1087,11 +1136,17 @@ static void print_aggr(char *prefix) if (run == 0 || ena == 0) { aggr_printout(counter, id, nr); - fprintf(output, "%*s%s%*s", + fprintf(output, "%*s%s", csv_output ? 0 : 18, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, - csv_sep, - csv_output ? 0 : -24, + csv_sep); + + fprintf(output, "%-*s%s", + csv_output ? 0 : unit_width, + counter->unit, csv_sep); + + fprintf(output, "%*s", + csv_output ? 0 : -25, perf_evsel__name(counter)); if (counter->cgrp) @@ -1101,11 +1156,12 @@ static void print_aggr(char *prefix) fputc('\n', output); continue; } + uval = val * counter->scale; if (nsec_counter(counter)) - nsec_printout(id, nr, counter, val); + nsec_printout(id, nr, counter, uval); else - abs_printout(id, nr, counter, val); + abs_printout(id, nr, counter, uval); if (!csv_output) { print_noise(counter, 1.0); @@ -1128,16 +1184,21 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) struct perf_stat *ps = counter->priv; double avg = avg_stats(&ps->res_stats[0]); int scaled = counter->counts->scaled; + double uval; if (prefix) fprintf(output, "%s", prefix); if (scaled == -1) { - fprintf(output, "%*s%s%*s", + fprintf(output, "%*s%s", csv_output ? 0 : 18, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, - csv_sep, - csv_output ? 0 : -24, + csv_sep); + fprintf(output, "%-*s%s", + csv_output ? 0 : unit_width, + counter->unit, csv_sep); + fprintf(output, "%*s", + csv_output ? 0 : -25, perf_evsel__name(counter)); if (counter->cgrp) @@ -1147,10 +1208,12 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) return; } + uval = avg * counter->scale; + if (nsec_counter(counter)) - nsec_printout(-1, 0, counter, avg); + nsec_printout(-1, 0, counter, uval); else - abs_printout(-1, 0, counter, avg); + abs_printout(-1, 0, counter, uval); print_noise(counter, avg); @@ -1177,6 +1240,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) static void print_counter(struct perf_evsel *counter, char *prefix) { u64 ena, run, val; + double uval; int cpu; for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { @@ -1188,14 +1252,20 @@ static void print_counter(struct perf_evsel *counter, char *prefix) fprintf(output, "%s", prefix); if (run == 0 || ena == 0) { - fprintf(output, "CPU%*d%s%*s%s%*s", + fprintf(output, "CPU%*d%s%*s%s", csv_output ? 0 : -4, perf_evsel__cpus(counter)->map[cpu], csv_sep, csv_output ? 0 : 18, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, - csv_sep, - csv_output ? 0 : -24, - perf_evsel__name(counter)); + csv_sep); + + fprintf(output, "%-*s%s", + csv_output ? 0 : unit_width, + counter->unit, csv_sep); + + fprintf(output, "%*s", + csv_output ? 0 : -25, + perf_evsel__name(counter)); if (counter->cgrp) fprintf(output, "%s%s", @@ -1205,10 +1275,12 @@ static void print_counter(struct perf_evsel *counter, char *prefix) continue; } + uval = val * counter->scale; + if (nsec_counter(counter)) - nsec_printout(cpu, 0, counter, val); + nsec_printout(cpu, 0, counter, uval); else - abs_printout(cpu, 0, counter, val); + abs_printout(cpu, 0, counter, uval); if (!csv_output) { print_noise(counter, 1.0); @@ -1256,11 +1328,11 @@ static void print_stat(int argc, const char **argv) print_aggr(NULL); break; case AGGR_GLOBAL: - list_for_each_entry(counter, &evsel_list->entries, node) + evlist__for_each(evsel_list, counter) print_counter_aggr(counter, NULL); break; case AGGR_NONE: - list_for_each_entry(counter, &evsel_list->entries, node) + evlist__for_each(evsel_list, counter) print_counter(counter, NULL); break; default: @@ -1710,14 +1782,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (interval && interval < 100) { pr_err("print interval must be >= 100ms\n"); parse_options_usage(stat_usage, options, "I", 1); - goto out_free_maps; + goto out; } if (perf_evlist__alloc_stats(evsel_list, interval)) - goto out_free_maps; + goto out; if (perf_stat_init_aggr_mode()) - goto out_free_maps; + goto out; /* * We dont want to block the signals - that would cause @@ -1749,8 +1821,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) print_stat(argc, argv); perf_evlist__free_stats(evsel_list); -out_free_maps: - perf_evlist__delete_maps(evsel_list); out: perf_evlist__delete(evsel_list); return status; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 41c9bde2fb67..652af0b66a62 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -41,25 +41,29 @@ #define SUPPORT_OLD_POWER_EVENTS 1 #define PWR_EVENT_EXIT -1 - -static unsigned int numcpus; -static u64 min_freq; /* Lowest CPU frequency seen */ -static u64 max_freq; /* Highest CPU frequency seen */ -static u64 turbo_frequency; - -static u64 first_time, last_time; - -static bool power_only; - - struct per_pid; -struct per_pidcomm; - -struct cpu_sample; struct power_event; struct wake_event; -struct sample_wrapper; +struct timechart { + struct perf_tool tool; + struct per_pid *all_data; + struct power_event *power_events; + struct wake_event *wake_events; + int proc_num; + unsigned int numcpus; + u64 min_freq, /* Lowest CPU frequency seen */ + max_freq, /* Highest CPU frequency seen */ + turbo_frequency, + first_time, last_time; + bool power_only, + tasks_only, + with_backtrace, + topology; +}; + +struct per_pidcomm; +struct cpu_sample; /* * Datastructure layout: @@ -124,10 +128,9 @@ struct cpu_sample { u64 end_time; int type; int cpu; + const char *backtrace; }; -static struct per_pid *all_data; - #define CSTATE 1 #define PSTATE 2 @@ -145,12 +148,9 @@ struct wake_event { int waker; int wakee; u64 time; + const char *backtrace; }; -static struct power_event *power_events; -static struct wake_event *wake_events; - -struct process_filter; struct process_filter { char *name; int pid; @@ -160,9 +160,9 @@ struct process_filter { static struct process_filter *process_filter; -static struct per_pid *find_create_pid(int pid) +static struct per_pid *find_create_pid(struct timechart *tchart, int pid) { - struct per_pid *cursor = all_data; + struct per_pid *cursor = tchart->all_data; while (cursor) { if (cursor->pid == pid) @@ -172,16 +172,16 @@ static struct per_pid *find_create_pid(int pid) cursor = zalloc(sizeof(*cursor)); assert(cursor != NULL); cursor->pid = pid; - cursor->next = all_data; - all_data = cursor; + cursor->next = tchart->all_data; + tchart->all_data = cursor; return cursor; } -static void pid_set_comm(int pid, char *comm) +static void pid_set_comm(struct timechart *tchart, int pid, char *comm) { struct per_pid *p; struct per_pidcomm *c; - p = find_create_pid(pid); + p = find_create_pid(tchart, pid); c = p->all; while (c) { if (c->comm && strcmp(c->comm, comm) == 0) { @@ -203,14 +203,14 @@ static void pid_set_comm(int pid, char *comm) p->all = c; } -static void pid_fork(int pid, int ppid, u64 timestamp) +static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp) { struct per_pid *p, *pp; - p = find_create_pid(pid); - pp = find_create_pid(ppid); + p = find_create_pid(tchart, pid); + pp = find_create_pid(tchart, ppid); p->ppid = ppid; if (pp->current && pp->current->comm && !p->current) - pid_set_comm(pid, pp->current->comm); + pid_set_comm(tchart, pid, pp->current->comm); p->start_time = timestamp; if (p->current) { @@ -219,23 +219,24 @@ static void pid_fork(int pid, int ppid, u64 timestamp) } } -static void pid_exit(int pid, u64 timestamp) +static void pid_exit(struct timechart *tchart, int pid, u64 timestamp) { struct per_pid *p; - p = find_create_pid(pid); + p = find_create_pid(tchart, pid); p->end_time = timestamp; if (p->current) p->current->end_time = timestamp; } -static void -pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) +static void pid_put_sample(struct timechart *tchart, int pid, int type, + unsigned int cpu, u64 start, u64 end, + const char *backtrace) { struct per_pid *p; struct per_pidcomm *c; struct cpu_sample *sample; - p = find_create_pid(pid); + p = find_create_pid(tchart, pid); c = p->current; if (!c) { c = zalloc(sizeof(*c)); @@ -252,6 +253,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) sample->type = type; sample->next = c->samples; sample->cpu = cpu; + sample->backtrace = backtrace; c->samples = sample; if (sample->type == TYPE_RUNNING && end > start && start > 0) { @@ -272,84 +274,47 @@ static int cpus_cstate_state[MAX_CPUS]; static u64 cpus_pstate_start_times[MAX_CPUS]; static u64 cpus_pstate_state[MAX_CPUS]; -static int process_comm_event(struct perf_tool *tool __maybe_unused, +static int process_comm_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - pid_set_comm(event->comm.tid, event->comm.comm); + struct timechart *tchart = container_of(tool, struct timechart, tool); + pid_set_comm(tchart, event->comm.tid, event->comm.comm); return 0; } -static int process_fork_event(struct perf_tool *tool __maybe_unused, +static int process_fork_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); + struct timechart *tchart = container_of(tool, struct timechart, tool); + pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time); return 0; } -static int process_exit_event(struct perf_tool *tool __maybe_unused, +static int process_exit_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - pid_exit(event->fork.pid, event->fork.time); + struct timechart *tchart = container_of(tool, struct timechart, tool); + pid_exit(tchart, event->fork.pid, event->fork.time); return 0; } -struct trace_entry { - unsigned short type; - unsigned char flags; - unsigned char preempt_count; - int pid; - int lock_depth; -}; - #ifdef SUPPORT_OLD_POWER_EVENTS static int use_old_power_events; -struct power_entry_old { - struct trace_entry te; - u64 type; - u64 value; - u64 cpu_id; -}; #endif -struct power_processor_entry { - struct trace_entry te; - u32 state; - u32 cpu_id; -}; - -#define TASK_COMM_LEN 16 -struct wakeup_entry { - struct trace_entry te; - char comm[TASK_COMM_LEN]; - int pid; - int prio; - int success; -}; - -struct sched_switch { - struct trace_entry te; - char prev_comm[TASK_COMM_LEN]; - int prev_pid; - int prev_prio; - long prev_state; /* Arjan weeps. */ - char next_comm[TASK_COMM_LEN]; - int next_pid; - int next_prio; -}; - static void c_state_start(int cpu, u64 timestamp, int state) { cpus_cstate_start_times[cpu] = timestamp; cpus_cstate_state[cpu] = state; } -static void c_state_end(int cpu, u64 timestamp) +static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp) { struct power_event *pwr = zalloc(sizeof(*pwr)); @@ -361,12 +326,12 @@ static void c_state_end(int cpu, u64 timestamp) pwr->end_time = timestamp; pwr->cpu = cpu; pwr->type = CSTATE; - pwr->next = power_events; + pwr->next = tchart->power_events; - power_events = pwr; + tchart->power_events = pwr; } -static void p_state_change(int cpu, u64 timestamp, u64 new_freq) +static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq) { struct power_event *pwr; @@ -382,73 +347,78 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq) pwr->end_time = timestamp; pwr->cpu = cpu; pwr->type = PSTATE; - pwr->next = power_events; + pwr->next = tchart->power_events; if (!pwr->start_time) - pwr->start_time = first_time; + pwr->start_time = tchart->first_time; - power_events = pwr; + tchart->power_events = pwr; cpus_pstate_state[cpu] = new_freq; cpus_pstate_start_times[cpu] = timestamp; - if ((u64)new_freq > max_freq) - max_freq = new_freq; + if ((u64)new_freq > tchart->max_freq) + tchart->max_freq = new_freq; - if (new_freq < min_freq || min_freq == 0) - min_freq = new_freq; + if (new_freq < tchart->min_freq || tchart->min_freq == 0) + tchart->min_freq = new_freq; - if (new_freq == max_freq - 1000) - turbo_frequency = max_freq; + if (new_freq == tchart->max_freq - 1000) + tchart->turbo_frequency = tchart->max_freq; } -static void -sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) +static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp, + int waker, int wakee, u8 flags, const char *backtrace) { struct per_pid *p; - struct wakeup_entry *wake = (void *)te; struct wake_event *we = zalloc(sizeof(*we)); if (!we) return; we->time = timestamp; - we->waker = pid; + we->waker = waker; + we->backtrace = backtrace; - if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ)) + if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) we->waker = -1; - we->wakee = wake->pid; - we->next = wake_events; - wake_events = we; - p = find_create_pid(we->wakee); + we->wakee = wakee; + we->next = tchart->wake_events; + tchart->wake_events = we; + p = find_create_pid(tchart, we->wakee); if (p && p->current && p->current->state == TYPE_NONE) { p->current->state_since = timestamp; p->current->state = TYPE_WAITING; } if (p && p->current && p->current->state == TYPE_BLOCKED) { - pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp); + pid_put_sample(tchart, p->pid, p->current->state, cpu, + p->current->state_since, timestamp, NULL); p->current->state_since = timestamp; p->current->state = TYPE_WAITING; } } -static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) +static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, + int prev_pid, int next_pid, u64 prev_state, + const char *backtrace) { struct per_pid *p = NULL, *prev_p; - struct sched_switch *sw = (void *)te; + prev_p = find_create_pid(tchart, prev_pid); - prev_p = find_create_pid(sw->prev_pid); - - p = find_create_pid(sw->next_pid); + p = find_create_pid(tchart, next_pid); if (prev_p->current && prev_p->current->state != TYPE_NONE) - pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp); + pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu, + prev_p->current->state_since, timestamp, + backtrace); if (p && p->current) { if (p->current->state != TYPE_NONE) - pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); + pid_put_sample(tchart, next_pid, p->current->state, cpu, + p->current->state_since, timestamp, + backtrace); p->current->state_since = timestamp; p->current->state = TYPE_RUNNING; @@ -457,109 +427,211 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) if (prev_p->current) { prev_p->current->state = TYPE_NONE; prev_p->current->state_since = timestamp; - if (sw->prev_state & 2) + if (prev_state & 2) prev_p->current->state = TYPE_BLOCKED; - if (sw->prev_state == 0) + if (prev_state == 0) prev_p->current->state = TYPE_WAITING; } } -typedef int (*tracepoint_handler)(struct perf_evsel *evsel, - struct perf_sample *sample); - -static int process_sample_event(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine __maybe_unused) +static const char *cat_backtrace(union perf_event *event, + struct perf_sample *sample, + struct machine *machine) { - if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { - if (!first_time || first_time > sample->time) - first_time = sample->time; - if (last_time < sample->time) - last_time = sample->time; + struct addr_location al; + unsigned int i; + char *p = NULL; + size_t p_len; + u8 cpumode = PERF_RECORD_MISC_USER; + struct addr_location tal; + struct ip_callchain *chain = sample->callchain; + FILE *f = open_memstream(&p, &p_len); + + if (!f) { + perror("open_memstream error"); + return NULL; } - if (sample->cpu > numcpus) - numcpus = sample->cpu; + if (!chain) + goto exit; + + if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { + fprintf(stderr, "problem processing %d event, skipping it.\n", + event->header.type); + goto exit; + } + + for (i = 0; i < chain->nr; i++) { + u64 ip; + + if (callchain_param.order == ORDER_CALLEE) + ip = chain->ips[i]; + else + ip = chain->ips[chain->nr - i - 1]; + + if (ip >= PERF_CONTEXT_MAX) { + switch (ip) { + case PERF_CONTEXT_HV: + cpumode = PERF_RECORD_MISC_HYPERVISOR; + break; + case PERF_CONTEXT_KERNEL: + cpumode = PERF_RECORD_MISC_KERNEL; + break; + case PERF_CONTEXT_USER: + cpumode = PERF_RECORD_MISC_USER; + break; + default: + pr_debug("invalid callchain context: " + "%"PRId64"\n", (s64) ip); + + /* + * It seems the callchain is corrupted. + * Discard all. + */ + zfree(&p); + goto exit; + } + continue; + } + + tal.filtered = false; + thread__find_addr_location(al.thread, machine, cpumode, + MAP__FUNCTION, ip, &tal); + + if (tal.sym) + fprintf(f, "..... %016" PRIx64 " %s\n", ip, + tal.sym->name); + else + fprintf(f, "..... %016" PRIx64 "\n", ip); + } + +exit: + fclose(f); + + return p; +} + +typedef int (*tracepoint_handler)(struct timechart *tchart, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace); + +static int process_sample_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct perf_evsel *evsel, + struct machine *machine) +{ + struct timechart *tchart = container_of(tool, struct timechart, tool); + + if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { + if (!tchart->first_time || tchart->first_time > sample->time) + tchart->first_time = sample->time; + if (tchart->last_time < sample->time) + tchart->last_time = sample->time; + } if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - return f(evsel, sample); + return f(tchart, evsel, sample, + cat_backtrace(event, sample, machine)); } return 0; } static int -process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_cpu_idle(struct timechart *tchart __maybe_unused, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_processor_entry *ppe = sample->raw_data; + u32 state = perf_evsel__intval(evsel, sample, "state"); + u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); - if (ppe->state == (u32) PWR_EVENT_EXIT) - c_state_end(ppe->cpu_id, sample->time); + if (state == (u32)PWR_EVENT_EXIT) + c_state_end(tchart, cpu_id, sample->time); else - c_state_start(ppe->cpu_id, sample->time, ppe->state); + c_state_start(cpu_id, sample->time, state); return 0; } static int -process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_cpu_frequency(struct timechart *tchart, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_processor_entry *ppe = sample->raw_data; + u32 state = perf_evsel__intval(evsel, sample, "state"); + u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); - p_state_change(ppe->cpu_id, sample->time, ppe->state); + p_state_change(tchart, cpu_id, sample->time, state); return 0; } static int -process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_sched_wakeup(struct timechart *tchart, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace) { - struct trace_entry *te = sample->raw_data; + u8 flags = perf_evsel__intval(evsel, sample, "common_flags"); + int waker = perf_evsel__intval(evsel, sample, "common_pid"); + int wakee = perf_evsel__intval(evsel, sample, "pid"); - sched_wakeup(sample->cpu, sample->time, sample->pid, te); + sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace); return 0; } static int -process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_sched_switch(struct timechart *tchart, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace) { - struct trace_entry *te = sample->raw_data; + int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"); + int next_pid = perf_evsel__intval(evsel, sample, "next_pid"); + u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); - sched_switch(sample->cpu, sample->time, te); + sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, + prev_state, backtrace); return 0; } #ifdef SUPPORT_OLD_POWER_EVENTS static int -process_sample_power_start(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_power_start(struct timechart *tchart __maybe_unused, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_entry_old *peo = sample->raw_data; + u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); + u64 value = perf_evsel__intval(evsel, sample, "value"); - c_state_start(peo->cpu_id, sample->time, peo->value); + c_state_start(cpu_id, sample->time, value); return 0; } static int -process_sample_power_end(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_power_end(struct timechart *tchart, + struct perf_evsel *evsel __maybe_unused, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - c_state_end(sample->cpu, sample->time); + c_state_end(tchart, sample->cpu, sample->time); return 0; } static int -process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_power_frequency(struct timechart *tchart, + struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_entry_old *peo = sample->raw_data; + u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); + u64 value = perf_evsel__intval(evsel, sample, "value"); - p_state_change(peo->cpu_id, sample->time, peo->value); + p_state_change(tchart, cpu_id, sample->time, value); return 0; } #endif /* SUPPORT_OLD_POWER_EVENTS */ @@ -568,12 +640,12 @@ process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, * After the last sample we need to wrap up the current C/P state * and close out each CPU for these. */ -static void end_sample_processing(void) +static void end_sample_processing(struct timechart *tchart) { u64 cpu; struct power_event *pwr; - for (cpu = 0; cpu <= numcpus; cpu++) { + for (cpu = 0; cpu <= tchart->numcpus; cpu++) { /* C state */ #if 0 pwr = zalloc(sizeof(*pwr)); @@ -582,12 +654,12 @@ static void end_sample_processing(void) pwr->state = cpus_cstate_state[cpu]; pwr->start_time = cpus_cstate_start_times[cpu]; - pwr->end_time = last_time; + pwr->end_time = tchart->last_time; pwr->cpu = cpu; pwr->type = CSTATE; - pwr->next = power_events; + pwr->next = tchart->power_events; - power_events = pwr; + tchart->power_events = pwr; #endif /* P state */ @@ -597,32 +669,32 @@ static void end_sample_processing(void) pwr->state = cpus_pstate_state[cpu]; pwr->start_time = cpus_pstate_start_times[cpu]; - pwr->end_time = last_time; + pwr->end_time = tchart->last_time; pwr->cpu = cpu; pwr->type = PSTATE; - pwr->next = power_events; + pwr->next = tchart->power_events; if (!pwr->start_time) - pwr->start_time = first_time; + pwr->start_time = tchart->first_time; if (!pwr->state) - pwr->state = min_freq; - power_events = pwr; + pwr->state = tchart->min_freq; + tchart->power_events = pwr; } } /* * Sort the pid datastructure */ -static void sort_pids(void) +static void sort_pids(struct timechart *tchart) { struct per_pid *new_list, *p, *cursor, *prev; /* sort by ppid first, then by pid, lowest to highest */ new_list = NULL; - while (all_data) { - p = all_data; - all_data = p->next; + while (tchart->all_data) { + p = tchart->all_data; + tchart->all_data = p->next; p->next = NULL; if (new_list == NULL) { @@ -655,14 +727,14 @@ static void sort_pids(void) prev->next = p; } } - all_data = new_list; + tchart->all_data = new_list; } -static void draw_c_p_states(void) +static void draw_c_p_states(struct timechart *tchart) { struct power_event *pwr; - pwr = power_events; + pwr = tchart->power_events; /* * two pass drawing so that the P state bars are on top of the C state blocks @@ -673,30 +745,30 @@ static void draw_c_p_states(void) pwr = pwr->next; } - pwr = power_events; + pwr = tchart->power_events; while (pwr) { if (pwr->type == PSTATE) { if (!pwr->state) - pwr->state = min_freq; + pwr->state = tchart->min_freq; svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state); } pwr = pwr->next; } } -static void draw_wakeups(void) +static void draw_wakeups(struct timechart *tchart) { struct wake_event *we; struct per_pid *p; struct per_pidcomm *c; - we = wake_events; + we = tchart->wake_events; while (we) { int from = 0, to = 0; char *task_from = NULL, *task_to = NULL; /* locate the column of the waker and wakee */ - p = all_data; + p = tchart->all_data; while (p) { if (p->pid == we->waker || p->pid == we->wakee) { c = p->all; @@ -739,11 +811,12 @@ static void draw_wakeups(void) } if (we->waker == -1) - svg_interrupt(we->time, to); + svg_interrupt(we->time, to, we->backtrace); else if (from && to && abs(from - to) == 1) - svg_wakeline(we->time, from, to); + svg_wakeline(we->time, from, to, we->backtrace); else - svg_partial_wakeline(we->time, from, task_from, to, task_to); + svg_partial_wakeline(we->time, from, task_from, to, + task_to, we->backtrace); we = we->next; free(task_from); @@ -751,19 +824,25 @@ static void draw_wakeups(void) } } -static void draw_cpu_usage(void) +static void draw_cpu_usage(struct timechart *tchart) { struct per_pid *p; struct per_pidcomm *c; struct cpu_sample *sample; - p = all_data; + p = tchart->all_data; while (p) { c = p->all; while (c) { sample = c->samples; while (sample) { - if (sample->type == TYPE_RUNNING) - svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm); + if (sample->type == TYPE_RUNNING) { + svg_process(sample->cpu, + sample->start_time, + sample->end_time, + p->pid, + c->comm, + sample->backtrace); + } sample = sample->next; } @@ -773,16 +852,16 @@ static void draw_cpu_usage(void) } } -static void draw_process_bars(void) +static void draw_process_bars(struct timechart *tchart) { struct per_pid *p; struct per_pidcomm *c; struct cpu_sample *sample; int Y = 0; - Y = 2 * numcpus + 2; + Y = 2 * tchart->numcpus + 2; - p = all_data; + p = tchart->all_data; while (p) { c = p->all; while (c) { @@ -796,11 +875,20 @@ static void draw_process_bars(void) sample = c->samples; while (sample) { if (sample->type == TYPE_RUNNING) - svg_sample(Y, sample->cpu, sample->start_time, sample->end_time); + svg_running(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); if (sample->type == TYPE_BLOCKED) - svg_box(Y, sample->start_time, sample->end_time, "blocked"); + svg_blocked(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); if (sample->type == TYPE_WAITING) - svg_waiting(Y, sample->start_time, sample->end_time); + svg_waiting(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); sample = sample->next; } @@ -853,21 +941,21 @@ static int passes_filter(struct per_pid *p, struct per_pidcomm *c) return 0; } -static int determine_display_tasks_filtered(void) +static int determine_display_tasks_filtered(struct timechart *tchart) { struct per_pid *p; struct per_pidcomm *c; int count = 0; - p = all_data; + p = tchart->all_data; while (p) { p->display = 0; if (p->start_time == 1) - p->start_time = first_time; + p->start_time = tchart->first_time; /* no exit marker, task kept running to the end */ if (p->end_time == 0) - p->end_time = last_time; + p->end_time = tchart->last_time; c = p->all; @@ -875,7 +963,7 @@ static int determine_display_tasks_filtered(void) c->display = 0; if (c->start_time == 1) - c->start_time = first_time; + c->start_time = tchart->first_time; if (passes_filter(p, c)) { c->display = 1; @@ -884,7 +972,7 @@ static int determine_display_tasks_filtered(void) } if (c->end_time == 0) - c->end_time = last_time; + c->end_time = tchart->last_time; c = c->next; } @@ -893,25 +981,25 @@ static int determine_display_tasks_filtered(void) return count; } -static int determine_display_tasks(u64 threshold) +static int determine_display_tasks(struct timechart *tchart, u64 threshold) { struct per_pid *p; struct per_pidcomm *c; int count = 0; if (process_filter) - return determine_display_tasks_filtered(); + return determine_display_tasks_filtered(tchart); - p = all_data; + p = tchart->all_data; while (p) { p->display = 0; if (p->start_time == 1) - p->start_time = first_time; + p->start_time = tchart->first_time; /* no exit marker, task kept running to the end */ if (p->end_time == 0) - p->end_time = last_time; - if (p->total_time >= threshold && !power_only) + p->end_time = tchart->last_time; + if (p->total_time >= threshold) p->display = 1; c = p->all; @@ -920,15 +1008,15 @@ static int determine_display_tasks(u64 threshold) c->display = 0; if (c->start_time == 1) - c->start_time = first_time; + c->start_time = tchart->first_time; - if (c->total_time >= threshold && !power_only) { + if (c->total_time >= threshold) { c->display = 1; count++; } if (c->end_time == 0) - c->end_time = last_time; + c->end_time = tchart->last_time; c = c->next; } @@ -941,45 +1029,74 @@ static int determine_display_tasks(u64 threshold) #define TIME_THRESH 10000000 -static void write_svg_file(const char *filename) +static void write_svg_file(struct timechart *tchart, const char *filename) { u64 i; int count; + int thresh = TIME_THRESH; - numcpus++; + if (tchart->power_only) + tchart->proc_num = 0; + /* We'd like to show at least proc_num tasks; + * be less picky if we have fewer */ + do { + count = determine_display_tasks(tchart, thresh); + thresh /= 10; + } while (!process_filter && thresh && count < tchart->proc_num); - count = determine_display_tasks(TIME_THRESH); - - /* We'd like to show at least 15 tasks; be less picky if we have fewer */ - if (count < 15) - count = determine_display_tasks(TIME_THRESH / 10); - - open_svg(filename, numcpus, count, first_time, last_time); + open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); svg_time_grid(); svg_legenda(); - for (i = 0; i < numcpus; i++) - svg_cpu_box(i, max_freq, turbo_frequency); + for (i = 0; i < tchart->numcpus; i++) + svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); - draw_cpu_usage(); - draw_process_bars(); - draw_c_p_states(); - draw_wakeups(); + draw_cpu_usage(tchart); + if (tchart->proc_num) + draw_process_bars(tchart); + if (!tchart->tasks_only) + draw_c_p_states(tchart); + if (tchart->proc_num) + draw_wakeups(tchart); svg_close(); } -static int __cmd_timechart(const char *output_name) +static int process_header(struct perf_file_section *section __maybe_unused, + struct perf_header *ph, + int feat, + int fd __maybe_unused, + void *data) +{ + struct timechart *tchart = data; + + switch (feat) { + case HEADER_NRCPUS: + tchart->numcpus = ph->env.nr_cpus_avail; + break; + + case HEADER_CPU_TOPOLOGY: + if (!tchart->topology) + break; + + if (svg_build_topology_map(ph->env.sibling_cores, + ph->env.nr_sibling_cores, + ph->env.sibling_threads, + ph->env.nr_sibling_threads)) + fprintf(stderr, "problem building topology\n"); + break; + + default: + break; + } + + return 0; +} + +static int __cmd_timechart(struct timechart *tchart, const char *output_name) { - struct perf_tool perf_timechart = { - .comm = process_comm_event, - .fork = process_fork_event, - .exit = process_exit_event, - .sample = process_sample_event, - .ordered_samples = true, - }; const struct perf_evsel_str_handler power_tracepoints[] = { { "power:cpu_idle", process_sample_cpu_idle }, { "power:cpu_frequency", process_sample_cpu_frequency }, @@ -997,12 +1114,17 @@ static int __cmd_timechart(const char *output_name) }; struct perf_session *session = perf_session__new(&file, false, - &perf_timechart); + &tchart->tool); int ret = -EINVAL; if (session == NULL) return -ENOMEM; + (void)perf_header__process_sections(&session->header, + perf_data_file__fd(session->file), + tchart, + process_header); + if (!perf_session__has_traces(session, "timechart record")) goto out_delete; @@ -1012,69 +1134,111 @@ static int __cmd_timechart(const char *output_name) goto out_delete; } - ret = perf_session__process_events(session, &perf_timechart); + ret = perf_session__process_events(session, &tchart->tool); if (ret) goto out_delete; - end_sample_processing(); + end_sample_processing(tchart); - sort_pids(); + sort_pids(tchart); - write_svg_file(output_name); + write_svg_file(tchart, output_name); pr_info("Written %2.1f seconds of trace to %s.\n", - (last_time - first_time) / 1000000000.0, output_name); + (tchart->last_time - tchart->first_time) / 1000000000.0, output_name); out_delete: perf_session__delete(session); return ret; } -static int __cmd_record(int argc, const char **argv) +static int timechart__record(struct timechart *tchart, int argc, const char **argv) { -#ifdef SUPPORT_OLD_POWER_EVENTS - const char * const record_old_args[] = { + unsigned int rec_argc, i, j; + const char **rec_argv; + const char **p; + unsigned int record_elems; + + const char * const common_args[] = { "record", "-a", "-R", "-c", "1", + }; + unsigned int common_args_nr = ARRAY_SIZE(common_args); + + const char * const backtrace_args[] = { + "-g", + }; + unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args); + + const char * const power_args[] = { + "-e", "power:cpu_frequency", + "-e", "power:cpu_idle", + }; + unsigned int power_args_nr = ARRAY_SIZE(power_args); + + const char * const old_power_args[] = { +#ifdef SUPPORT_OLD_POWER_EVENTS "-e", "power:power_start", "-e", "power:power_end", "-e", "power:power_frequency", - "-e", "sched:sched_wakeup", - "-e", "sched:sched_switch", - }; #endif - const char * const record_new_args[] = { - "record", "-a", "-R", "-c", "1", - "-e", "power:cpu_frequency", - "-e", "power:cpu_idle", + }; + unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args); + + const char * const tasks_args[] = { "-e", "sched:sched_wakeup", "-e", "sched:sched_switch", }; - unsigned int rec_argc, i, j; - const char **rec_argv; - const char * const *record_args = record_new_args; - unsigned int record_elems = ARRAY_SIZE(record_new_args); + unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args); #ifdef SUPPORT_OLD_POWER_EVENTS if (!is_valid_tracepoint("power:cpu_idle") && is_valid_tracepoint("power:power_start")) { use_old_power_events = 1; - record_args = record_old_args; - record_elems = ARRAY_SIZE(record_old_args); + power_args_nr = 0; + } else { + old_power_args_nr = 0; } #endif - rec_argc = record_elems + argc - 1; + if (tchart->power_only) + tasks_args_nr = 0; + + if (tchart->tasks_only) { + power_args_nr = 0; + old_power_args_nr = 0; + } + + if (!tchart->with_backtrace) + backtrace_args_no = 0; + + record_elems = common_args_nr + tasks_args_nr + + power_args_nr + old_power_args_nr + backtrace_args_no; + + rec_argc = record_elems + argc; rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (rec_argv == NULL) return -ENOMEM; - for (i = 0; i < record_elems; i++) - rec_argv[i] = strdup(record_args[i]); + p = rec_argv; + for (i = 0; i < common_args_nr; i++) + *p++ = strdup(common_args[i]); - for (j = 1; j < (unsigned int)argc; j++, i++) - rec_argv[i] = argv[j]; + for (i = 0; i < backtrace_args_no; i++) + *p++ = strdup(backtrace_args[i]); - return cmd_record(i, rec_argv, NULL); + for (i = 0; i < tasks_args_nr; i++) + *p++ = strdup(tasks_args[i]); + + for (i = 0; i < power_args_nr; i++) + *p++ = strdup(power_args[i]); + + for (i = 0; i < old_power_args_nr; i++) + *p++ = strdup(old_power_args[i]); + + for (j = 1; j < (unsigned int)argc; j++) + *p++ = argv[j]; + + return cmd_record(rec_argc, rec_argv, NULL); } static int @@ -1086,20 +1250,56 @@ parse_process(const struct option *opt __maybe_unused, const char *arg, return 0; } +static int +parse_highlight(const struct option *opt __maybe_unused, const char *arg, + int __maybe_unused unset) +{ + unsigned long duration = strtoul(arg, NULL, 0); + + if (svg_highlight || svg_highlight_name) + return -1; + + if (duration) + svg_highlight = duration; + else + svg_highlight_name = strdup(arg); + + return 0; +} + int cmd_timechart(int argc, const char **argv, const char *prefix __maybe_unused) { + struct timechart tchart = { + .tool = { + .comm = process_comm_event, + .fork = process_fork_event, + .exit = process_exit_event, + .sample = process_sample_event, + .ordered_samples = true, + }, + .proc_num = 15, + }; const char *output_name = "output.svg"; - const struct option options[] = { + const struct option timechart_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_INTEGER('w', "width", &svg_page_width, "page width"), - OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), + OPT_CALLBACK(0, "highlight", NULL, "duration or task name", + "highlight tasks. Pass duration in ns or process name.", + parse_highlight), + OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), + OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, + "output processes data only"), OPT_CALLBACK('p', "process", NULL, "process", "process selector. Pass a pid or process name.", parse_process), OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), + OPT_INTEGER('n', "proc-num", &tchart.proc_num, + "min. number of tasks to print"), + OPT_BOOLEAN('t', "topology", &tchart.topology, + "sort CPUs according to topology"), OPT_END() }; const char * const timechart_usage[] = { @@ -1107,17 +1307,41 @@ int cmd_timechart(int argc, const char **argv, NULL }; - argc = parse_options(argc, argv, options, timechart_usage, + const struct option record_options[] = { + OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), + OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, + "output processes data only"), + OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), + OPT_END() + }; + const char * const record_usage[] = { + "perf timechart record []", + NULL + }; + argc = parse_options(argc, argv, timechart_options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (tchart.power_only && tchart.tasks_only) { + pr_err("-P and -T options cannot be used at the same time.\n"); + return -1; + } + symbol__init(); - if (argc && !strncmp(argv[0], "rec", 3)) - return __cmd_record(argc, argv); - else if (argc) - usage_with_options(timechart_usage, options); + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = parse_options(argc, argv, record_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (tchart.power_only && tchart.tasks_only) { + pr_err("-P and -T options cannot be used at the same time.\n"); + return -1; + } + + return timechart__record(&tchart, argc, argv); + } else if (argc) + usage_with_options(timechart_usage, timechart_options); setup_pager(); - return __cmd_timechart(output_name); + return __cmd_timechart(&tchart, output_name); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 71e6402729a8..76cd510d34d0 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -189,21 +189,18 @@ static void perf_top__record_precise_ip(struct perf_top *top, if (pthread_mutex_trylock(¬es->lock)) return; - if (notes->src == NULL && symbol__alloc_hist(sym) < 0) { - pthread_mutex_unlock(¬es->lock); - pr_err("Not enough memory for annotating '%s' symbol!\n", - sym->name); - sleep(1); - return; - } - ip = he->ms.map->map_ip(he->ms.map, ip); - err = symbol__inc_addr_samples(sym, he->ms.map, counter, ip); + err = hist_entry__inc_addr_samples(he, counter, ip); pthread_mutex_unlock(¬es->lock); if (err == -ERANGE && !he->ms.map->erange_warned) ui__warn_map_erange(he->ms.map, sym, ip); + else if (err == -ENOMEM) { + pr_err("Not enough memory for annotating '%s' symbol!\n", + sym->name); + sleep(1); + } } static void perf_top__show_details(struct perf_top *top) @@ -485,7 +482,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) fprintf(stderr, "\nAvailable events:"); - list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) + evlist__for_each(top->evlist, top->sym_evsel) fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel)); prompt_integer(&counter, "Enter details event counter"); @@ -496,7 +493,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) sleep(1); break; } - list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) + evlist__for_each(top->evlist, top->sym_evsel) if (top->sym_evsel->idx == counter) break; } else @@ -578,7 +575,7 @@ static void *display_thread_tui(void *arg) * Zooming in/out UIDs. For now juse use whatever the user passed * via --uid. */ - list_for_each_entry(pos, &top->evlist->entries, node) + evlist__for_each(top->evlist, pos) pos->hists.uid_filter_str = top->record_opts.target.uid_str; perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, @@ -634,26 +631,9 @@ repeat: return NULL; } -/* Tag samples to be skipped. */ -static const char *skip_symbols[] = { - "intel_idle", - "default_idle", - "native_safe_halt", - "cpu_idle", - "enter_idle", - "exit_idle", - "mwait_idle", - "mwait_idle_with_hints", - "poll_idle", - "ppc64_runlatch_off", - "pseries_dedicated_idle_sleep", - NULL -}; - static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) { const char *name = sym->name; - int i; /* * ppc64 uses function descriptors and appends a '.' to the @@ -671,12 +651,8 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) strstr(name, "_text_end")) return 1; - for (i = 0; skip_symbols[i]; i++) { - if (!strcmp(skip_symbols[i], name)) { - sym->ignore = true; - break; - } - } + if (symbol__is_idle(sym)) + sym->ignore = true; return 0; } @@ -767,15 +743,10 @@ static void perf_event__process_sample(struct perf_tool *tool, if (al.sym == NULL || !al.sym->ignore) { struct hist_entry *he; - if ((sort__has_parent || symbol_conf.use_callchain) && - sample->callchain) { - err = machine__resolve_callchain(machine, evsel, - al.thread, sample, - &parent, &al, - top->max_stack); - if (err) - return; - } + err = sample__resolve_callchain(sample, &parent, evsel, &al, + top->max_stack); + if (err) + return; he = perf_evsel__add_hist_entry(evsel, &al, sample); if (he == NULL) { @@ -783,12 +754,9 @@ static void perf_event__process_sample(struct perf_tool *tool, return; } - if (symbol_conf.use_callchain) { - err = callchain_append(he->callchain, &callchain_cursor, - sample->period); - if (err) - return; - } + err = hist_entry__append_callchain(he, sample); + if (err) + return; if (sort__has_sym) perf_top__record_precise_ip(top, he, evsel->idx, ip); @@ -878,11 +846,11 @@ static int perf_top__start_counters(struct perf_top *top) char msg[512]; struct perf_evsel *counter; struct perf_evlist *evlist = top->evlist; - struct perf_record_opts *opts = &top->record_opts; + struct record_opts *opts = &top->record_opts; perf_evlist__config(evlist, opts); - list_for_each_entry(counter, &evlist->entries, node) { + evlist__for_each(evlist, counter) { try_again: if (perf_evsel__open(counter, top->evlist->cpus, top->evlist->threads) < 0) { @@ -930,7 +898,7 @@ static int perf_top__setup_sample_type(struct perf_top *top __maybe_unused) static int __cmd_top(struct perf_top *top) { - struct perf_record_opts *opts = &top->record_opts; + struct record_opts *opts = &top->record_opts; pthread_t thread; int ret; @@ -1052,7 +1020,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) .max_stack = PERF_MAX_STACK_DEPTH, .sym_pcnt_filter = 5, }; - struct perf_record_opts *opts = &top.record_opts; + struct record_opts *opts = &top.record_opts; struct target *target = &opts->target; const struct option options[] = { OPT_CALLBACK('e', "event", &top.evlist, "event", @@ -1084,7 +1052,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) "dump the symbol table used for profiling"), OPT_INTEGER('f', "count-filter", &top.count_filter, "only display functions with more events than this"), - OPT_BOOLEAN('g', "group", &opts->group, + OPT_BOOLEAN(0, "group", &opts->group, "put the counters into a counter group"), OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit, "child tasks do not inherit counters"), @@ -1105,7 +1073,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) " abort, in_tx, transaction"), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), - OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts, + OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, NULL, "enables call-graph recording", &callchain_opt), OPT_CALLBACK(0, "call-graph", &top.record_opts, @@ -1195,7 +1163,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (!top.evlist->nr_entries && perf_evlist__add_default(top.evlist) < 0) { ui__error("Not enough memory for event selector list\n"); - goto out_delete_maps; + goto out_delete_evlist; } symbol_conf.nr_events = top.evlist->nr_entries; @@ -1203,9 +1171,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (top.delay_secs < 1) top.delay_secs = 1; - if (perf_record_opts__config(opts)) { + if (record_opts__config(opts)) { status = -EINVAL; - goto out_delete_maps; + goto out_delete_evlist; } top.sym_evsel = perf_evlist__first(top.evlist); @@ -1230,8 +1198,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) status = __cmd_top(&top); -out_delete_maps: - perf_evlist__delete_maps(top.evlist); out_delete_evlist: perf_evlist__delete(top.evlist); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8be17fc462ba..896f27047ed6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -11,6 +11,8 @@ #include "util/intlist.h" #include "util/thread_map.h" #include "util/stat.h" +#include "trace-event.h" +#include "util/parse-events.h" #include #include @@ -144,8 +146,7 @@ static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, static void perf_evsel__delete_priv(struct perf_evsel *evsel) { - free(evsel->priv); - evsel->priv = NULL; + zfree(&evsel->priv); perf_evsel__delete(evsel); } @@ -163,8 +164,7 @@ static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler) return -ENOMEM; out_delete: - free(evsel->priv); - evsel->priv = NULL; + zfree(&evsel->priv); return -ENOENT; } @@ -172,6 +172,10 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void { struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); + /* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */ + if (evsel == NULL) + evsel = perf_evsel__newtp("syscalls", direction); + if (evsel) { if (perf_evsel__init_syscall_tp(evsel, handler)) goto out_delete; @@ -1153,29 +1157,30 @@ struct trace { int max; struct syscall *table; } syscalls; - struct perf_record_opts opts; + struct record_opts opts; struct machine *host; u64 base_time; - bool full_time; FILE *output; unsigned long nr_events; struct strlist *ev_qualifier; - bool not_ev_qualifier; - bool live; const char *last_vfs_getname; struct intlist *tid_list; struct intlist *pid_list; + double duration_filter; + double runtime_ms; + struct { + u64 vfs_getname, + proc_getname; + } stats; + bool not_ev_qualifier; + bool live; + bool full_time; bool sched; bool multiple_threads; bool summary; bool summary_only; bool show_comm; bool show_tool_stats; - double duration_filter; - double runtime_ms; - struct { - u64 vfs_getname, proc_getname; - } stats; }; static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) @@ -1272,10 +1277,8 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); struct thread_trace *ttrace = arg->thread->priv; - if (ttrace && fd >= 0 && fd <= ttrace->paths.max) { - free(ttrace->paths.table[fd]); - ttrace->paths.table[fd] = NULL; - } + if (ttrace && fd >= 0 && fd <= ttrace->paths.max) + zfree(&ttrace->paths.table[fd]); return printed; } @@ -1430,11 +1433,11 @@ static int trace__read_syscall_info(struct trace *trace, int id) sc->fmt = syscall_fmt__find(sc->name); snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); - sc->tp_format = event_format__new("syscalls", tp_name); + sc->tp_format = trace_event__tp_format("syscalls", tp_name); if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) { snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); - sc->tp_format = event_format__new("syscalls", tp_name); + sc->tp_format = trace_event__tp_format("syscalls", tp_name); } if (sc->tp_format == NULL) @@ -1764,8 +1767,10 @@ static int trace__process_sample(struct perf_tool *tool, if (!trace->full_time && trace->base_time == 0) trace->base_time = sample->time; - if (handler) + if (handler) { + ++trace->nr_events; handler(trace, evsel, sample); + } return err; } @@ -1800,10 +1805,11 @@ static int trace__record(int argc, const char **argv) "-R", "-m", "1024", "-c", "1", - "-e", "raw_syscalls:sys_enter,raw_syscalls:sys_exit", + "-e", }; - rec_argc = ARRAY_SIZE(record_args) + argc; + /* +1 is for the event string below */ + rec_argc = ARRAY_SIZE(record_args) + 1 + argc; rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (rec_argv == NULL) @@ -1812,6 +1818,17 @@ static int trace__record(int argc, const char **argv) for (i = 0; i < ARRAY_SIZE(record_args); i++) rec_argv[i] = record_args[i]; + /* event string may be different for older kernels - e.g., RHEL6 */ + if (is_valid_tracepoint("raw_syscalls:sys_enter")) + rec_argv[i] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit"; + else if (is_valid_tracepoint("syscalls:sys_enter")) + rec_argv[i] = "syscalls:sys_enter,syscalls:sys_exit"; + else { + pr_err("Neither raw_syscalls nor syscalls events exist.\n"); + return -1; + } + i++; + for (j = 0; j < (unsigned int)argc; j++, i++) rec_argv[i] = argv[j]; @@ -1869,7 +1886,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) err = trace__symbols_init(trace, evlist); if (err < 0) { fprintf(trace->output, "Problems initializing symbol libraries!\n"); - goto out_delete_maps; + goto out_delete_evlist; } perf_evlist__config(evlist, &trace->opts); @@ -1879,10 +1896,10 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evlist, &trace->opts.target, - argv, false, false); + argv, false, NULL); if (err < 0) { fprintf(trace->output, "Couldn't run the workload!\n"); - goto out_delete_maps; + goto out_delete_evlist; } } @@ -1890,10 +1907,10 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (err < 0) goto out_error_open; - err = perf_evlist__mmap(evlist, UINT_MAX, false); + 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)); - goto out_close_evlist; + goto out_delete_evlist; } perf_evlist__enable(evlist); @@ -1977,11 +1994,6 @@ out_disable: } } - perf_evlist__munmap(evlist); -out_close_evlist: - perf_evlist__close(evlist); -out_delete_maps: - perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: @@ -2047,6 +2059,10 @@ static int trace__replay(struct trace *trace) evsel = perf_evlist__find_tracepoint_by_name(session->evlist, "raw_syscalls:sys_enter"); + /* older kernels have syscalls tp versus raw_syscalls */ + if (evsel == NULL) + evsel = perf_evlist__find_tracepoint_by_name(session->evlist, + "syscalls:sys_enter"); if (evsel == NULL) { pr_err("Data file does not have raw_syscalls:sys_enter event\n"); goto out; @@ -2060,6 +2076,9 @@ static int trace__replay(struct trace *trace) evsel = perf_evlist__find_tracepoint_by_name(session->evlist, "raw_syscalls:sys_exit"); + if (evsel == NULL) + evsel = perf_evlist__find_tracepoint_by_name(session->evlist, + "syscalls:sys_exit"); if (evsel == NULL) { pr_err("Data file does not have raw_syscalls:sys_exit event\n"); goto out; @@ -2158,7 +2177,6 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) size_t printed = data->printed; struct trace *trace = data->trace; struct thread_trace *ttrace = thread->priv; - const char *color; double ratio; if (ttrace == NULL) @@ -2166,17 +2184,9 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; - color = PERF_COLOR_NORMAL; - if (ratio > 50.0) - color = PERF_COLOR_RED; - else if (ratio > 25.0) - color = PERF_COLOR_GREEN; - else if (ratio > 5.0) - color = PERF_COLOR_YELLOW; - - printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid); + printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); printed += fprintf(fp, "%lu events, ", ttrace->nr_events); - printed += color_fprintf(fp, color, "%.1f%%", ratio); + printed += fprintf(fp, "%.1f%%", ratio); printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); printed += thread__dump_stats(ttrace, trace, fp); @@ -2248,7 +2258,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) }, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .no_delay = true, + .no_buffering = true, .mmap_pages = 1024, }, .output = stdout, diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index f7d11a811c74..d604e50fc167 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -1,44 +1,3 @@ -uname_M := $(shell uname -m 2>/dev/null || echo not) - -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ - -e s/arm.*/arm/ -e s/sa110/arm/ \ - -e s/s390x/s390/ -e s/parisc64/parisc/ \ - -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ - -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) -NO_PERF_REGS := 1 -CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) - -# Additional ARCH settings for x86 -ifeq ($(ARCH),i386) - override ARCH := x86 - NO_PERF_REGS := 0 - LIBUNWIND_LIBS = -lunwind -lunwind-x86 -endif - -ifeq ($(ARCH),x86_64) - override ARCH := x86 - IS_X86_64 := 0 - ifeq (, $(findstring m32,$(CFLAGS))) - IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) - endif - ifeq (${IS_X86_64}, 1) - RAW_ARCH := x86_64 - CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT - ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S - LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 - else - LIBUNWIND_LIBS = -lunwind -lunwind-x86 - endif - NO_PERF_REGS := 0 -endif -ifeq ($(ARCH),arm) - NO_PERF_REGS := 0 - LIBUNWIND_LIBS = -lunwind -lunwind-arm -endif - -ifeq ($(NO_PERF_REGS),0) - CFLAGS += -DHAVE_PERF_REGS_SUPPORT -endif ifeq ($(src-perf),) src-perf := $(srctree)/tools/perf @@ -53,6 +12,52 @@ obj-perf := $(abspath $(obj-perf))/ endif LIB_INCLUDE := $(srctree)/tools/lib/ +CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) + +include $(src-perf)/config/Makefile.arch + +NO_PERF_REGS := 1 + +# Additional ARCH settings for x86 +ifeq ($(ARCH),x86) + ifeq (${IS_X86_64}, 1) + CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT + ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S + LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 + else + LIBUNWIND_LIBS = -lunwind -lunwind-x86 + endif + NO_PERF_REGS := 0 +endif +ifeq ($(ARCH),arm) + NO_PERF_REGS := 0 + LIBUNWIND_LIBS = -lunwind -lunwind-arm +endif + +ifeq ($(LIBUNWIND_LIBS),) + NO_LIBUNWIND := 1 +else + # + # For linking with debug library, run like: + # + # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ + # + ifdef LIBUNWIND_DIR + LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include + LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib + endif + LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS) + + # Set per-feature check compilation flags + FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) + FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) + FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) + FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) +endif + +ifeq ($(NO_PERF_REGS),0) + CFLAGS += -DHAVE_PERF_REGS_SUPPORT +endif # include ARCH specific config -include $(src-perf)/arch/$(ARCH)/Makefile @@ -102,7 +107,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS="$(LDFLAGS)" LIBUNWIND_LIBS="$(LIBUNWIND_LIBS)" -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C config/feature-checks test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) @@ -141,16 +146,26 @@ CORE_FEATURE_TESTS = \ libslang \ libunwind \ on-exit \ - stackprotector \ stackprotector-all \ timerfd +# Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. +# If in the future we need per-feature checks/flags for features not +# mentioned in this list we need to refactor this ;-). +set_test_all_flags = $(eval $(set_test_all_flags_code)) +define set_test_all_flags_code + FEATURE_CHECK_CFLAGS-all += $(FEATURE_CHECK_CFLAGS-$(1)) + FEATURE_CHECK_LDFLAGS-all += $(FEATURE_CHECK_LDFLAGS-$(1)) +endef + +$(foreach feat,$(CORE_FEATURE_TESTS),$(call set_test_all_flags,$(feat))) + # # So here we detect whether test-all was rebuilt, to be able # to skip the print-out of the long features list if the file # existed before and after it was built: # -ifeq ($(wildcard $(OUTPUT)config/feature-checks/test-all),) +ifeq ($(wildcard $(OUTPUT)config/feature-checks/test-all.bin),) test-all-failed := 1 else test-all-failed := 0 @@ -180,7 +195,7 @@ ifeq ($(feature-all), 1) # $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) + $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(addsuffix .bin,$(CORE_FEATURE_TESTS)) >/dev/null 2>&1) $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif @@ -209,10 +224,6 @@ ifeq ($(feature-stackprotector-all), 1) CFLAGS += -fstack-protector-all endif -ifeq ($(feature-stackprotector), 1) - CFLAGS += -Wstack-protector -endif - ifeq ($(DEBUG),0) ifeq ($(feature-fortify-source), 1) CFLAGS += -D_FORTIFY_SOURCE=2 @@ -221,6 +232,7 @@ endif CFLAGS += -I$(src-perf)/util/include CFLAGS += -I$(src-perf)/arch/$(ARCH)/include +CFLAGS += -I$(srctree)/tools/include/ CFLAGS += -I$(srctree)/arch/$(ARCH)/include/uapi CFLAGS += -I$(srctree)/arch/$(ARCH)/include CFLAGS += -I$(srctree)/include/uapi @@ -310,21 +322,7 @@ ifndef NO_LIBELF endif # NO_DWARF endif # NO_LIBELF -ifeq ($(LIBUNWIND_LIBS),) - NO_LIBUNWIND := 1 -endif - ifndef NO_LIBUNWIND - # - # For linking with debug library, run like: - # - # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ - # - ifdef LIBUNWIND_DIR - LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include - LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib - endif - ifneq ($(feature-libunwind), 1) msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1); NO_LIBUNWIND := 1 @@ -339,14 +337,12 @@ ifndef NO_LIBUNWIND # non-ARM has no dwarf_find_debug_frame() function: CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME endif - endif -endif -ifndef NO_LIBUNWIND - CFLAGS += -DHAVE_LIBUNWIND_SUPPORT - EXTLIBS += $(LIBUNWIND_LIBS) - CFLAGS += $(LIBUNWIND_CFLAGS) - LDFLAGS += $(LIBUNWIND_LDFLAGS) + CFLAGS += -DHAVE_LIBUNWIND_SUPPORT + EXTLIBS += $(LIBUNWIND_LIBS) + CFLAGS += $(LIBUNWIND_CFLAGS) + LDFLAGS += $(LIBUNWIND_LDFLAGS) + endif # ifneq ($(feature-libunwind), 1) endif ifndef NO_LIBAUDIT @@ -376,7 +372,7 @@ ifndef NO_SLANG endif ifndef NO_GTK2 - FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) + FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) ifneq ($(feature-gtk2), 1) msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); NO_GTK2 := 1 @@ -385,8 +381,8 @@ ifndef NO_GTK2 GTK_CFLAGS := -DHAVE_GTK_INFO_BAR_SUPPORT endif CFLAGS += -DHAVE_GTK2_SUPPORT - GTK_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) - GTK_LIBS := $(shell pkg-config --libs gtk+-2.0 2>/dev/null) + GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk+-2.0 2>/dev/null) + GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-2.0 2>/dev/null) EXTLIBS += -ldl endif endif @@ -533,7 +529,7 @@ endif ifndef NO_LIBNUMA ifeq ($(feature-libnuma), 0) - msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); + msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numactl-devel/libnuma-devel/libnuma-dev); NO_LIBNUMA := 1 else CFLAGS += -DHAVE_LIBNUMA_SUPPORT @@ -598,3 +594,11 @@ else perfexec_instdir = $(prefix)/$(perfexecdir) endif perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) + +# If we install to $(HOME) we keep the traceevent default: +# $(HOME)/.traceevent/plugins +# Otherwise we install plugins into the global $(libdir). +ifdef DESTDIR +plugindir=$(libdir)/traceevent/plugins +plugindir_SQ= $(subst ','\'',$(prefix)/$(plugindir)) +endif diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch new file mode 100644 index 000000000000..fef8ae922800 --- /dev/null +++ b/tools/perf/config/Makefile.arch @@ -0,0 +1,22 @@ + +uname_M := $(shell uname -m 2>/dev/null || echo not) + +ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/s390x/s390/ -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ + -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) + +# Additional ARCH settings for x86 +ifeq ($(ARCH),i386) + override ARCH := x86 +endif + +ifeq ($(ARCH),x86_64) + override ARCH := x86 + IS_X86_64 := 0 + ifeq (, $(findstring m32,$(CFLAGS))) + IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) + RAW_ARCH := x86_64 + endif +endif diff --git a/tools/perf/config/feature-checks/.gitignore b/tools/perf/config/feature-checks/.gitignore new file mode 100644 index 000000000000..80f3da0c3515 --- /dev/null +++ b/tools/perf/config/feature-checks/.gitignore @@ -0,0 +1,2 @@ +*.d +*.bin diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 87e790017c69..12e551346fa6 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -1,95 +1,92 @@ FILES= \ - test-all \ - test-backtrace \ - test-bionic \ - test-dwarf \ - test-fortify-source \ - test-glibc \ - test-gtk2 \ - test-gtk2-infobar \ - test-hello \ - test-libaudit \ - test-libbfd \ - test-liberty \ - test-liberty-z \ - test-cplus-demangle \ - test-libelf \ - test-libelf-getphdrnum \ - test-libelf-mmap \ - test-libnuma \ - test-libperl \ - test-libpython \ - test-libpython-version \ - test-libslang \ - test-libunwind \ - test-libunwind-debug-frame \ - test-on-exit \ - test-stackprotector-all \ - test-stackprotector \ - test-timerfd + test-all.bin \ + test-backtrace.bin \ + test-bionic.bin \ + test-dwarf.bin \ + test-fortify-source.bin \ + test-glibc.bin \ + test-gtk2.bin \ + test-gtk2-infobar.bin \ + test-hello.bin \ + test-libaudit.bin \ + test-libbfd.bin \ + test-liberty.bin \ + test-liberty-z.bin \ + test-cplus-demangle.bin \ + test-libelf.bin \ + test-libelf-getphdrnum.bin \ + test-libelf-mmap.bin \ + test-libnuma.bin \ + test-libperl.bin \ + test-libpython.bin \ + test-libpython-version.bin \ + test-libslang.bin \ + test-libunwind.bin \ + test-libunwind-debug-frame.bin \ + test-on-exit.bin \ + test-stackprotector-all.bin \ + test-timerfd.bin -CC := $(CC) -MD +CC := $(CROSS_COMPILE)gcc -MD +PKG_CONFIG := $(CROSS_COMPILE)pkg-config all: $(FILES) -BUILD = $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUTPUT)$@ $@.c +BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) ############################### -test-all: - $(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma $(LIBUNWIND_LIBS) -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl +test-all.bin: + $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -test-hello: +test-hello.bin: $(BUILD) -test-stackprotector-all: +test-stackprotector-all.bin: $(BUILD) -Werror -fstack-protector-all -test-stackprotector: - $(BUILD) -Werror -fstack-protector -Wstack-protector - -test-fortify-source: +test-fortify-source.bin: $(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 -test-bionic: +test-bionic.bin: $(BUILD) -test-libelf: +test-libelf.bin: $(BUILD) -lelf -test-glibc: +test-glibc.bin: $(BUILD) -test-dwarf: +test-dwarf.bin: $(BUILD) -ldw -test-libelf-mmap: +test-libelf-mmap.bin: $(BUILD) -lelf -test-libelf-getphdrnum: +test-libelf-getphdrnum.bin: $(BUILD) -lelf -test-libnuma: +test-libnuma.bin: $(BUILD) -lnuma -test-libunwind: - $(BUILD) $(LIBUNWIND_LIBS) -lelf +test-libunwind.bin: + $(BUILD) -lelf -test-libunwind-debug-frame: - $(BUILD) $(LIBUNWIND_LIBS) -lelf +test-libunwind-debug-frame.bin: + $(BUILD) -lelf -test-libaudit: +test-libaudit.bin: $(BUILD) -laudit -test-libslang: +test-libslang.bin: $(BUILD) -I/usr/include/slang -lslang -test-gtk2: - $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) +test-gtk2.bin: + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -test-gtk2-infobar: - $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) +test-gtk2-infobar.bin: + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) grep-libs = $(filter -l%,$(1)) strip-libs = $(filter-out -l%,$(1)) @@ -100,7 +97,7 @@ PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) -test-libperl: +test-libperl.bin: $(BUILD) $(FLAGS_PERL_EMBED) override PYTHON := python @@ -117,31 +114,31 @@ 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: +test-libpython.bin: $(BUILD) $(FLAGS_PYTHON_EMBED) -test-libpython-version: +test-libpython-version.bin: $(BUILD) $(FLAGS_PYTHON_EMBED) -test-libbfd: +test-libbfd.bin: $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl -test-liberty: +test-liberty.bin: $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -test-liberty-z: +test-liberty-z.bin: $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -lz -test-cplus-demangle: +test-cplus-demangle.bin: $(BUILD) -liberty -test-on-exit: +test-on-exit.bin: $(BUILD) -test-backtrace: +test-backtrace.bin: $(BUILD) -test-timerfd: +test-timerfd.bin: $(BUILD) -include *.d diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 59e7a705e146..9b8a544155bb 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c @@ -85,6 +85,10 @@ # include "test-timerfd.c" #undef main +#define main main_test_stackprotector_all +# include "test-stackprotector-all.c" +#undef main + int main(int argc, char *argv[]) { main_test_libpython(); @@ -106,6 +110,7 @@ int main(int argc, char *argv[]) main_test_backtrace(); main_test_libnuma(); main_test_timerfd(); + main_test_stackprotector_all(); return 0; } diff --git a/tools/perf/config/feature-checks/test-stackprotector.c b/tools/perf/config/feature-checks/test-stackprotector.c deleted file mode 100644 index c9f398d87868..000000000000 --- a/tools/perf/config/feature-checks/test-stackprotector.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(void) -{ - return puts("hi"); -} diff --git a/tools/perf/config/feature-checks/test-volatile-register-var.c b/tools/perf/config/feature-checks/test-volatile-register-var.c deleted file mode 100644 index c9f398d87868..000000000000 --- a/tools/perf/config/feature-checks/test-volatile-register-var.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(void) -{ - return puts("hi"); -} diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index f168debc5be2..4d985e0f03f5 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -178,10 +178,3 @@ endef _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2))) _gea_warn = $(warning The path '$(1)' is not executable.) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) - -ifneq ($(findstring $(MAKEFLAGS),s),s) - ifneq ($(V),1) - QUIET_CLEAN = @printf ' CLEAN %s\n' $1; - QUIET_INSTALL = @printf ' INSTALL %s\n' $1; - endif -endif diff --git a/tools/perf/bash_completion b/tools/perf/perf-completion.sh similarity index 63% rename from tools/perf/bash_completion rename to tools/perf/perf-completion.sh index 62e157db2e2b..496e2abb5482 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/perf-completion.sh @@ -1,4 +1,4 @@ -# perf completion +# perf bash and zsh completion # Taken from git.git's completion script. __my_reassemble_comp_words_by_ref() @@ -89,37 +89,117 @@ __ltrim_colon_completions() fi } -type perf &>/dev/null && -_perf() +__perfcomp () { - local cur words cword prev cmd + COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) +} - COMPREPLY=() - _get_comp_words_by_ref -n =: cur words cword prev +__perfcomp_colon () +{ + __perfcomp "$1" "$2" + __ltrim_colon_completions $cur +} + +__perf_main () +{ + local cmd cmd=${words[0]} + COMPREPLY=() # List perf subcommands or long options if [ $cword -eq 1 ]; then if [[ $cur == --* ]]; then - COMPREPLY=( $( compgen -W '--help --version \ + __perfcomp '--help --version \ --exec-path --html-path --paginate --no-pager \ - --perf-dir --work-tree --debugfs-dir' -- "$cur" ) ) + --perf-dir --work-tree --debugfs-dir' -- "$cur" else cmds=$($cmd --list-cmds) - COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) + __perfcomp "$cmds" "$cur" fi # List possible events for -e option elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then evts=$($cmd list --raw-dump) - COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) ) - __ltrim_colon_completions $cur + __perfcomp_colon "$evts" "$cur" + # List subcommands for 'perf kvm' + elif [[ $prev == "kvm" ]]; then + subcmds="top record report diff buildid-list stat" + __perfcomp_colon "$subcmds" "$cur" # List long option names elif [[ $cur == --* ]]; then subcmd=${words[1]} opts=$($cmd $subcmd --list-opts) - COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) + __perfcomp "$opts" "$cur" fi +} + +if [[ -n ${ZSH_VERSION-} ]]; then + autoload -U +X compinit && compinit + + __perfcomp () + { + emulate -L zsh + + local c IFS=$' \t\n' + local -a array + + for c in ${=1}; do + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + array[${#array[@]}+1]="$c" + done + + compset -P '*[=:]' + compadd -Q -S '' -a -- array && _ret=0 + } + + __perfcomp_colon () + { + emulate -L zsh + + local cur_="${2-$cur}" + local c IFS=$' \t\n' + local -a array + + if [[ "$cur_" == *:* ]]; then + local colon_word=${cur_%"${cur_##*:}"} + fi + + for c in ${=1}; do + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + array[$#array+1]=${c#"$colon_word"} + done + + compset -P '*[=:]' + compadd -Q -S '' -a -- array && _ret=0 + } + + _perf () + { + local _ret=1 cur cword prev + cur=${words[CURRENT]} + prev=${words[CURRENT-1]} + let cword=CURRENT-1 + emulate ksh -c __perf_main + let _ret && _default && _ret=0 + return _ret + } + + compdef _perf perf + return +fi + +type perf &>/dev/null && +_perf() +{ + local cur words cword prev + _get_comp_words_by_ref -n =: cur words cword prev + __perf_main } && complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 8b38b4e80ec2..431798a4110d 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,7 +13,7 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" -#include +#include #include const char perf_usage_string[] = diff --git a/tools/perf/perf.h b/tools/perf/perf.h index b079304bd53d..3c2f213e979d 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -247,13 +247,14 @@ enum perf_call_graph_mode { CALLCHAIN_DWARF }; -struct perf_record_opts { +struct record_opts { struct target target; int call_graph; bool group; bool inherit_stat; - bool no_delay; + bool no_buffering; bool no_inherit; + bool no_inherit_set; bool no_samples; bool raw_samples; bool sample_address; @@ -268,6 +269,7 @@ struct perf_record_opts { u64 user_interval; u16 stack_dump_size; bool sample_transaction; + unsigned initial_delay; }; #endif diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/attr/test-record-no-inherit index 9079a25cd643..44edcb2edcd5 100644 --- a/tools/perf/tests/attr/test-record-no-inherit +++ b/tools/perf/tests/attr/test-record-no-inherit @@ -3,5 +3,5 @@ command = record args = -i kill >/dev/null 2>&1 [event:base-record] -sample_type=259 +sample_type=263 inherit=0 diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 85d4919dd623..653a8fe2db95 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -391,7 +391,7 @@ static int do_test_code_reading(bool try_kcore) struct machines machines; struct machine *machine; struct thread *thread; - struct perf_record_opts opts = { + struct record_opts opts = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, @@ -540,14 +540,11 @@ static int do_test_code_reading(bool try_kcore) err = TEST_CODE_READING_OK; out_err: if (evlist) { - perf_evlist__munmap(evlist); - perf_evlist__close(evlist); perf_evlist__delete(evlist); - } - if (cpus) + } else { cpu_map__delete(cpus); - if (threads) thread_map__delete(threads); + } machines__destroy_kernel_maps(&machines); machine__delete_threads(machine); machines__exit(&machines); diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 0197bda9c461..465cdbc345cf 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -79,7 +79,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) } err = 0; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { --err; pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 173bf42cc03e..2b6519e0e36f 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -208,7 +208,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) * However the second evsel also has a collapsed entry for * "bash [libc] malloc" so total 9 entries will be in the tree. */ - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { const union perf_event event = { .header = { @@ -466,7 +466,7 @@ int test__hists_link(void) if (err < 0) goto out; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { hists__collapse_resort(&evsel->hists, NULL); if (verbose > 2) diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 376c35608534..497957f269d8 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -51,7 +51,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm) */ int test__keep_tracking(void) { - struct perf_record_opts opts = { + struct record_opts opts = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, @@ -142,14 +142,11 @@ int test__keep_tracking(void) out_err: if (evlist) { perf_evlist__disable(evlist); - perf_evlist__munmap(evlist); - perf_evlist__close(evlist); perf_evlist__delete(evlist); - } - if (cpus) + } else { cpu_map__delete(cpus); - if (threads) thread_map__delete(threads); + } return err; } diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 2ca0abf1b2b6..00544b8b644b 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -1,6 +1,16 @@ PERF := . MK := Makefile +include config/Makefile.arch + +# FIXME looks like x86 is the only arch running tests ;-) +# we need some IS_(32/64) flag to make this generic +ifeq ($(IS_X86_64),1) +lib = lib64 +else +lib = lib +endif + has = $(shell which $1 2>/dev/null) # standard single make variable specified @@ -106,10 +116,36 @@ test_make_python_perf_so := test -f $(PERF)/python/perf.so test_make_perf_o := test -f $(PERF)/perf.o test_make_util_map_o := test -f $(PERF)/util/map.o -test_make_install := test -x $$TMP_DEST/bin/perf -test_make_install_O := $(test_make_install) -test_make_install_bin := $(test_make_install) -test_make_install_bin_O := $(test_make_install) +define test_dest_files + for file in $(1); do \ + if [ ! -x $$TMP_DEST/$$file ]; then \ + echo " failed to find: $$file"; \ + fi \ + done +endef + +installed_files_bin := bin/perf +installed_files_bin += etc/bash_completion.d/perf +installed_files_bin += libexec/perf-core/perf-archive + +installed_files_plugins := $(lib)/traceevent/plugins/plugin_cfg80211.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_scsi.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_xen.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_function.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_sched_switch.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_mac80211.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_kvm.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_kmem.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_hrtimer.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_jbd2.so + +installed_files_all := $(installed_files_bin) +installed_files_all += $(installed_files_plugins) + +test_make_install := $(call test_dest_files,$(installed_files_all)) +test_make_install_O := $(call test_dest_files,$(installed_files_all)) +test_make_install_bin := $(call test_dest_files,$(installed_files_bin)) +test_make_install_bin_O := $(call test_dest_files,$(installed_files_bin)) # FIXME nothing gets installed test_make_install_man := test -f $$TMP_DEST/share/man/man1/perf.1 @@ -162,7 +198,7 @@ $(run): cmd="cd $(PERF) && make -f $(MK) DESTDIR=$$TMP_DEST $($@)"; \ echo "- $@: $$cmd" && echo $$cmd > $@ && \ ( eval $$cmd ) >> $@ 2>&1; \ - echo " test: $(call test,$@)"; \ + echo " test: $(call test,$@)" >> $@ 2>&1; \ $(call test,$@) && \ rm -f $@ \ rm -rf $$TMP_DEST @@ -174,16 +210,22 @@ $(run_O): cmd="cd $(PERF) && make -f $(MK) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \ echo "- $@: $$cmd" && echo $$cmd > $@ && \ ( eval $$cmd ) >> $@ 2>&1 && \ - echo " test: $(call test_O,$@)"; \ + echo " test: $(call test_O,$@)" >> $@ 2>&1; \ $(call test_O,$@) && \ rm -f $@ && \ rm -rf $$TMP_O \ rm -rf $$TMP_DEST -all: $(run) $(run_O) +tarpkg: + @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ + echo "- $@: $$cmd" && echo $$cmd > $@ && \ + ( eval $$cmd ) >> $@ 2>&1 + + +all: $(run) $(run_O) tarpkg @echo OK out: $(run_O) @echo OK -.PHONY: all $(run) $(run_O) clean +.PHONY: all $(run) $(run_O) tarpkg clean diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index d64ab79c6d35..142263492f6f 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -68,7 +68,7 @@ int test__basic_mmap(void) evsels[i] = perf_evsel__newtp("syscalls", name); if (evsels[i] == NULL) { pr_debug("perf_evsel__new\n"); - goto out_free_evlist; + goto out_delete_evlist; } evsels[i]->attr.wakeup_events = 1; @@ -80,7 +80,7 @@ int test__basic_mmap(void) pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); - goto out_close_fd; + goto out_delete_evlist; } nr_events[i] = 0; @@ -90,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)); - goto out_close_fd; + goto out_delete_evlist; } for (i = 0; i < nsyscalls; ++i) @@ -105,13 +105,13 @@ int test__basic_mmap(void) if (event->header.type != PERF_RECORD_SAMPLE) { pr_debug("unexpected %s event\n", perf_event__name(event->header.type)); - goto out_munmap; + goto out_delete_evlist; } err = perf_evlist__parse_sample(evlist, event, &sample); if (err) { pr_err("Can't parse sample, err = %d\n", err); - goto out_munmap; + goto out_delete_evlist; } err = -1; @@ -119,30 +119,27 @@ int test__basic_mmap(void) if (evsel == NULL) { pr_debug("event with id %" PRIu64 " doesn't map to an evsel\n", sample.id); - goto out_munmap; + goto out_delete_evlist; } nr_events[evsel->idx]++; perf_evlist__mmap_consume(evlist, 0); } err = 0; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { pr_debug("expected %d %s events, got %d\n", expected_nr_events[evsel->idx], perf_evsel__name(evsel), nr_events[evsel->idx]); err = -1; - goto out_munmap; + goto out_delete_evlist; } } -out_munmap: - perf_evlist__munmap(evlist); -out_close_fd: - for (i = 0; i < nsyscalls; ++i) - perf_evsel__close_fd(evsels[i], 1, threads->nr); -out_free_evlist: +out_delete_evlist: perf_evlist__delete(evlist); + cpus = NULL; + threads = NULL; out_free_cpus: cpu_map__delete(cpus); out_free_threads: diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 41cc0badb74b..c505ef2af245 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -6,15 +6,15 @@ int test__syscall_open_tp_fields(void) { - struct perf_record_opts opts = { + struct record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, - .no_delay = true, - .freq = 1, - .mmap_pages = 256, - .raw_samples = true, + .no_buffering = true, + .freq = 1, + .mmap_pages = 256, + .raw_samples = true, }; const char *filename = "/etc/passwd"; int flags = O_RDONLY | O_DIRECTORY; @@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(void) err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror(errno)); - goto out_delete_maps; + goto out_delete_evlist; } err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); - goto out_close_evlist; + goto out_delete_evlist; } perf_evlist__enable(evlist); @@ -85,7 +85,7 @@ int test__syscall_open_tp_fields(void) err = perf_evsel__parse_sample(evsel, event, &sample); if (err) { pr_err("Can't parse sample, err = %d\n", err); - goto out_munmap; + goto out_delete_evlist; } tp_flags = perf_evsel__intval(evsel, &sample, "flags"); @@ -93,7 +93,7 @@ int test__syscall_open_tp_fields(void) if (flags != tp_flags) { pr_debug("%s: Expected flags=%#x, got %#x\n", __func__, flags, tp_flags); - goto out_munmap; + goto out_delete_evlist; } goto out_ok; @@ -105,17 +105,11 @@ int test__syscall_open_tp_fields(void) if (++nr_polls > 5) { pr_debug("%s: no events!\n", __func__); - goto out_munmap; + goto out_delete_evlist; } } out_ok: err = 0; -out_munmap: - perf_evlist__munmap(evlist); -out_close_evlist: - perf_evlist__close(evlist); -out_delete_maps: - perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 3cbd10496087..4db0ae617d70 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -3,7 +3,7 @@ #include "evsel.h" #include "evlist.h" #include "fs.h" -#include +#include #include "tests.h" #include @@ -30,7 +30,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); TEST_ASSERT_VAL("wrong sample_type", @@ -201,7 +201,7 @@ test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -1385,10 +1385,10 @@ static int test_event(struct evlist_test *e) if (ret) { pr_debug("failed to parse event '%s', err %d\n", e->name, ret); - return ret; + } else { + ret = e->check(evlist); } - - ret = e->check(evlist); + perf_evlist__delete(evlist); return ret; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 93a62b06c3af..aca1a83dd13a 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -34,14 +34,14 @@ realloc: int test__PERF_RECORD(void) { - struct perf_record_opts opts = { + struct record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, - .no_delay = true, - .freq = 10, - .mmap_pages = 256, + .no_buffering = true, + .freq = 10, + .mmap_pages = 256, }; cpu_set_t cpu_mask; size_t cpu_mask_size = sizeof(cpu_mask); @@ -83,11 +83,10 @@ int test__PERF_RECORD(void) * so that we have time to open the evlist (calling sys_perf_event_open * on all the fds) and then mmap them. */ - err = perf_evlist__prepare_workload(evlist, &opts.target, argv, - false, false); + err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false, NULL); if (err < 0) { pr_debug("Couldn't run the workload!\n"); - goto out_delete_maps; + goto out_delete_evlist; } /* @@ -102,7 +101,7 @@ 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)); - goto out_delete_maps; + goto out_delete_evlist; } cpu = err; @@ -112,7 +111,7 @@ int test__PERF_RECORD(void) */ if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { pr_debug("sched_setaffinity: %s\n", strerror(errno)); - goto out_delete_maps; + goto out_delete_evlist; } /* @@ -122,7 +121,7 @@ int test__PERF_RECORD(void) err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror(errno)); - goto out_delete_maps; + goto out_delete_evlist; } /* @@ -133,7 +132,7 @@ 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)); - goto out_close_evlist; + goto out_delete_evlist; } /* @@ -166,7 +165,7 @@ int test__PERF_RECORD(void) if (verbose) perf_event__fprintf(event, stderr); pr_debug("Couldn't parse sample\n"); - goto out_err; + goto out_delete_evlist; } if (verbose) { @@ -303,12 +302,6 @@ found_exit: pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); ++errs; } -out_err: - perf_evlist__munmap(evlist); -out_close_evlist: - perf_evlist__close(evlist); -out_delete_maps: - perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: diff --git a/tools/perf/tests/perf-targz-src-pkg b/tools/perf/tests/perf-targz-src-pkg new file mode 100755 index 000000000000..238aa3927c71 --- /dev/null +++ b/tools/perf/tests/perf-targz-src-pkg @@ -0,0 +1,21 @@ +#!/bin/sh +# Test one of the main kernel Makefile targets to generate a perf sources tarball +# suitable for build outside the full kernel sources. +# +# This is to test that the tools/perf/MANIFEST file lists all the files needed to +# be in such tarball, which sometimes gets broken when we move files around, +# like when we made some files that were in tools/perf/ available to other tools/ +# codebases by moving it to tools/include/, etc. + +PERF=$1 +cd ${PERF}/../.. +make perf-targz-src-pkg > /dev/null +TARBALL=$(ls -rt perf-*.tar.gz) +TMP_DEST=$(mktemp -d) +tar xf ${TARBALL} -C $TMP_DEST +rm -f ${TARBALL} +cd - > /dev/null +make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 +RC=$? +rm -rf ${TMP_DEST} +exit $RC diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index 4ca1b938f6a6..47146d388dbf 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -46,7 +46,7 @@ static u64 rdtsc(void) */ int test__perf_time_to_tsc(void) { - struct perf_record_opts opts = { + struct record_opts opts = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, @@ -166,14 +166,8 @@ next_event: out_err: if (evlist) { perf_evlist__disable(evlist); - perf_evlist__munmap(evlist); - perf_evlist__close(evlist); perf_evlist__delete(evlist); } - if (cpus) - cpu_map__delete(cpus); - if (threads) - thread_map__delete(threads); return err; } diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 6664a7cd828c..983d6b8562a8 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -45,7 +45,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) evsel = perf_evsel__new(&attr); if (evsel == NULL) { pr_debug("perf_evsel__new\n"); - goto out_free_evlist; + goto out_delete_evlist; } perf_evlist__add(evlist, evsel); @@ -54,7 +54,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) if (!evlist->cpus || !evlist->threads) { err = -ENOMEM; pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_maps; + goto out_delete_evlist; } if (perf_evlist__open(evlist)) { @@ -63,14 +63,14 @@ 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); - goto out_delete_maps; + 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)); - goto out_close_evlist; + goto out_delete_evlist; } perf_evlist__enable(evlist); @@ -90,7 +90,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) err = perf_evlist__parse_sample(evlist, event, &sample); if (err < 0) { pr_debug("Error during parse sample\n"); - goto out_unmap_evlist; + goto out_delete_evlist; } total_periods += sample.period; @@ -105,13 +105,7 @@ next_event: err = -1; } -out_unmap_evlist: - perf_evlist__munmap(evlist); -out_close_evlist: - perf_evlist__close(evlist); -out_delete_maps: - perf_evlist__delete_maps(evlist); -out_free_evlist: +out_delete_evlist: perf_evlist__delete(evlist); return err; } diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index d09ab579119e..5ff3db318f12 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -9,12 +9,21 @@ static int exited; static int nr_exit; -static void sig_handler(int sig) +static void sig_handler(int sig __maybe_unused) { exited = 1; +} - if (sig == SIGUSR1) - nr_exit = -1; +/* + * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since + * we asked by setting its exec_error to this handler. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, + siginfo_t *info __maybe_unused, + void *ucontext __maybe_unused) +{ + exited = 1; + nr_exit = -1; } /* @@ -35,7 +44,6 @@ int test__task_exit(void) const char *argv[] = { "true", NULL }; signal(SIGCHLD, sig_handler); - signal(SIGUSR1, sig_handler); evlist = perf_evlist__new_default(); if (evlist == NULL) { @@ -54,13 +62,14 @@ int test__task_exit(void) if (!evlist->cpus || !evlist->threads) { err = -ENOMEM; pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_maps; + goto out_delete_evlist; } - err = perf_evlist__prepare_workload(evlist, &target, argv, false, true); + err = perf_evlist__prepare_workload(evlist, &target, argv, false, + workload_exec_failed_signal); if (err < 0) { pr_debug("Couldn't run the workload!\n"); - goto out_delete_maps; + goto out_delete_evlist; } evsel = perf_evlist__first(evlist); @@ -74,13 +83,13 @@ int test__task_exit(void) err = perf_evlist__open(evlist); if (err < 0) { pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); - goto out_delete_maps; + goto out_delete_evlist; } if (perf_evlist__mmap(evlist, 128, true) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, strerror(errno)); - goto out_close_evlist; + goto out_delete_evlist; } perf_evlist__start_workload(evlist); @@ -103,11 +112,7 @@ retry: err = -1; } - perf_evlist__munmap(evlist); -out_close_evlist: - perf_evlist__close(evlist); -out_delete_maps: - perf_evlist__delete_maps(evlist); +out_delete_evlist: perf_evlist__delete(evlist); return err; } diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index cbaa7af45513..d11541d4d7d7 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -256,8 +256,7 @@ int ui_browser__show(struct ui_browser *browser, const char *title, __ui_browser__show_title(browser, title); browser->title = title; - free(browser->helpline); - browser->helpline = NULL; + zfree(&browser->helpline); va_start(ap, helpline); err = vasprintf(&browser->helpline, helpline, ap); @@ -268,12 +267,11 @@ int ui_browser__show(struct ui_browser *browser, const char *title, return err ? 0 : -1; } -void ui_browser__hide(struct ui_browser *browser __maybe_unused) +void ui_browser__hide(struct ui_browser *browser) { pthread_mutex_lock(&ui__lock); ui_helpline__pop(); - free(browser->helpline); - browser->helpline = NULL; + zfree(&browser->helpline); pthread_mutex_unlock(&ui__lock); } diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 7d45d2f53601..118cca29dd26 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -59,6 +59,8 @@ int ui_browser__help_window(struct ui_browser *browser, const char *text); bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); int ui_browser__input_window(const char *title, const char *text, char *input, const char *exit_msg, int delay_sec); +struct perf_session_env; +int tui__header_window(struct perf_session_env *env); void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); unsigned int ui_browser__argv_refresh(struct ui_browser *browser); diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c new file mode 100644 index 000000000000..89c16b988618 --- /dev/null +++ b/tools/perf/ui/browsers/header.c @@ -0,0 +1,127 @@ +#include "util/cache.h" +#include "util/debug.h" +#include "ui/browser.h" +#include "ui/ui.h" +#include "ui/util.h" +#include "ui/libslang.h" +#include "util/header.h" +#include "util/session.h" + +static void ui_browser__argv_write(struct ui_browser *browser, + void *entry, int row) +{ + char **arg = entry; + char *str = *arg; + char empty[] = " "; + bool current_entry = ui_browser__is_current_entry(browser, row); + unsigned long offset = (unsigned long)browser->priv; + + if (offset >= strlen(str)) + str = empty; + else + str = str + offset; + + ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : + HE_COLORSET_NORMAL); + + slsmg_write_nstring(str, browser->width); +} + +static int list_menu__run(struct ui_browser *menu) +{ + int key; + unsigned long offset; + const char help[] = + "h/?/F1 Show this window\n" + "UP/DOWN/PGUP\n" + "PGDN/SPACE\n" + "LEFT/RIGHT Navigate\n" + "q/ESC/CTRL+C Exit browser"; + + if (ui_browser__show(menu, "Header information", "Press 'q' to exit") < 0) + return -1; + + while (1) { + key = ui_browser__run(menu, 0); + + switch (key) { + case K_RIGHT: + offset = (unsigned long)menu->priv; + offset += 10; + menu->priv = (void *)offset; + continue; + case K_LEFT: + offset = (unsigned long)menu->priv; + if (offset >= 10) + offset -= 10; + menu->priv = (void *)offset; + continue; + case K_F1: + case 'h': + case '?': + ui_browser__help_window(menu, help); + continue; + case K_ESC: + case 'q': + case CTRL('c'): + key = -1; + break; + default: + continue; + } + + break; + } + + ui_browser__hide(menu); + return key; +} + +static int ui__list_menu(int argc, char * const argv[]) +{ + struct ui_browser menu = { + .entries = (void *)argv, + .refresh = ui_browser__argv_refresh, + .seek = ui_browser__argv_seek, + .write = ui_browser__argv_write, + .nr_entries = argc, + }; + + return list_menu__run(&menu); +} + +int tui__header_window(struct perf_session_env *env) +{ + int i, argc = 0; + char **argv; + struct perf_session *session; + char *ptr, *pos; + size_t size; + FILE *fp = open_memstream(&ptr, &size); + + session = container_of(env, struct perf_session, header.env); + perf_header__fprintf_info(session, fp, true); + fclose(fp); + + for (pos = ptr, argc = 0; (pos = strchr(pos, '\n')) != NULL; pos++) + argc++; + + argv = calloc(argc + 1, sizeof(*argv)); + if (argv == NULL) + goto out; + + argv[0] = pos = ptr; + for (i = 1; (pos = strchr(pos, '\n')) != NULL; i++) { + *pos++ = '\0'; + argv[i] = pos; + } + + BUG_ON(i != argc + 1); + + ui__list_menu(argc, argv); + +out: + free(argv); + free(ptr); + return 0; +} diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a440e03cd8c2..b720b92eba6e 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1267,10 +1267,8 @@ static inline void free_popup_options(char **options, int n) { int i; - for (i = 0; i < n; ++i) { - free(options[i]); - options[i] = NULL; - } + for (i = 0; i < n; ++i) + zfree(&options[i]); } /* Check whether the browser is for 'top' or 'report' */ @@ -1329,7 +1327,7 @@ static int switch_data_file(void) abs_path[nr_options] = strdup(path); if (!abs_path[nr_options]) { - free(options[nr_options]); + zfree(&options[nr_options]); ui__warning("Can't search all data files due to memory shortage.\n"); fclose(file); break; @@ -1400,6 +1398,36 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, char script_opt[64]; int delay_secs = hbt ? hbt->refresh : 0; +#define HIST_BROWSER_HELP_COMMON \ + "h/?/F1 Show this window\n" \ + "UP/DOWN/PGUP\n" \ + "PGDN/SPACE Navigate\n" \ + "q/ESC/CTRL+C Exit browser\n\n" \ + "For multiple event sessions:\n\n" \ + "TAB/UNTAB Switch events\n\n" \ + "For symbolic views (--sort has sym):\n\n" \ + "-> Zoom into DSO/Threads & Annotate current symbol\n" \ + "<- Zoom out\n" \ + "a Annotate current symbol\n" \ + "C Collapse all callchains\n" \ + "d Zoom into current DSO\n" \ + "E Expand all callchains\n" \ + + /* help messages are sorted by lexical order of the hotkey */ + const char report_help[] = HIST_BROWSER_HELP_COMMON + "i Show header information\n" + "P Print histograms to perf.hist.N\n" + "r Run available scripts\n" + "s Switch to another data file in PWD\n" + "t Zoom into current Thread\n" + "V Verbose (DSO names in callchains, etc)\n" + "/ Filter symbol by name"; + const char top_help[] = HIST_BROWSER_HELP_COMMON + "P Print histograms to perf.hist.N\n" + "t Zoom into current Thread\n" + "V Verbose (DSO names in callchains, etc)\n" + "/ Filter symbol by name"; + if (browser == NULL) return -1; @@ -1484,29 +1512,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, if (is_report_browser(hbt)) goto do_data_switch; continue; + case 'i': + /* env->arch is NULL for live-mode (i.e. perf top) */ + if (env->arch) + tui__header_window(env); + continue; case K_F1: case 'h': case '?': ui_browser__help_window(&browser->b, - "h/?/F1 Show this window\n" - "UP/DOWN/PGUP\n" - "PGDN/SPACE Navigate\n" - "q/ESC/CTRL+C Exit browser\n\n" - "For multiple event sessions:\n\n" - "TAB/UNTAB Switch events\n\n" - "For symbolic views (--sort has sym):\n\n" - "-> Zoom into DSO/Threads & Annotate current symbol\n" - "<- Zoom out\n" - "a Annotate current symbol\n" - "C Collapse all callchains\n" - "E Expand all callchains\n" - "d Zoom into current DSO\n" - "t Zoom into current Thread\n" - "r Run available scripts('perf report' only)\n" - "s Switch to another data file in PWD ('perf report' only)\n" - "P Print histograms to perf.hist.N\n" - "V Verbose (DSO names in callchains, etc)\n" - "/ Filter symbol by name"); + is_report_browser(hbt) ? report_help : top_help); continue; case K_ENTER: case K_RIGHT: @@ -1923,7 +1938,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, ui_helpline__push("Press ESC to exit"); - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { const char *ev_name = perf_evsel__name(pos); size_t line_len = strlen(ev_name) + 7; @@ -1955,9 +1970,10 @@ single_entry: struct perf_evsel *pos; nr_entries = 0; - list_for_each_entry(pos, &evlist->entries, node) + evlist__for_each(evlist, pos) { if (perf_evsel__is_group_leader(pos)) nr_entries++; + } if (nr_entries == 1) goto single_entry; diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index d63c68ea02a8..402d2bd30b09 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -173,8 +173,7 @@ int script_browse(const char *script_opt) if (script.b.width > AVERAGE_LINE_LEN) script.b.width = AVERAGE_LINE_LEN; - if (line) - free(line); + free(line); pclose(fp); script.nr_lines = nr_entries; diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 2ca66cc1160f..5b95c44f3435 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -375,7 +375,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, gtk_container_add(GTK_CONTAINER(window), vbox); - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { struct hists *hists = &pos->hists; const char *evname = perf_evsel__name(pos); GtkWidget *scrolled_window; diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index 696c1fbe4248..52e7fc48af9f 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c @@ -23,8 +23,7 @@ int perf_gtk__deactivate_context(struct perf_gtk_context **ctx) if (!perf_gtk__is_active_context(*ctx)) return -1; - free(*ctx); - *ctx = NULL; + zfree(ctx); return 0; } diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index c244cb524ef2..831fbb77d1ff 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -510,7 +510,7 @@ print_entries: free(line); out: - free(rem_sq_bracket); + zfree(&rem_sq_bracket); return ret; } diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c index 092902e30cee..bf890f72fe80 100644 --- a/tools/perf/ui/tui/util.c +++ b/tools/perf/ui/tui/util.c @@ -92,6 +92,8 @@ int ui_browser__input_window(const char *title, const char *text, char *input, t = sep + 1; } + pthread_mutex_lock(&ui__lock); + max_len += 2; nr_lines += 8; y = SLtt_Screen_Rows / 2 - nr_lines / 2; @@ -120,13 +122,19 @@ int ui_browser__input_window(const char *title, const char *text, char *input, SLsmg_write_nstring((char *)exit_msg, max_len); SLsmg_refresh(); + pthread_mutex_unlock(&ui__lock); + x += 2; len = 0; key = ui__getch(delay_secs); while (key != K_TIMER && key != K_ENTER && key != K_ESC) { + pthread_mutex_lock(&ui__lock); + if (key == K_BKSPC) { - if (len == 0) + if (len == 0) { + pthread_mutex_unlock(&ui__lock); goto next_key; + } SLsmg_gotorc(y, x + --len); SLsmg_write_char(' '); } else { @@ -136,6 +144,8 @@ int ui_browser__input_window(const char *title, const char *text, char *input, } SLsmg_refresh(); + pthread_mutex_unlock(&ui__lock); + /* XXX more graceful overflow handling needed */ if (len == sizeof(buf) - 1) { ui_helpline__push("maximum size of symbol name reached!"); @@ -174,6 +184,8 @@ int ui__question_window(const char *title, const char *text, t = sep + 1; } + pthread_mutex_lock(&ui__lock); + max_len += 2; nr_lines += 4; y = SLtt_Screen_Rows / 2 - nr_lines / 2, @@ -195,6 +207,9 @@ int ui__question_window(const char *title, const char *text, SLsmg_gotorc(y + nr_lines - 1, x); SLsmg_write_nstring((char *)exit_msg, max_len); SLsmg_refresh(); + + pthread_mutex_unlock(&ui__lock); + return ui__getch(delay_secs); } @@ -215,9 +230,7 @@ static int __ui__warning(const char *title, const char *format, va_list args) if (vasprintf(&s, format, args) > 0) { int key; - pthread_mutex_lock(&ui__lock); key = ui__question_window(title, s, "Press any key...", 0); - pthread_mutex_unlock(&ui__lock); free(s); return key; } diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index e6d134773d0a..c0b43ee40d95 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c @@ -55,8 +55,7 @@ int split_cmdline(char *cmdline, const char ***argv) src++; c = cmdline[src]; if (!c) { - free(*argv); - *argv = NULL; + zfree(argv); return error("cmdline ends with \\"); } } @@ -68,8 +67,7 @@ int split_cmdline(char *cmdline, const char ***argv) cmdline[dst] = 0; if (quoted) { - free(*argv); - *argv = NULL; + zfree(argv); return error("unclosed quote"); } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index cf6242c92ee2..469eb679fb9d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -26,10 +26,10 @@ static int disasm_line__parse(char *line, char **namep, char **rawp); static void ins__delete(struct ins_operands *ops) { - free(ops->source.raw); - free(ops->source.name); - free(ops->target.raw); - free(ops->target.name); + zfree(&ops->source.raw); + zfree(&ops->source.name); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, @@ -185,8 +185,7 @@ static int lock__parse(struct ins_operands *ops) return 0; out_free_ops: - free(ops->locked.ops); - ops->locked.ops = NULL; + zfree(&ops->locked.ops); return 0; } @@ -205,9 +204,9 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size, static void lock__delete(struct ins_operands *ops) { - free(ops->locked.ops); - free(ops->target.raw); - free(ops->target.name); + zfree(&ops->locked.ops); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static struct ins_ops lock_ops = { @@ -256,8 +255,7 @@ static int mov__parse(struct ins_operands *ops) return 0; out_free_source: - free(ops->source.raw); - ops->source.raw = NULL; + zfree(&ops->source.raw); return -1; } @@ -464,17 +462,12 @@ void symbol__annotate_zero_histograms(struct symbol *sym) pthread_mutex_unlock(¬es->lock); } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, - int evidx, u64 addr) +static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, + struct annotation *notes, int evidx, u64 addr) { unsigned offset; - struct annotation *notes; struct sym_hist *h; - notes = symbol__annotation(sym); - if (notes->src == NULL) - return -ENOMEM; - pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); if (addr < sym->start || addr > sym->end) @@ -491,6 +484,33 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, return 0; } +static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, + int evidx, u64 addr) +{ + struct annotation *notes; + + if (sym == NULL || use_browser != 1 || !sort__has_sym) + return 0; + + notes = symbol__annotation(sym); + if (notes->src == NULL) { + if (symbol__alloc_hist(sym) < 0) + return -ENOMEM; + } + + return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); +} + +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) +{ + return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); +} + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) +{ + return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); +} + static void disasm_line__init_ins(struct disasm_line *dl) { dl->ins = ins__find(dl->name); @@ -538,8 +558,7 @@ static int disasm_line__parse(char *line, char **namep, char **rawp) return 0; out_free_name: - free(*namep); - *namep = NULL; + zfree(namep); return -1; } @@ -564,7 +583,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs return dl; out_free_line: - free(dl->line); + zfree(&dl->line); out_delete: free(dl); return NULL; @@ -572,8 +591,8 @@ out_delete: void disasm_line__free(struct disasm_line *dl) { - free(dl->line); - free(dl->name); + zfree(&dl->line); + zfree(&dl->name); if (dl->ins && dl->ins->ops->free) dl->ins->ops->free(&dl->ops); else @@ -900,7 +919,7 @@ fallback: * cache, or is just a kallsyms file, well, lets hope that this * DSO is the same as when 'perf record' ran. */ - filename = dso->long_name; + filename = (char *)dso->long_name; snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", symbol_conf.symfs, filename); free_filename = false; @@ -1091,8 +1110,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) src_line = (void *)src_line + sizeof_src_line; } - free(notes->src->lines); - notes->src->lines = NULL; + zfree(¬es->src->lines); } /* Get the filename:line for the colored entries */ @@ -1376,3 +1394,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, return 0; } + +int hist_entry__annotate(struct hist_entry *he, size_t privsize) +{ + return symbol__annotate(he->ms.sym, he->ms.map, privsize); +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 834b7b57b788..b2aef59d6bb2 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -132,12 +132,17 @@ static inline struct annotation *symbol__annotation(struct symbol *sym) return &a->annotation; } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, - int evidx, u64 addr); +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx); + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); + int symbol__alloc_hist(struct symbol *sym); void symbol__annotate_zero_histograms(struct symbol *sym); int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); + +int hist_entry__annotate(struct hist_entry *he, size_t privsize); + int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); int symbol__annotate_printf(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool full_paths, diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index a92770c98cc7..6baabe63182b 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -89,7 +89,7 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) return raw - build_id; } -char *dso__build_id_filename(struct dso *dso, char *bf, size_t size) +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 929f28a7c14d..845ef865eced 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -10,7 +10,7 @@ extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *dso, char *bf, size_t size); +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e3970e3eaacf..8d9db454f1a9 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -15,8 +15,12 @@ #include #include +#include "asm/bug.h" + #include "hist.h" #include "util.h" +#include "sort.h" +#include "machine.h" #include "callchain.h" __thread struct callchain_cursor callchain_cursor; @@ -356,19 +360,14 @@ append_chain_children(struct callchain_node *root, /* lookup in childrens */ while (*p) { s64 ret; - struct callchain_list *cnode; parent = *p; rnode = rb_entry(parent, struct callchain_node, rb_node_in); - cnode = list_first_entry(&rnode->val, struct callchain_list, - list); - /* just check first entry */ - ret = match_chain(node, cnode); - if (ret == 0) { - append_chain(rnode, cursor, period); + /* If at least first entry matches, rely to children */ + ret = append_chain(rnode, cursor, period); + if (ret == 0) goto inc_children_hit; - } if (ret < 0) p = &parent->rb_left; @@ -389,11 +388,11 @@ append_chain(struct callchain_node *root, struct callchain_cursor *cursor, u64 period) { - struct callchain_cursor_node *curr_snap = cursor->curr; struct callchain_list *cnode; u64 start = cursor->pos; bool found = false; u64 matches; + int cmp = 0; /* * Lookup in the current node @@ -408,7 +407,8 @@ append_chain(struct callchain_node *root, if (!node) break; - if (match_chain(node, cnode) != 0) + cmp = match_chain(node, cnode); + if (cmp) break; found = true; @@ -418,9 +418,8 @@ append_chain(struct callchain_node *root, /* matches not, relay no the parent */ if (!found) { - cursor->curr = curr_snap; - cursor->pos = start; - return -1; + WARN_ONCE(!cmp, "Chain comparison error\n"); + return cmp; } matches = cursor->pos - start; @@ -531,3 +530,24 @@ int callchain_cursor_append(struct callchain_cursor *cursor, return 0; } + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, + struct perf_evsel *evsel, struct addr_location *al, + int max_stack) +{ + if (sample->callchain == NULL) + return 0; + + if (symbol_conf.use_callchain || sort__has_parent) { + return machine__resolve_callchain(al->machine, evsel, al->thread, + sample, parent, al, max_stack); + } + return 0; +} + +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) +{ + if (!symbol_conf.use_callchain) + return 0; + return callchain_append(he->callchain, &callchain_cursor, sample->period); +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 4f7f989876ec..8ad97e9b119f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -145,10 +145,16 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) } struct option; +struct hist_entry; -int record_parse_callchain(const char *arg, struct perf_record_opts *opts); +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); +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, + struct perf_evsel *evsel, struct addr_location *al, + int max_stack); +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); + extern const char record_callchain_help[]; #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 96bbda1ddb83..88f7be399432 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -81,7 +81,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str) /* * check if cgrp is already defined, if so we reuse it */ - list_for_each_entry(counter, &evlist->entries, node) { + evlist__for_each(evlist, counter) { cgrp = counter->cgrp; if (!cgrp) continue; @@ -110,7 +110,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str) * if add cgroup N, then need to find event N */ n = 0; - list_for_each_entry(counter, &evlist->entries, node) { + evlist__for_each(evlist, counter) { if (n == nr_cgroups) goto found; n++; @@ -133,7 +133,7 @@ void close_cgroup(struct cgroup_sel *cgrp) /* XXX: not reentrant */ if (--cgrp->refcnt == 0) { close(cgrp->fd); - free(cgrp->name); + zfree(&cgrp->name); free(cgrp); } } diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 66e44a5019d5..87b8672eb413 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -1,6 +1,7 @@ #include #include "cache.h" #include "color.h" +#include int perf_use_color_default = -1; @@ -298,10 +299,10 @@ const char *get_percent_color(double percent) * entries in green - and keep the low overhead places * normal: */ - if (percent >= MIN_RED) + if (fabs(percent) >= MIN_RED) color = PERF_COLOR_RED; else { - if (percent > MIN_GREEN) + if (fabs(percent) > MIN_GREEN) color = PERF_COLOR_GREEN; } return color; @@ -318,15 +319,19 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value) +{ + const char *color = get_percent_color(value); + return color_snprintf(bf, size, color, fmt, value); +} + int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) { va_list args; double percent; - const char *color; va_start(args, fmt); percent = va_arg(args, double); va_end(args); - color = get_percent_color(percent); - return color_snprintf(bf, size, color, fmt, percent); + return value_color_snprintf(bf, size, fmt, percent); } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index fced3840e99c..7ff30a62a132 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,6 +39,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); 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_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index ee0df0e24cdb..f9e777629e21 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -21,7 +21,7 @@ static void comm_str__put(struct comm_str *cs) { if (!--cs->ref) { rb_erase(&cs->rb_node, &comm_str_root); - free(cs->str); + zfree(&cs->str); free(cs); } } @@ -94,19 +94,20 @@ struct comm *comm__new(const char *str, u64 timestamp) return comm; } -void comm__override(struct comm *comm, const char *str, u64 timestamp) +int comm__override(struct comm *comm, const char *str, u64 timestamp) { - struct comm_str *old = comm->comm_str; + struct comm_str *new, *old = comm->comm_str; - comm->comm_str = comm_str__findnew(str, &comm_str_root); - if (!comm->comm_str) { - comm->comm_str = old; - return; - } + new = comm_str__findnew(str, &comm_str_root); + if (!new) + return -ENOMEM; - comm->start = timestamp; - comm_str__get(comm->comm_str); + comm_str__get(new); comm_str__put(old); + comm->comm_str = new; + comm->start = timestamp; + + return 0; } void comm__free(struct comm *comm) diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index 7a86e5656710..fac5bd51befc 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h @@ -16,6 +16,6 @@ struct comm { void comm__free(struct comm *comm); struct comm *comm__new(const char *str, u64 timestamp); const char *comm__str(const struct comm *comm); -void comm__override(struct comm *comm, const char *str, u64 timestamp); +int comm__override(struct comm *comm, const char *str, u64 timestamp); #endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 7d09faf85cf1..1fbcd8bdc11b 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -118,3 +118,9 @@ void perf_data_file__close(struct perf_data_file *file) { close(file->fd); } + +ssize_t perf_data_file__write(struct perf_data_file *file, + void *buf, size_t size) +{ + return writen(file->fd, buf, size); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 8c2df80152a5..2b15d0c95c7f 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -9,12 +9,12 @@ enum perf_data_mode { }; struct perf_data_file { - const char *path; - int fd; - bool is_pipe; - bool force; - unsigned long size; - enum perf_data_mode mode; + const char *path; + int fd; + bool is_pipe; + bool force; + unsigned long size; + enum perf_data_mode mode; }; static inline bool perf_data_file__is_read(struct perf_data_file *file) @@ -44,5 +44,7 @@ static inline unsigned long perf_data_file__size(struct perf_data_file *file) int perf_data_file__open(struct perf_data_file *file); void perf_data_file__close(struct perf_data_file *file); +ssize_t perf_data_file__write(struct perf_data_file *file, + void *buf, size_t size); #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 399e74c34c1a..299b55586502 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -16,23 +16,46 @@ int verbose; bool dump_trace = false, quiet = false; -int eprintf(int level, const char *fmt, ...) +static int _eprintf(int level, const char *fmt, va_list args) { - va_list args; int ret = 0; if (verbose >= level) { - va_start(args, fmt); if (use_browser >= 1) ui_helpline__vshow(fmt, args); else ret = vfprintf(stderr, fmt, args); - va_end(args); } return ret; } +int eprintf(int level, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = _eprintf(level, fmt, args); + va_end(args); + + return ret; +} + +/* + * Overloading libtraceevent standard info print + * function, display with -v in perf. + */ +void pr_stat(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _eprintf(1, fmt, args); + va_end(args); + eprintf(1, "\n"); +} + int dump_printf(const char *fmt, ...) { va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index efbd98805ad0..443694c36b03 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -17,4 +17,6 @@ void trace_event(union perf_event *event); int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +void pr_stat(const char *fmt, ...); + #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index af4c687cc49b..4045d086d9d9 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -28,8 +28,9 @@ char dso__symtab_origin(const struct dso *dso) return origin[dso->symtab_type]; } -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, - char *root_dir, char *file, size_t size) +int dso__read_binary_type_filename(const struct dso *dso, + enum dso_binary_type type, + char *root_dir, char *filename, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = 0; @@ -38,36 +39,36 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, case DSO_BINARY_TYPE__DEBUGLINK: { char *debuglink; - strncpy(file, dso->long_name, size); - debuglink = file + dso->long_name_len; - while (debuglink != file && *debuglink != '/') + strncpy(filename, dso->long_name, size); + debuglink = filename + dso->long_name_len; + while (debuglink != filename && *debuglink != '/') debuglink--; if (*debuglink == '/') debuglink++; filename__read_debuglink(dso->long_name, debuglink, - size - (debuglink - file)); + size - (debuglink - filename)); } break; case DSO_BINARY_TYPE__BUILD_ID_CACHE: /* skip the locally configured cache if a symfs is given */ if (symbol_conf.symfs[0] || - (dso__build_id_filename(dso, file, size) == NULL)) + (dso__build_id_filename(dso, filename, size) == NULL)) ret = -1; break; case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: - snprintf(file, size, "%s/usr/lib/debug%s.debug", + snprintf(filename, size, "%s/usr/lib/debug%s.debug", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: - snprintf(file, size, "%s/usr/lib/debug%s", + snprintf(filename, size, "%s/usr/lib/debug%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: { - char *last_slash; + const char *last_slash; size_t len; size_t dir_size; @@ -75,14 +76,14 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, while (last_slash != dso->long_name && *last_slash != '/') last_slash--; - len = scnprintf(file, size, "%s", symbol_conf.symfs); + len = scnprintf(filename, size, "%s", symbol_conf.symfs); dir_size = last_slash - dso->long_name + 2; if (dir_size > (size - len)) { ret = -1; break; } - len += scnprintf(file + len, dir_size, "%s", dso->long_name); - len += scnprintf(file + len , size - len, ".debug%s", + len += scnprintf(filename + len, dir_size, "%s", dso->long_name); + len += scnprintf(filename + len , size - len, ".debug%s", last_slash); break; } @@ -96,7 +97,7 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); - snprintf(file, size, + snprintf(filename, size, "%s/usr/lib/debug/.build-id/%.2s/%s.debug", symbol_conf.symfs, build_id_hex, build_id_hex + 2); break; @@ -104,23 +105,23 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, case DSO_BINARY_TYPE__VMLINUX: case DSO_BINARY_TYPE__GUEST_VMLINUX: case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: - snprintf(file, size, "%s%s", + snprintf(filename, size, "%s%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__GUEST_KMODULE: - snprintf(file, size, "%s%s%s", symbol_conf.symfs, + snprintf(filename, size, "%s%s%s", symbol_conf.symfs, root_dir, dso->long_name); break; case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: - snprintf(file, size, "%s%s", symbol_conf.symfs, + snprintf(filename, size, "%s%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__KCORE: case DSO_BINARY_TYPE__GUEST_KCORE: - snprintf(file, size, "%s", dso->long_name); + snprintf(filename, size, "%s", dso->long_name); break; default: @@ -137,19 +138,18 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, static int open_dso(struct dso *dso, struct machine *machine) { - char *root_dir = (char *) ""; - char *name; int fd; + char *root_dir = (char *)""; + char *name = malloc(PATH_MAX); - name = malloc(PATH_MAX); if (!name) return -ENOMEM; if (machine) root_dir = machine->root_dir; - if (dso__binary_type_file(dso, dso->data_type, - root_dir, name, PATH_MAX)) { + if (dso__read_binary_type_filename(dso, dso->binary_type, + root_dir, name, PATH_MAX)) { free(name); return -EINVAL; } @@ -161,26 +161,26 @@ static int open_dso(struct dso *dso, struct machine *machine) int dso__data_fd(struct dso *dso, struct machine *machine) { - static enum dso_binary_type binary_type_data[] = { + enum dso_binary_type binary_type_data[] = { DSO_BINARY_TYPE__BUILD_ID_CACHE, DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__NOT_FOUND, }; int i = 0; - if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) + if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) return open_dso(dso, machine); do { int fd; - dso->data_type = binary_type_data[i++]; + dso->binary_type = binary_type_data[i++]; fd = open_dso(dso, machine); if (fd >= 0) return fd; - } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); + } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); return -EINVAL; } @@ -200,11 +200,10 @@ dso_cache__free(struct rb_root *root) } } -static struct dso_cache* -dso_cache__find(struct rb_root *root, u64 offset) +static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset) { - struct rb_node **p = &root->rb_node; - struct rb_node *parent = NULL; + struct rb_node * const *p = &root->rb_node; + const struct rb_node *parent = NULL; struct dso_cache *cache; while (*p != NULL) { @@ -379,32 +378,63 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, * processing we had no idea this was the kernel dso. */ if (dso != NULL) { - dso__set_short_name(dso, short_name); + dso__set_short_name(dso, short_name, false); dso->kernel = dso_type; } return dso; } -void dso__set_long_name(struct dso *dso, char *name) +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) { if (name == NULL) return; - dso->long_name = name; - dso->long_name_len = strlen(name); + + if (dso->long_name_allocated) + free((char *)dso->long_name); + + dso->long_name = name; + dso->long_name_len = strlen(name); + dso->long_name_allocated = name_allocated; } -void dso__set_short_name(struct dso *dso, const char *name) +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated) { if (name == NULL) return; - dso->short_name = name; - dso->short_name_len = strlen(name); + + if (dso->short_name_allocated) + free((char *)dso->short_name); + + dso->short_name = name; + dso->short_name_len = strlen(name); + dso->short_name_allocated = name_allocated; } static void dso__set_basename(struct dso *dso) { - dso__set_short_name(dso, basename(dso->long_name)); + /* + * basename() may modify path buffer, so we must pass + * a copy. + */ + char *base, *lname = strdup(dso->long_name); + + if (!lname) + return; + + /* + * basename() may return a pointer to internal + * storage which is reused in subsequent calls + * so copy the result. + */ + base = strdup(basename(lname)); + + free(lname); + + if (!base) + return; + + dso__set_short_name(dso, base, true); } int dso__name_len(const struct dso *dso) @@ -439,18 +469,19 @@ struct dso *dso__new(const char *name) if (dso != NULL) { int i; strcpy(dso->name, name); - dso__set_long_name(dso, dso->name); - dso__set_short_name(dso, dso->name); + dso__set_long_name(dso, dso->name, false); + dso__set_short_name(dso, dso->name, false); for (i = 0; i < MAP__NR_TYPES; ++i) dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->cache = RB_ROOT; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; - dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; + dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->loaded = 0; dso->rel = 0; dso->sorted_by_name = 0; dso->has_build_id = 0; dso->has_srcline = 1; + dso->a2l_fails = 1; dso->kernel = DSO_TYPE_USER; dso->needs_swap = DSO_SWAP__UNSET; INIT_LIST_HEAD(&dso->node); @@ -464,11 +495,20 @@ void dso__delete(struct dso *dso) int i; for (i = 0; i < MAP__NR_TYPES; ++i) symbols__delete(&dso->symbols[i]); - if (dso->sname_alloc) - free((char *)dso->short_name); - if (dso->lname_alloc) - free(dso->long_name); + + if (dso->short_name_allocated) { + zfree((char **)&dso->short_name); + dso->short_name_allocated = false; + } + + if (dso->long_name_allocated) { + zfree((char **)&dso->long_name); + dso->long_name_allocated = false; + } + dso_cache__free(&dso->cache); + dso__free_a2l(dso); + zfree(&dso->symsrc_filename); free(dso); } @@ -543,7 +583,7 @@ void dsos__add(struct list_head *head, struct dso *dso) list_add_tail(&dso->node, head); } -struct dso *dsos__find(struct list_head *head, const char *name, bool cmp_short) +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short) { struct dso *pos; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 9ac666abbe7e..cd7d6f078cdd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -77,23 +77,26 @@ struct dso { struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES]; struct rb_root cache; + void *a2l; + char *symsrc_filename; + unsigned int a2l_fails; enum dso_kernel_type kernel; enum dso_swap_type needs_swap; enum dso_binary_type symtab_type; - enum dso_binary_type data_type; + enum dso_binary_type binary_type; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1; u8 hit:1; u8 annotate_warned:1; - u8 sname_alloc:1; - u8 lname_alloc:1; + u8 short_name_allocated:1; + u8 long_name_allocated:1; u8 sorted_by_name; u8 loaded; u8 rel; u8 build_id[BUILD_ID_SIZE]; const char *short_name; - char *long_name; + const char *long_name; u16 long_name_len; u16 short_name_len; char name[0]; @@ -107,8 +110,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type) struct dso *dso__new(const char *name); void dso__delete(struct dso *dso); -void dso__set_short_name(struct dso *dso, const char *name); -void dso__set_long_name(struct dso *dso, char *name); +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated); int dso__name_len(const struct dso *dso); @@ -125,8 +128,8 @@ void dso__read_running_kernel_build_id(struct dso *dso, int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); char dso__symtab_origin(const struct dso *dso); -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, - char *root_dir, char *file, size_t size); +int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, + char *root_dir, char *filename, size_t size); int dso__data_fd(struct dso *dso, struct machine *machine); ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, @@ -140,7 +143,7 @@ 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(struct list_head *head, const char *name, +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short); struct dso *__dsos__findnew(struct list_head *head, const char *name); bool __dsos__read_build_ids(struct list_head *head, bool with_hits); @@ -156,14 +159,16 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); static inline bool dso__is_vmlinux(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__VMLINUX || - dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; + return dso->binary_type == DSO_BINARY_TYPE__VMLINUX || + dso->binary_type == DSO_BINARY_TYPE__GUEST_VMLINUX; } static inline bool dso__is_kcore(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__KCORE || - dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; + return dso->binary_type == DSO_BINARY_TYPE__KCORE || + dso->binary_type == DSO_BINARY_TYPE__GUEST_KCORE; } +void dso__free_a2l(struct dso *dso); + #endif /* __PERF_DSO */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb788c109fe6..1fc1c2f04772 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,6 +7,7 @@ #include "strlist.h" #include "thread.h" #include "thread_map.h" +#include "symbol/kallsyms.h" static const char *perf_event__names[] = { [0] = "TOTAL", @@ -105,8 +106,12 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, memset(&event->comm, 0, sizeof(event->comm)); - tgid = perf_event__get_comm_tgid(pid, event->comm.comm, - sizeof(event->comm.comm)); + if (machine__is_host(machine)) + tgid = perf_event__get_comm_tgid(pid, event->comm.comm, + sizeof(event->comm.comm)); + else + tgid = machine->pid; + if (tgid < 0) goto out; @@ -128,7 +133,11 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, goto out; } - snprintf(filename, sizeof(filename), "/proc/%d/task", pid); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(filename, sizeof(filename), "%s/proc/%d/task", + machine->root_dir, pid); tasks = opendir(filename); if (tasks == NULL) { @@ -166,18 +175,22 @@ out: return tgid; } -static int perf_event__synthesize_mmap_events(struct perf_tool *tool, - union perf_event *event, - pid_t pid, pid_t tgid, - perf_event__handler_t process, - struct machine *machine, - bool mmap_data) +int perf_event__synthesize_mmap_events(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine, + bool mmap_data) { char filename[PATH_MAX]; FILE *fp; int rc = 0; - snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(filename, sizeof(filename), "%s/proc/%d/maps", + machine->root_dir, pid); fp = fopen(filename, "r"); if (fp == NULL) { @@ -217,7 +230,10 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, /* * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c */ - event->header.misc = PERF_RECORD_MISC_USER; + if (machine__is_host(machine)) + event->header.misc = PERF_RECORD_MISC_USER; + else + event->header.misc = PERF_RECORD_MISC_GUEST_USER; if (prot[2] != 'x') { if (!mmap_data || prot[0] != 'r') @@ -386,6 +402,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, struct machine *machine, bool mmap_data) { DIR *proc; + char proc_path[PATH_MAX]; struct dirent dirent, *next; union perf_event *comm_event, *mmap_event; int err = -1; @@ -398,7 +415,12 @@ int perf_event__synthesize_threads(struct perf_tool *tool, if (mmap_event == NULL) goto out_free_comm; - proc = opendir("/proc"); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); + proc = opendir(proc_path); + if (proc == NULL) goto out_free_mmap; @@ -637,6 +659,7 @@ void thread__find_addr_map(struct thread *thread, struct map_groups *mg = &thread->mg; bool load_map = false; + al->machine = machine; al->thread = thread; al->addr = addr; al->cpumode = cpumode; @@ -657,15 +680,10 @@ void thread__find_addr_map(struct thread *thread, al->level = 'g'; mg = &machine->kmaps; load_map = true; + } else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { + al->level = 'u'; } else { - /* - * 'u' means guest os user space. - * TODO: We don't support guest user space. Might support late. - */ - if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) - al->level = 'u'; - else - al->level = 'H'; + al->level = 'H'; al->map = NULL; if ((cpumode == PERF_RECORD_MISC_GUEST_USER || @@ -732,8 +750,7 @@ int perf_event__preprocess_sample(const union perf_event *event, if (thread == NULL) return -1; - if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) + if (thread__is_filtered(thread)) goto out_filtered; dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 30fec9901e44..faf6e219be21 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -266,6 +266,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, const struct perf_sample *sample, bool swapped); +int perf_event__synthesize_mmap_events(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine, + bool mmap_data); + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index bbc746aa5716..40bd2c04df8a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@ * Released under the GPL v2. (and only v2, not any later version) */ #include "util.h" -#include +#include #include #include "cpumap.h" #include "thread_map.h" @@ -81,7 +81,7 @@ static void perf_evlist__update_id_pos(struct perf_evlist *evlist) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__calc_id_pos(evsel); perf_evlist__set_id_pos(evlist); @@ -91,7 +91,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist) { struct perf_evsel *pos, *n; - list_for_each_entry_safe(pos, n, &evlist->entries, node) { + evlist__for_each_safe(evlist, n, pos) { list_del_init(&pos->node); perf_evsel__delete(pos); } @@ -101,14 +101,18 @@ static void perf_evlist__purge(struct perf_evlist *evlist) void perf_evlist__exit(struct perf_evlist *evlist) { - free(evlist->mmap); - free(evlist->pollfd); - evlist->mmap = NULL; - evlist->pollfd = NULL; + zfree(&evlist->mmap); + zfree(&evlist->pollfd); } void perf_evlist__delete(struct perf_evlist *evlist) { + perf_evlist__munmap(evlist); + perf_evlist__close(evlist); + cpu_map__delete(evlist->cpus); + thread_map__delete(evlist->threads); + evlist->cpus = NULL; + evlist->threads = NULL; perf_evlist__purge(evlist); perf_evlist__exit(evlist); free(evlist); @@ -144,7 +148,7 @@ void __perf_evlist__set_leader(struct list_head *list) leader->nr_members = evsel->idx - leader->idx + 1; - list_for_each_entry(evsel, list, node) { + __evlist__for_each(list, evsel) { evsel->leader = leader; } } @@ -203,7 +207,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist, return 0; out_delete_partial_list: - list_for_each_entry_safe(evsel, n, &head, node) + __evlist__for_each_safe(&head, n, evsel) perf_evsel__delete(evsel); return -1; } @@ -224,7 +228,7 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->attr.type == PERF_TYPE_TRACEPOINT && (int)evsel->attr.config == id) return evsel; @@ -239,7 +243,7 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && (strcmp(evsel->name, name) == 0)) return evsel; @@ -269,7 +273,7 @@ void perf_evlist__disable(struct perf_evlist *evlist) int nr_threads = thread_map__nr(evlist->threads); for (cpu = 0; cpu < nr_cpus; cpu++) { - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (!perf_evsel__is_group_leader(pos) || !pos->fd) continue; for (thread = 0; thread < nr_threads; thread++) @@ -287,7 +291,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) int nr_threads = thread_map__nr(evlist->threads); for (cpu = 0; cpu < nr_cpus; cpu++) { - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (!perf_evsel__is_group_leader(pos) || !pos->fd) continue; for (thread = 0; thread < nr_threads; thread++) @@ -584,11 +588,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist) { int i; + if (evlist->mmap == NULL) + return; + for (i = 0; i < evlist->nr_mmaps; i++) __perf_evlist__munmap(evlist, i); - free(evlist->mmap); - evlist->mmap = NULL; + zfree(&evlist->mmap); } static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) @@ -624,7 +630,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { int fd = FD(evsel, cpu, thread); if (*output == -1) { @@ -732,11 +738,13 @@ static long parse_pages_arg(const char *str, unsigned long min, return -EINVAL; } - if ((pages == 0) && (min == 0)) { + if (pages == 0 && min == 0) { /* leave number of pages at 0 */ - } else if (pages < (1UL << 31) && !is_power_of_2(pages)) { + } else if (!is_power_of_2(pages)) { /* round pages up to next power of 2 */ - pages = next_pow2(pages); + pages = next_pow2_l(pages); + if (!pages) + return -EINVAL; pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", pages * page_size, pages); } @@ -754,7 +762,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, unsigned long max = UINT_MAX; long pages; - if (max < SIZE_MAX / page_size) + if (max > SIZE_MAX / page_size) max = SIZE_MAX / page_size; pages = parse_pages_arg(str, 1, max); @@ -798,7 +806,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, pr_debug("mmap size %zuB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && evsel->sample_id == NULL && perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) @@ -819,11 +827,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) if (evlist->threads == NULL) return -1; - if (target->force_per_cpu) - evlist->cpus = cpu_map__new(target->cpu_list); - else if (target__has_task(target)) - evlist->cpus = cpu_map__dummy_new(); - else if (!target__has_cpu(target) && !target->uses_mmap) + if (target__uses_dummy_map(target)) evlist->cpus = cpu_map__dummy_new(); else evlist->cpus = cpu_map__new(target->cpu_list); @@ -838,14 +842,6 @@ out_delete_threads: return -1; } -void perf_evlist__delete_maps(struct perf_evlist *evlist) -{ - cpu_map__delete(evlist->cpus); - thread_map__delete(evlist->threads); - evlist->cpus = NULL; - evlist->threads = NULL; -} - int perf_evlist__apply_filters(struct perf_evlist *evlist) { struct perf_evsel *evsel; @@ -853,7 +849,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist) const int ncpus = cpu_map__nr(evlist->cpus), nthreads = thread_map__nr(evlist->threads); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->filter == NULL) continue; @@ -872,7 +868,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) const int ncpus = cpu_map__nr(evlist->cpus), nthreads = thread_map__nr(evlist->threads); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); if (err) break; @@ -891,7 +887,7 @@ bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) if (evlist->id_pos < 0 || evlist->is_pos < 0) return false; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (pos->id_pos != evlist->id_pos || pos->is_pos != evlist->is_pos) return false; @@ -907,7 +903,7 @@ u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) if (evlist->combined_sample_type) return evlist->combined_sample_type; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) evlist->combined_sample_type |= evsel->attr.sample_type; return evlist->combined_sample_type; @@ -925,7 +921,7 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist) u64 read_format = first->attr.read_format; u64 sample_type = first->attr.sample_type; - list_for_each_entry_continue(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (read_format != pos->attr.read_format) return false; } @@ -982,7 +978,7 @@ bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) { struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; - list_for_each_entry_continue(pos, &evlist->entries, node) { + evlist__for_each_continue(evlist, pos) { if (first->attr.sample_id_all != pos->attr.sample_id_all) return false; } @@ -1008,7 +1004,7 @@ void perf_evlist__close(struct perf_evlist *evlist) int ncpus = cpu_map__nr(evlist->cpus); int nthreads = thread_map__nr(evlist->threads); - list_for_each_entry_reverse(evsel, &evlist->entries, node) + evlist__for_each_reverse(evlist, evsel) perf_evsel__close(evsel, ncpus, nthreads); } @@ -1019,7 +1015,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); if (err < 0) goto out_err; @@ -1034,7 +1030,7 @@ out_err: int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, const char *argv[], bool pipe_output, - bool want_signal) + void (*exec_error)(int signo, siginfo_t *info, void *ucontext)) { int child_ready_pipe[2], go_pipe[2]; char bf; @@ -1078,12 +1074,25 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar execvp(argv[0], (char **)argv); - perror(argv[0]); - if (want_signal) - kill(getppid(), SIGUSR1); + if (exec_error) { + union sigval val; + + val.sival_int = errno; + if (sigqueue(getppid(), SIGUSR1, val)) + perror(argv[0]); + } else + perror(argv[0]); exit(-1); } + if (exec_error) { + struct sigaction act = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = exec_error, + }; + sigaction(SIGUSR1, &act, NULL); + } + if (target__none(target)) evlist->threads->map[0] = evlist->workload.pid; @@ -1145,7 +1154,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) struct perf_evsel *evsel; size_t printed = 0; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", perf_evsel__name(evsel)); } @@ -1193,8 +1202,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, "Error:\t%s.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); - if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) - break; + value = perf_event_paranoid(); printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); @@ -1215,3 +1223,20 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, return 0; } + +void perf_evlist__to_front(struct perf_evlist *evlist, + struct perf_evsel *move_evsel) +{ + struct perf_evsel *evsel, *n; + LIST_HEAD(move); + + if (move_evsel == perf_evlist__first(evlist)) + return; + + evlist__for_each_safe(evlist, n, evsel) { + if (evsel->leader == move_evsel->leader) + list_move_tail(&evsel->node, &move); + } + + list_splice(&move, &evlist->entries); +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 649d6ea98a84..f5173cd63693 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -12,7 +12,7 @@ struct pollfd; struct thread_map; struct cpu_map; -struct perf_record_opts; +struct record_opts; #define PERF_EVLIST__HLIST_BITS 8 #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) @@ -97,14 +97,14 @@ void perf_evlist__close(struct perf_evlist *evlist); void perf_evlist__set_id_pos(struct perf_evlist *evlist); bool perf_can_sample_identifier(void); -void perf_evlist__config(struct perf_evlist *evlist, - struct perf_record_opts *opts); -int perf_record_opts__config(struct perf_record_opts *opts); +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); +int record_opts__config(struct record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, const char *argv[], bool pipe_output, - bool want_signal); + void (*exec_error)(int signo, siginfo_t *info, + void *ucontext)); int perf_evlist__start_workload(struct perf_evlist *evlist); int perf_evlist__parse_mmap_pages(const struct option *opt, @@ -135,7 +135,6 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, } int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target); -void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__apply_filters(struct perf_evlist *evlist); void __perf_evlist__set_leader(struct list_head *list); @@ -193,4 +192,74 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, pc->data_tail = tail; } +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str); +void perf_evlist__to_front(struct perf_evlist *evlist, + struct perf_evsel *move_evsel); + +/** + * __evlist__for_each - iterate thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each(list, evsel) \ + list_for_each_entry(evsel, list, node) + +/** + * evlist__for_each - iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each(evlist, evsel) \ + __evlist__for_each(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_continue - continue iteration thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_continue(list, evsel) \ + list_for_each_entry_continue(evsel, list, node) + +/** + * evlist__for_each_continue - continue iteration thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_continue(evlist, evsel) \ + __evlist__for_each_continue(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_reverse(list, evsel) \ + list_for_each_entry_reverse(evsel, list, node) + +/** + * evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_reverse(evlist, evsel) \ + __evlist__for_each_reverse(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_safe(list, tmp, evsel) \ + list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * evlist__for_each_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define evlist__for_each_safe(evlist, tmp, evsel) \ + __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) + #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 46dd4c2a41ce..22e18a26b7e6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -23,6 +23,7 @@ #include "target.h" #include "perf_regs.h" #include "debug.h" +#include "trace-event.h" static struct { bool sample_id_all; @@ -162,6 +163,8 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->idx = idx; evsel->attr = *attr; evsel->leader = evsel; + evsel->unit = ""; + evsel->scale = 1.0; INIT_LIST_HEAD(&evsel->node); hists__init(&evsel->hists); evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); @@ -178,47 +181,6 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) return evsel; } -struct event_format *event_format__new(const char *sys, const char *name) -{ - int fd, n; - char *filename; - void *bf = NULL, *nbf; - size_t size = 0, alloc_size = 0; - struct event_format *format = NULL; - - if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) - goto out; - - fd = open(filename, O_RDONLY); - if (fd < 0) - goto out_free_filename; - - do { - if (size == alloc_size) { - alloc_size += BUFSIZ; - nbf = realloc(bf, alloc_size); - if (nbf == NULL) - goto out_free_bf; - bf = nbf; - } - - n = read(fd, bf + size, alloc_size - size); - if (n < 0) - goto out_free_bf; - size += n; - } while (n > 0); - - pevent_parse_format(&format, bf, size, sys); - -out_free_bf: - free(bf); - close(fd); -out_free_filename: - free(filename); -out: - return format; -} - struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -233,7 +195,7 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) goto out_free; - evsel->tp_format = event_format__new(sys, name); + evsel->tp_format = trace_event__tp_format(sys, name); if (evsel->tp_format == NULL) goto out_free; @@ -246,7 +208,7 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int return evsel; out_free: - free(evsel->name); + zfree(&evsel->name); free(evsel); return NULL; } @@ -566,12 +528,12 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) * enable/disable events specifically, as there's no * initial traced exec call. */ -void perf_evsel__config(struct perf_evsel *evsel, - struct perf_record_opts *opts) +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 */ + bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; attr->inherit = !opts->no_inherit; @@ -645,7 +607,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } } - if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) + if (target__has_cpu(&opts->target)) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) @@ -653,7 +615,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (!perf_missing_features.sample_id_all && (opts->sample_time || !opts->no_inherit || - target__has_cpu(&opts->target) || opts->target.force_per_cpu)) + target__has_cpu(&opts->target) || per_cpu)) perf_evsel__set_sample_bit(evsel, TIME); if (opts->raw_samples) { @@ -665,7 +627,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (opts->sample_address) perf_evsel__set_sample_bit(evsel, DATA_SRC); - if (opts->no_delay) { + if (opts->no_buffering) { attr->watermark = 0; attr->wakeup_events = 1; } @@ -696,7 +658,8 @@ void perf_evsel__config(struct perf_evsel *evsel, * Setting enable_on_exec for independent events and * group leaders for traced executed by perf. */ - if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) + if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) && + !opts->initial_delay) attr->enable_on_exec = 1; } @@ -788,8 +751,7 @@ void perf_evsel__free_id(struct perf_evsel *evsel) { xyarray__delete(evsel->sample_id); evsel->sample_id = NULL; - free(evsel->id); - evsel->id = NULL; + zfree(&evsel->id); } void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) @@ -805,7 +767,7 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) void perf_evsel__free_counts(struct perf_evsel *evsel) { - free(evsel->counts); + zfree(&evsel->counts); } void perf_evsel__exit(struct perf_evsel *evsel) @@ -819,10 +781,10 @@ void perf_evsel__delete(struct perf_evsel *evsel) { perf_evsel__exit(evsel); close_cgroup(evsel->cgrp); - free(evsel->group_name); + zfree(&evsel->group_name); if (evsel->tp_format) pevent_free_format(evsel->tp_format); - free(evsel->name); + zfree(&evsel->name); free(evsel); } @@ -1998,8 +1960,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, evsel->attr.type = PERF_TYPE_SOFTWARE; evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; - free(evsel->name); - evsel->name = NULL; + zfree(&evsel->name); return true; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 1ea7c92e6e33..f1b325665aae 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -68,6 +68,8 @@ struct perf_evsel { u32 ids; struct hists hists; char *name; + double scale; + const char *unit; struct event_format *tp_format; union { void *priv; @@ -94,7 +96,7 @@ struct perf_evsel { struct cpu_map; struct thread_map; struct perf_evlist; -struct perf_record_opts; +struct record_opts; struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); @@ -118,7 +120,7 @@ void perf_evsel__exit(struct perf_evsel *evsel); void perf_evsel__delete(struct perf_evsel *evsel); void perf_evsel__config(struct perf_evsel *evsel, - struct perf_record_opts *opts); + struct record_opts *opts); int __perf_evsel__sample_size(u64 sample_type); void perf_evsel__calc_id_pos(struct perf_evsel *evsel); @@ -138,6 +140,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size); const char *perf_evsel__name(struct perf_evsel *evsel); + const char *perf_evsel__group_name(struct perf_evsel *evsel); int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1cd035708931..bb3e0ede6183 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -177,7 +177,7 @@ perf_header__set_cmdline(int argc, const char **argv) continue; \ else -static int write_buildid(char *name, size_t name_len, u8 *build_id, +static int write_buildid(const char *name, size_t name_len, u8 *build_id, pid_t pid, u16 misc, int fd) { int err; @@ -209,7 +209,7 @@ static int __dsos__write_buildid_table(struct list_head *head, dsos__for_each_with_build_id(pos, head) { int err; - char *name; + const char *name; size_t name_len; if (!pos->hit) @@ -387,7 +387,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine, { bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; bool is_vdso = is_vdso_map(dso->short_name); - char *name = dso->long_name; + const char *name = dso->long_name; char nm[PATH_MAX]; if (dso__is_kcore(dso)) { @@ -643,8 +643,7 @@ static int write_event_desc(int fd, struct perf_header *h __maybe_unused, if (ret < 0) return ret; - list_for_each_entry(evsel, &evlist->entries, node) { - + evlist__for_each(evlist, evsel) { ret = do_write(fd, &evsel->attr, sz); if (ret < 0) return ret; @@ -800,10 +799,10 @@ static void free_cpu_topo(struct cpu_topo *tp) return; for (i = 0 ; i < tp->core_sib; i++) - free(tp->core_siblings[i]); + zfree(&tp->core_siblings[i]); for (i = 0 ; i < tp->thread_sib; i++) - free(tp->thread_siblings[i]); + zfree(&tp->thread_siblings[i]); free(tp); } @@ -1092,7 +1091,7 @@ static int write_group_desc(int fd, struct perf_header *h __maybe_unused, if (ret < 0) return ret; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1) { const char *name = evsel->group_name ?: "{anon_group}"; @@ -1232,10 +1231,8 @@ static void free_event_desc(struct perf_evsel *events) return; for (evsel = events; evsel->attr.size; evsel++) { - if (evsel->name) - free(evsel->name); - if (evsel->id) - free(evsel->id); + zfree(&evsel->name); + zfree(&evsel->id); } free(events); @@ -1326,8 +1323,7 @@ read_event_desc(struct perf_header *ph, int fd) } } out: - if (buf) - free(buf); + free(buf); return events; error: if (events) @@ -1490,7 +1486,7 @@ static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, session = container_of(ph, struct perf_session, header); - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1) { fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", @@ -1709,7 +1705,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr; ret = readn(fd, &nr, sizeof(nr)); @@ -1753,7 +1749,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused, void *data __maybe_unused) { uint64_t mem; - size_t ret; + ssize_t ret; ret = readn(fd, &mem, sizeof(mem)); if (ret != sizeof(mem)) @@ -1771,7 +1767,7 @@ perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->idx == idx) return evsel; } @@ -1822,7 +1818,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused, struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; char *str; u32 nr, i; struct strbuf sb; @@ -1858,7 +1854,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr, i; char *str; struct strbuf sb; @@ -1914,7 +1910,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr, node, i; char *str; uint64_t mem_total, mem_free; @@ -1974,7 +1970,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; char *name; u32 pmu_num; u32 type; @@ -2074,7 +2070,7 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused, session->evlist->nr_groups = nr_groups; i = nr = 0; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (evsel->idx == (int) desc[i].leader_idx) { evsel->leader = evsel; /* {anon_group} is a dummy name */ @@ -2108,7 +2104,7 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused, ret = 0; out_free: for (i = 0; i < nr_groups; i++) - free(desc[i].name); + zfree(&desc[i].name); free(desc); return ret; @@ -2301,7 +2297,7 @@ int perf_session__write_header(struct perf_session *session, lseek(fd, sizeof(f_header), SEEK_SET); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { evsel->id_offset = lseek(fd, 0, SEEK_CUR); err = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); if (err < 0) { @@ -2312,7 +2308,7 @@ int perf_session__write_header(struct perf_session *session, attr_offset = lseek(fd, 0, SEEK_CUR); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { f_attr = (struct perf_file_attr){ .attr = evsel->attr, .ids = { @@ -2327,7 +2323,8 @@ int perf_session__write_header(struct perf_session *session, } } - header->data_offset = lseek(fd, 0, SEEK_CUR); + if (!header->data_offset) + header->data_offset = lseek(fd, 0, SEEK_CUR); header->feat_offset = header->data_offset + header->data_size; if (at_exit) { @@ -2534,7 +2531,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, int perf_file_header__read(struct perf_file_header *header, struct perf_header *ph, int fd) { - int ret; + ssize_t ret; lseek(fd, 0, SEEK_SET); @@ -2628,7 +2625,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, struct perf_header *ph, int fd, bool repipe) { - int ret; + ssize_t ret; ret = readn(fd, header, sizeof(*header)); if (ret <= 0) @@ -2669,7 +2666,7 @@ static int read_attr(int fd, struct perf_header *ph, struct perf_event_attr *attr = &f_attr->attr; size_t sz, left; size_t our_sz = sizeof(f_attr->attr); - int ret; + ssize_t ret; memset(f_attr, 0, sizeof(*f_attr)); @@ -2744,7 +2741,7 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, { struct perf_evsel *pos; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (pos->attr.type == PERF_TYPE_TRACEPOINT && perf_evsel__prepare_tracepoint_event(pos, pevent)) return -1; @@ -2834,11 +2831,11 @@ int perf_session__read_header(struct perf_session *session) symbol_conf.nr_events = nr_attrs; - perf_header__process_sections(header, fd, &session->pevent, + perf_header__process_sections(header, fd, &session->tevent, perf_file_section__process); if (perf_evlist__prepare_tracepoint_events(session->evlist, - session->pevent)) + session->tevent.pevent)) goto out_delete_evlist; return 0; @@ -2892,7 +2889,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_evsel *evsel; int err = 0; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, evsel->id, process); if (err) { @@ -3003,7 +3000,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, lseek(fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(fd, &session->pevent, + size_read = trace_report(fd, &session->tevent, session->repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; @@ -3025,7 +3022,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, } perf_evlist__prepare_tracepoint_events(session->evlist, - session->pevent); + session->tevent.pevent); return size_read + padding; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 307c9aed972e..a2d047bdf4ef 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -77,16 +77,16 @@ struct perf_session_env { unsigned long long total_mem; int nr_cmdline; - char *cmdline; int nr_sibling_cores; - char *sibling_cores; int nr_sibling_threads; - char *sibling_threads; int nr_numa_nodes; - char *numa_nodes; int nr_pmu_mappings; - char *pmu_mappings; int nr_groups; + char *cmdline; + char *sibling_cores; + char *sibling_threads; + char *numa_nodes; + char *pmu_mappings; }; struct perf_header { diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8b1f6e891b8a..86c37c472263 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -22,8 +22,8 @@ static void clean_cmdnames(struct cmdnames *cmds) unsigned int i; for (i = 0; i < cmds->cnt; ++i) - free(cmds->names[i]); - free(cmds->names); + zfree(&cmds->names[i]); + zfree(&cmds->names); cmds->cnt = 0; cmds->alloc = 0; } @@ -263,9 +263,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) for (i = 0; i < old->cnt; i++) cmds->names[cmds->cnt++] = old->names[i]; - free(old->names); + zfree(&old->names); old->cnt = 0; - old->names = NULL; } const char *help_unknown_cmd(const char *cmd) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 822903eaa201..e4e6249b87d4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,4 +1,3 @@ -#include "annotate.h" #include "util.h" #include "build-id.h" #include "hist.h" @@ -182,21 +181,21 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows) } } -static void hist_entry__add_cpumode_period(struct hist_entry *he, - unsigned int cpumode, u64 period) +static void he_stat__add_cpumode_period(struct he_stat *he_stat, + unsigned int cpumode, u64 period) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->stat.period_sys += period; + he_stat->period_sys += period; break; case PERF_RECORD_MISC_USER: - he->stat.period_us += period; + he_stat->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->stat.period_guest_sys += period; + he_stat->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - he->stat.period_guest_us += period; + he_stat->period_guest_us += period; break; default: break; @@ -223,10 +222,10 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) dest->weight += src->weight; } -static void hist_entry__decay(struct hist_entry *he) +static void he_stat__decay(struct he_stat *he_stat) { - he->stat.period = (he->stat.period * 7) / 8; - he->stat.nr_events = (he->stat.nr_events * 7) / 8; + he_stat->period = (he_stat->period * 7) / 8; + he_stat->nr_events = (he_stat->nr_events * 7) / 8; /* XXX need decay for weight too? */ } @@ -237,7 +236,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) if (prev_period == 0) return true; - hist_entry__decay(he); + he_stat__decay(&he->stat); if (!he->filtered) hists->stats.total_period -= prev_period - he->stat.period; @@ -342,15 +341,15 @@ static u8 symbol__parent_filter(const struct symbol *parent) } static struct hist_entry *add_hist_entry(struct hists *hists, - struct hist_entry *entry, - struct addr_location *al, - u64 period, - u64 weight) + struct hist_entry *entry, + struct addr_location *al) { struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; int64_t cmp; + u64 period = entry->stat.period; + u64 weight = entry->stat.weight; p = &hists->entries_in->rb_node; @@ -373,7 +372,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, * This mem info was allocated from machine__resolve_mem * and will not be used anymore. */ - free(entry->mem_info); + zfree(&entry->mem_info); /* If the map of an existing hist_entry has * become out-of-date due to an exec() or @@ -403,7 +402,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: - hist_entry__add_cpumode_period(he, al->cpumode, period); + he_stat__add_cpumode_period(&he->stat, al->cpumode, period); return he; } @@ -437,7 +436,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .transaction = transaction, }; - return add_hist_entry(hists, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al); } int64_t @@ -476,8 +475,8 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) void hist_entry__free(struct hist_entry *he) { - free(he->branch_info); - free(he->mem_info); + zfree(&he->branch_info); + zfree(&he->mem_info); free_srcline(he->srcline); free(he); } @@ -807,16 +806,6 @@ void hists__filter_by_symbol(struct hists *hists) } } -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) -{ - return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); -} - -int hist_entry__annotate(struct hist_entry *he, size_t privsize) -{ - return symbol__annotate(he->ms.sym, he->ms.map, privsize); -} - void events_stats__inc(struct events_stats *stats, u32 type) { ++stats->nr_events[0]; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index b621347a1585..a59743fa3ef7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -111,9 +111,6 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *he, size_t privsize); - void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); void hists__filter_by_symbol(struct hists *hists); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 84cdb072ac83..ded74590b92f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -9,6 +9,7 @@ #include "strlist.h" #include "thread.h" #include +#include #include "unwind.h" int machine__init(struct machine *machine, const char *root_dir, pid_t pid) @@ -26,6 +27,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) machine->pid = pid; machine->symbol_filter = NULL; + machine->id_hdr_size = 0; machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) @@ -101,8 +103,7 @@ void machine__exit(struct machine *machine) map_groups__exit(&machine->kmaps); dsos__delete(&machine->user_dsos); dsos__delete(&machine->kernel_dsos); - free(machine->root_dir); - machine->root_dir = NULL; + zfree(&machine->root_dir); } void machine__delete(struct machine *machine) @@ -502,15 +503,11 @@ static u64 machine__get_kernel_start_addr(struct machine *machine) char path[PATH_MAX]; struct process_args args; - if (machine__is_host(machine)) { - filename = "/proc/kallsyms"; - } else { - if (machine__is_default_guest(machine)) - filename = (char *)symbol_conf.default_guest_kallsyms; - else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - filename = path; - } + if (machine__is_default_guest(machine)) + filename = (char *)symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", machine->root_dir); + filename = path; } if (symbol__restricted_filename(filename, "/proc/kallsyms")) @@ -565,11 +562,10 @@ void machine__destroy_kernel_maps(struct machine *machine) * on one of them. */ if (type == MAP__FUNCTION) { - free((char *)kmap->ref_reloc_sym->name); - kmap->ref_reloc_sym->name = NULL; - free(kmap->ref_reloc_sym); - } - kmap->ref_reloc_sym = NULL; + zfree((char **)&kmap->ref_reloc_sym->name); + zfree(&kmap->ref_reloc_sym); + } else + kmap->ref_reloc_sym = NULL; } map__delete(machine->vmlinux_maps[type]); @@ -767,8 +763,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, ret = -1; goto out; } - dso__set_long_name(map->dso, long_name); - map->dso->lname_alloc = 1; + dso__set_long_name(map->dso, long_name, true); dso__kernel_module_get_build_id(map->dso, ""); } } @@ -939,8 +934,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, if (name == NULL) goto out_problem; - map->dso->short_name = name; - map->dso->sname_alloc = 1; + dso__set_short_name(map->dso, name, true); map->end = map->start + event->mmap.len; } else if (is_kernel_mmap) { const char *symbol_name = (event->mmap.filename + @@ -1320,8 +1314,6 @@ static int machine__resolve_callchain_sample(struct machine *machine, *root_al = al; callchain_cursor_reset(&callchain_cursor); } - if (!symbol_conf.use_callchain) - break; } err = callchain_cursor_append(&callchain_cursor, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ef5bc913ca7a..9b9bd719aa19 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -11,6 +11,7 @@ #include "strlist.h" #include "vdso.h" #include "build-id.h" +#include "util.h" #include const char *map_type__name[MAP__NR_TYPES] = { @@ -252,6 +253,22 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) return fprintf(fp, "%s", dsoname); } +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, + FILE *fp) +{ + char *srcline; + int ret = 0; + + if (map && map->dso) { + srcline = get_srcline(map->dso, + map__rip_2objdump(map, addr)); + if (srcline != SRCLINE_UNKNOWN) + ret = fprintf(fp, "%s%s", prefix, srcline); + free_srcline(srcline); + } + return ret; +} + /** * map__rip_2objdump - convert symbol start address to objdump address. * @map: memory map diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index e4e259c3ba16..18068c6b71c1 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -103,6 +103,8 @@ struct map *map__clone(struct map *map); int map__overlap(struct map *l, struct map *r); size_t map__fprintf(struct map *map, FILE *fp); size_t map__fprintf_dsoname(struct map *map, FILE *fp); +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, + FILE *fp); int map__load(struct map *map, symbol_filter_t filter); struct symbol *map__find_symbol(struct map *map, diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6de6f89c2a61..a7f1b6a91fdd 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@ #include "symbol.h" #include "cache.h" #include "header.h" -#include +#include #include "parse-events-bison.h" #define YY_EXTRA_TYPE int #include "parse-events-flex.h" @@ -204,7 +204,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) } path->name = malloc(MAX_EVENT_LENGTH); if (!path->name) { - free(path->system); + zfree(&path->system); free(path); return NULL; } @@ -236,8 +236,8 @@ struct tracepoint_path *tracepoint_name_to_path(const char *name) path->name = strdup(str+1); if (path->system == NULL || path->name == NULL) { - free(path->system); - free(path->name); + zfree(&path->system); + zfree(&path->name); free(path); path = NULL; } @@ -269,9 +269,10 @@ const char *event_type(int type) -static int __add_event(struct list_head *list, int *idx, - struct perf_event_attr *attr, - char *name, struct cpu_map *cpus) +static struct perf_evsel * +__add_event(struct list_head *list, int *idx, + struct perf_event_attr *attr, + char *name, struct cpu_map *cpus) { struct perf_evsel *evsel; @@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx, evsel = perf_evsel__new_idx(attr, (*idx)++); if (!evsel) - return -ENOMEM; + return NULL; evsel->cpus = cpus; if (name) evsel->name = strdup(name); list_add_tail(&evsel->node, list); - return 0; + return evsel; } static int add_event(struct list_head *list, int *idx, struct perf_event_attr *attr, char *name) { - return __add_event(list, idx, attr, name, NULL); + return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM; } static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) @@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx, { struct perf_event_attr attr; struct perf_pmu *pmu; + struct perf_evsel *evsel; + char *unit; + double scale; pmu = perf_pmu__find(name); if (!pmu) @@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx, memset(&attr, 0, sizeof(attr)); - if (perf_pmu__check_alias(pmu, head_config)) + if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) return -EINVAL; /* @@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx, if (perf_pmu__config(pmu, &attr, head_config)) return -EINVAL; - return __add_event(list, idx, &attr, pmu_event_name(head_config), - pmu->cpus); + evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), + pmu->cpus); + if (evsel) { + evsel->unit = unit; + evsel->scale = scale; + } + + return evsel ? 0 : -ENOMEM; } int parse_events__modifier_group(struct list_head *list, @@ -810,8 +820,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) if (!add && get_event_modifier(&mod, str, NULL)) return -EINVAL; - list_for_each_entry(evsel, list, node) { - + __evlist__for_each(list, evsel) { if (add && get_event_modifier(&mod, str, evsel)) return -EINVAL; @@ -835,7 +844,7 @@ int parse_events_name(struct list_head *list, char *name) { struct perf_evsel *evsel; - list_for_each_entry(evsel, list, node) { + __evlist__for_each(list, evsel) { if (!evsel->name) evsel->name = strdup(name); } @@ -907,7 +916,7 @@ int parse_events_terms(struct list_head *terms, const char *str) ret = parse_events__scanner(str, &data, PE_START_TERMS); if (!ret) { list_splice(data.terms, terms); - free(data.terms); + zfree(&data.terms); return 0; } diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 31f404a032a9..d22e3f8017dc 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: *(bool *)opt->value = unset ? false : true; + if (opt->set) + *(bool *)opt->set = true; return 0; case OPTION_INCR: @@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, return 0; } if (!rest) { + if (!prefixcmp(options->long_name, "no-")) { + /* + * The long name itself starts with "no-", so + * accept the option without "no-" so that users + * do not have to enter "no-no-" to get the + * negation. + */ + rest = skip_prefix(arg, options->long_name + 3); + if (rest) { + flags |= OPT_UNSET; + goto match; + } + /* Abbreviated case */ + if (!prefixcmp(options->long_name + 3, arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + } /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: @@ -259,6 +279,7 @@ is_abbreviated: if (!rest) continue; } +match: if (*rest) { if (*rest != '=') continue; diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b0241e28eaf7..cbf0149cf221 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. + * + * `set`:: + * whether an option was set by the user */ struct option { enum parse_opt_type type; @@ -94,6 +97,7 @@ struct option { int flags; parse_opt_cb *callback; intptr_t defval; + bool *set; }; #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -103,6 +107,10 @@ struct option { #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ + { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, bool *), .help = (h), \ + .set = check_vtype(os, bool *)} #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index c232d8dd410b..d9cab4d27192 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1,19 +1,23 @@ #include #include -#include #include #include #include #include "fs.h" +#include #include "util.h" #include "pmu.h" #include "parse-events.h" #include "cpumap.h" +#define UNIT_MAX_LEN 31 /* max length for event unit name */ + struct perf_pmu_alias { char *name; struct list_head terms; struct list_head list; + char unit[UNIT_MAX_LEN+1]; + double scale; }; struct perf_pmu_format { @@ -94,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format) return 0; } -static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ + struct stat st; + ssize_t sret; + char scale[128]; + int fd, ret = -1; + char path[PATH_MAX]; + char *lc; + + snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + if (fstat(fd, &st) < 0) + goto error; + + sret = read(fd, scale, sizeof(scale)-1); + if (sret < 0) + goto error; + + scale[sret] = '\0'; + /* + * save current locale + */ + lc = setlocale(LC_NUMERIC, NULL); + + /* + * force to C locale to ensure kernel + * scale string is converted correctly. + * kernel uses default C locale. + */ + setlocale(LC_NUMERIC, "C"); + + alias->scale = strtod(scale, NULL); + + /* restore locale */ + setlocale(LC_NUMERIC, lc); + + ret = 0; +error: + close(fd); + return ret; +} + +static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) +{ + char path[PATH_MAX]; + ssize_t sret; + int fd; + + snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + sret = read(fd, alias->unit, UNIT_MAX_LEN); + if (sret < 0) + goto error; + + close(fd); + + alias->unit[sret] = '\0'; + + return 0; +error: + close(fd); + alias->unit[0] = '\0'; + return -1; +} + +static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) { struct perf_pmu_alias *alias; char buf[256]; @@ -110,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) return -ENOMEM; INIT_LIST_HEAD(&alias->terms); + alias->scale = 1.0; + alias->unit[0] = '\0'; + ret = parse_events_terms(&alias->terms, buf); if (ret) { free(alias); @@ -117,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) } alias->name = strdup(name); + /* + * load unit name and scale if available + */ + perf_pmu__parse_unit(alias, dir, name); + perf_pmu__parse_scale(alias, dir, name); + list_add_tail(&alias->list, list); + return 0; } @@ -129,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) { struct dirent *evt_ent; DIR *event_dir; + size_t len; int ret = 0; event_dir = opendir(dir); @@ -143,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) if (!strcmp(name, ".") || !strcmp(name, "..")) continue; + /* + * skip .unit and .scale info files + * parsed in perf_pmu__new_alias() + */ + len = strlen(name); + if (len > 5 && !strcmp(name + len - 5, ".unit")) + continue; + if (len > 6 && !strcmp(name + len - 6, ".scale")) + continue; + snprintf(path, PATH_MAX, "%s/%s", dir, name); ret = -EINVAL; file = fopen(path, "r"); if (!file) break; - ret = perf_pmu__new_alias(head, name, file); + + ret = perf_pmu__new_alias(head, dir, name, file); fclose(file); } @@ -406,7 +505,7 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) /* * Setup one of config[12] attr members based on the - * user input data - temr parameter. + * user input data - term parameter. */ static int pmu_config_term(struct list_head *formats, struct perf_event_attr *attr, @@ -508,16 +607,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, return NULL; } + +static int check_unit_scale(struct perf_pmu_alias *alias, + char **unit, double *scale) +{ + /* + * Only one term in event definition can + * define unit and scale, fail if there's + * more than one. + */ + if ((*unit && alias->unit) || + (*scale && alias->scale)) + return -EINVAL; + + if (alias->unit) + *unit = alias->unit; + + if (alias->scale) + *scale = alias->scale; + + return 0; +} + /* * Find alias in the terms list and replace it with the terms * defined for the alias */ -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, + char **unit, double *scale) { struct parse_events_term *term, *h; struct perf_pmu_alias *alias; int ret; + *unit = NULL; + *scale = 0; + list_for_each_entry_safe(term, h, head_terms, list) { alias = pmu_find_alias(pmu, term); if (!alias) @@ -525,6 +650,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) ret = pmu_alias_terms(alias, &term->list); if (ret) return ret; + + ret = check_unit_scale(alias, unit, scale); + if (ret) + return ret; + list_del(&term->list); free(term); } @@ -625,7 +755,7 @@ void print_pmu_events(const char *event_glob, bool name_only) continue; } printf(" %-50s [Kernel PMU event]\n", aliases[j]); - free(aliases[j]); + zfree(&aliases[j]); printed++; } if (printed) diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 1179b26f244a..9183380e2038 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, int perf_pmu__config_terms(struct list_head *formats, struct perf_event_attr *attr, struct list_head *head_terms); -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, + char **unit, double *scale); struct list_head *perf_pmu__alias(struct perf_pmu *pmu, struct list_head *head_terms); int perf_pmu_wrap(void); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9c6989ca2bea..a8a9b6cd93a8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,7 +40,7 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include +#include #include "trace-event.h" /* For __maybe_unused */ #include "probe-event.h" #include "probe-finder.h" @@ -72,6 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec); +static void clear_probe_trace_event(struct probe_trace_event *tev); static struct machine machine; /* Initialize symbol maps and path of vmlinux/modules */ @@ -154,7 +155,7 @@ static struct dso *kernel_get_module_dso(const char *module) vmlinux_name = symbol_conf.vmlinux_name; if (vmlinux_name) { - if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) + if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) return NULL; } else { if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { @@ -186,6 +187,37 @@ static int init_user_exec(void) return ret; } +static int convert_exec_to_group(const char *exec, char **result) +{ + char *ptr1, *ptr2, *exec_copy; + char buf[64]; + int ret; + + exec_copy = strdup(exec); + if (!exec_copy) + return -ENOMEM; + + ptr1 = basename(exec_copy); + if (!ptr1) { + ret = -EINVAL; + goto out; + } + + ptr2 = strpbrk(ptr1, "-._"); + if (ptr2) + *ptr2 = '\0'; + ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); + if (ret < 0) + goto out; + + *result = strdup(buf); + ret = *result ? 0 : -ENOMEM; + +out: + free(exec_copy); + return ret; +} + static int convert_to_perf_probe_point(struct probe_trace_point *tp, struct perf_probe_point *pp) { @@ -261,6 +293,68 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, return 0; } +static int get_text_start_address(const char *exec, unsigned long *address) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + int fd, ret = -ENOENT; + + fd = open(exec, O_RDONLY); + if (fd < 0) + return -errno; + + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf == NULL) + return -EINVAL; + + if (gelf_getehdr(elf, &ehdr) == NULL) + goto out; + + if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) + goto out; + + *address = shdr.sh_addr - shdr.sh_offset; + ret = 0; +out: + elf_end(elf); + return ret; +} + +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, + int ntevs, const char *exec) +{ + int i, ret = 0; + unsigned long offset, stext = 0; + char buf[32]; + + if (!exec) + return 0; + + ret = get_text_start_address(exec, &stext); + if (ret < 0) + return ret; + + for (i = 0; i < ntevs && ret >= 0; i++) { + offset = tevs[i].point.address - stext; + offset += tevs[i].point.offset; + tevs[i].point.offset = 0; + zfree(&tevs[i].point.symbol); + ret = e_snprintf(buf, 32, "0x%lx", offset); + if (ret < 0) + break; + tevs[i].point.module = strdup(exec); + tevs[i].point.symbol = strdup(buf); + if (!tevs[i].point.symbol || !tevs[i].point.module) { + ret = -ENOMEM; + break; + } + tevs[i].uprobes = true; + } + + return ret; +} + static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, int ntevs, const char *module) { @@ -290,12 +384,18 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, } } - if (tmp) - free(tmp); - + free(tmp); return ret; } +static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) +{ + int i; + + for (i = 0; i < ntevs; i++) + clear_probe_trace_event(tevs + i); +} + /* Try to find perf_probe_event with debuginfo */ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, @@ -305,15 +405,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct debuginfo *dinfo; int ntevs, ret = 0; - if (pev->uprobes) { - if (need_dwarf) { - pr_warning("Debuginfo-analysis is not yet supported" - " with -x/--exec option.\n"); - return -ENOSYS; - } - return convert_name_to_addr(pev, target); - } - dinfo = open_debuginfo(target); if (!dinfo) { @@ -332,9 +423,18 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, if (ntevs > 0) { /* Succeeded to find trace events */ pr_debug("find %d probe_trace_events.\n", ntevs); - if (target) - ret = add_module_to_probe_trace_events(*tevs, ntevs, - target); + if (target) { + if (pev->uprobes) + ret = add_exec_to_probe_trace_events(*tevs, + ntevs, target); + else + ret = add_module_to_probe_trace_events(*tevs, + ntevs, target); + } + if (ret < 0) { + clear_probe_trace_events(*tevs, ntevs); + zfree(tevs); + } return ret < 0 ? ret : ntevs; } @@ -401,15 +501,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir, case EFAULT: raw_path = strchr(++raw_path, '/'); if (!raw_path) { - free(*new_path); - *new_path = NULL; + zfree(new_path); return -ENOENT; } continue; default: - free(*new_path); - *new_path = NULL; + zfree(new_path); return -errno; } } @@ -580,7 +678,7 @@ static int show_available_vars_at(struct debuginfo *dinfo, */ fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, vl->point.offset); - free(vl->point.symbol); + zfree(&vl->point.symbol); nvars = 0; if (vl->vars) { strlist__for_each(node, vl->vars) { @@ -647,16 +745,14 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs __maybe_unused, - int max_tevs __maybe_unused, const char *target) + int max_tevs __maybe_unused, + const char *target __maybe_unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); return -ENOSYS; } - if (pev->uprobes) - return convert_name_to_addr(pev, target); - return 0; } @@ -678,6 +774,28 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused, } #endif +void line_range__clear(struct line_range *lr) +{ + struct line_node *ln; + + free(lr->function); + free(lr->file); + free(lr->path); + free(lr->comp_dir); + while (!list_empty(&lr->line_list)) { + ln = list_first_entry(&lr->line_list, struct line_node, list); + list_del(&ln->list); + free(ln); + } + memset(lr, 0, sizeof(*lr)); +} + +void line_range__init(struct line_range *lr) +{ + memset(lr, 0, sizeof(*lr)); + INIT_LIST_HEAD(&lr->line_list); +} + static int parse_line_num(char **ptr, int *val, const char *what) { const char *start = *ptr; @@ -1278,8 +1396,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) error: pr_debug("Failed to synthesize perf probe point: %s\n", strerror(-ret)); - if (buf) - free(buf); + free(buf); return NULL; } @@ -1480,34 +1597,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev) struct perf_probe_arg_field *field, *next; int i; - if (pev->event) - free(pev->event); - if (pev->group) - free(pev->group); - if (pp->file) - free(pp->file); - if (pp->function) - free(pp->function); - if (pp->lazy_line) - free(pp->lazy_line); + free(pev->event); + free(pev->group); + free(pp->file); + free(pp->function); + free(pp->lazy_line); + for (i = 0; i < pev->nargs; i++) { - if (pev->args[i].name) - free(pev->args[i].name); - if (pev->args[i].var) - free(pev->args[i].var); - if (pev->args[i].type) - free(pev->args[i].type); + free(pev->args[i].name); + free(pev->args[i].var); + free(pev->args[i].type); field = pev->args[i].field; while (field) { next = field->next; - if (field->name) - free(field->name); + zfree(&field->name); free(field); field = next; } } - if (pev->args) - free(pev->args); + free(pev->args); memset(pev, 0, sizeof(*pev)); } @@ -1516,21 +1624,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) struct probe_trace_arg_ref *ref, *next; int i; - if (tev->event) - free(tev->event); - if (tev->group) - free(tev->group); - if (tev->point.symbol) - free(tev->point.symbol); - if (tev->point.module) - free(tev->point.module); + free(tev->event); + free(tev->group); + free(tev->point.symbol); + free(tev->point.module); for (i = 0; i < tev->nargs; i++) { - if (tev->args[i].name) - free(tev->args[i].name); - if (tev->args[i].value) - free(tev->args[i].value); - if (tev->args[i].type) - free(tev->args[i].type); + free(tev->args[i].name); + free(tev->args[i].value); + free(tev->args[i].type); ref = tev->args[i].ref; while (ref) { next = ref->next; @@ -1538,8 +1639,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) ref = next; } } - if (tev->args) - free(tev->args); + free(tev->args); memset(tev, 0, sizeof(*tev)); } @@ -1913,14 +2013,29 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, int max_tevs, const char *target) { struct symbol *sym; - int ret = 0, i; + int ret, i; struct probe_trace_event *tev; + if (pev->uprobes && !pev->group) { + /* Replace group name if not given */ + ret = convert_exec_to_group(target, &pev->group); + if (ret != 0) { + pr_warning("Failed to make a group name.\n"); + return ret; + } + } + /* Convert perf_probe_event with debuginfo */ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); if (ret != 0) return ret; /* Found in debuginfo or got an error */ + if (pev->uprobes) { + ret = convert_name_to_addr(pev, target); + if (ret < 0) + return ret; + } + /* Allocate trace event buffer */ tev = *tevs = zalloc(sizeof(struct probe_trace_event)); if (tev == NULL) @@ -2056,7 +2171,7 @@ end: for (i = 0; i < npevs; i++) { for (j = 0; j < pkgs[i].ntevs; j++) clear_probe_trace_event(&pkgs[i].tevs[j]); - free(pkgs[i].tevs); + zfree(&pkgs[i].tevs); } free(pkgs); @@ -2281,7 +2396,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) struct perf_probe_point *pp = &pev->point; struct symbol *sym; struct map *map = NULL; - char *function = NULL, *name = NULL; + char *function = NULL; int ret = -EINVAL; unsigned long long vaddr = 0; @@ -2297,12 +2412,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) goto out; } - name = realpath(exec, NULL); - if (!name) { - pr_warning("Cannot find realpath for %s.\n", exec); - goto out; - } - map = dso__new_map(name); + map = dso__new_map(exec); if (!map) { pr_warning("Cannot find appropriate DSO for %s.\n", exec); goto out; @@ -2367,7 +2477,5 @@ out: } if (function) free(function); - if (name) - free(name); return ret; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f9f3de8b4220..fcaf7273e85a 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -12,6 +12,7 @@ struct probe_trace_point { char *symbol; /* Base symbol */ char *module; /* Module name */ unsigned long offset; /* Offset from symbol */ + unsigned long address; /* Actual address of the trace point */ bool retprobe; /* Return probe flag */ }; @@ -119,6 +120,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev); /* Command string to line-range */ extern int parse_line_range_desc(const char *cmd, struct line_range *lr); +/* Release line range members */ +extern void line_range__clear(struct line_range *lr); + +/* Initialize line range */ +extern void line_range__init(struct line_range *lr); + /* Internal use: Return kernel/module path */ extern const char *kernel_get_module_path(const char *module); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ffb657ffd327..061edb162b5b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -226,10 +226,8 @@ struct debuginfo *debuginfo__new(const char *path) if (!dbg) return NULL; - if (debuginfo__init_offline_dwarf(dbg, path) < 0) { - free(dbg); - dbg = NULL; - } + if (debuginfo__init_offline_dwarf(dbg, path) < 0) + zfree(&dbg); return dbg; } @@ -241,10 +239,8 @@ struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) if (!dbg) return NULL; - if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) { - free(dbg); - dbg = NULL; - } + if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) + zfree(&dbg); return dbg; } @@ -729,6 +725,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, return -ENOENT; } tp->offset = (unsigned long)(paddr - sym.st_value); + tp->address = (unsigned long)paddr; tp->symbol = strdup(symbol); if (!tp->symbol) return -ENOMEM; @@ -1301,8 +1298,7 @@ int debuginfo__find_trace_events(struct debuginfo *dbg, ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { - free(*tevs); - *tevs = NULL; + zfree(tevs); return ret; } @@ -1413,13 +1409,10 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg, if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { - if (af.vls[af.nvls].point.symbol) - free(af.vls[af.nvls].point.symbol); - if (af.vls[af.nvls].vars) - strlist__delete(af.vls[af.nvls].vars); + zfree(&af.vls[af.nvls].point.symbol); + strlist__delete(af.vls[af.nvls].vars); } - free(af.vls); - *vls = NULL; + zfree(vls); return ret; } @@ -1523,10 +1516,7 @@ post: if (fname) { ppt->file = strdup(fname); if (ppt->file == NULL) { - if (ppt->function) { - free(ppt->function); - ppt->function = NULL; - } + zfree(&ppt->function); ret = -ENOMEM; goto end; } @@ -1580,8 +1570,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) else ret = 0; /* Lines are not found */ else { - free(lf->lr->path); - lf->lr->path = NULL; + zfree(&lf->lr->path); } return ret; } diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 239036fb2b2c..595bfc73d2ed 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -18,4 +18,5 @@ util/cgroup.c util/rblist.c util/strlist.c util/fs.c +util/trace-event.c ../../lib/rbtree.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4bf8ace7f511..122669c18ff4 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -908,9 +908,10 @@ static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) if (i >= pevlist->evlist.nr_entries) return NULL; - list_for_each_entry(pos, &pevlist->evlist.entries, node) + evlist__for_each(&pevlist->evlist, pos) { if (i-- == 0) break; + } return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); } diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index c8845b107f60..373762501dad 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -74,8 +74,7 @@ bool perf_can_sample_identifier(void) return perf_probe_api(perf_probe_sample_identifier); } -void perf_evlist__config(struct perf_evlist *evlist, - struct perf_record_opts *opts) +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) { struct perf_evsel *evsel; bool use_sample_identifier = false; @@ -90,19 +89,19 @@ void perf_evlist__config(struct perf_evlist *evlist, if (evlist->cpus->map[0] < 0) opts->no_inherit = true; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__config(evsel, opts); if (evlist->nr_entries > 1) { struct perf_evsel *first = perf_evlist__first(evlist); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->attr.sample_type == first->attr.sample_type) continue; use_sample_identifier = perf_can_sample_identifier(); break; } - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__set_sample_id(evsel, use_sample_identifier); } @@ -123,7 +122,7 @@ static int get_max_rate(unsigned int *rate) return filename__read_int(path, (int *) rate); } -static int perf_record_opts__config_freq(struct perf_record_opts *opts) +static int record_opts__config_freq(struct record_opts *opts) { bool user_freq = opts->user_freq != UINT_MAX; unsigned int max_rate; @@ -173,7 +172,44 @@ static int perf_record_opts__config_freq(struct perf_record_opts *opts) return 0; } -int perf_record_opts__config(struct perf_record_opts *opts) +int record_opts__config(struct record_opts *opts) { - return perf_record_opts__config_freq(opts); + return record_opts__config_freq(opts); +} + +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) +{ + struct perf_evlist *temp_evlist; + struct perf_evsel *evsel; + int err, fd, cpu; + bool ret = false; + + temp_evlist = perf_evlist__new(); + if (!temp_evlist) + return false; + + err = parse_events(temp_evlist, str); + if (err) + goto out_delete; + + evsel = perf_evlist__last(temp_evlist); + + if (!evlist || cpu_map__empty(evlist->cpus)) { + struct cpu_map *cpus = cpu_map__new(NULL); + + cpu = cpus ? cpus->map[0] : 0; + cpu_map__delete(cpus); + } else { + cpu = evlist->cpus->map[0]; + } + + fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); + if (fd >= 0) { + close(fd); + ret = true; + } + +out_delete: + perf_evlist__delete(temp_evlist); + return ret; } diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index d5e5969f6fea..e108207c5de0 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -194,8 +194,7 @@ static void define_event_symbols(struct event_format *event, zero_flag_atom = 0; break; case PRINT_FIELD: - if (cur_field_name) - free(cur_field_name); + free(cur_field_name); cur_field_name = strdup(args->field.name); break; case PRINT_FLAGS: @@ -257,12 +256,9 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel) return event; } -static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, - struct perf_sample *sample, +static void perl_process_tracepoint(struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread, - struct addr_location *al) + struct thread *thread) { struct format_field *field; static char handler[256]; @@ -349,10 +345,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, static void perl_process_event_generic(union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread __maybe_unused, - struct addr_location *al __maybe_unused) + struct perf_evsel *evsel) { dSP; @@ -377,12 +370,11 @@ static void perl_process_event_generic(union perf_event *event, static void perl_process_event(union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, struct thread *thread, - struct addr_location *al) + struct addr_location *al __maybe_unused) { - perl_process_tracepoint(event, sample, evsel, machine, thread, al); - perl_process_event_generic(event, sample, evsel, machine, thread, al); + perl_process_tracepoint(sample, evsel, thread); + perl_process_event_generic(event, sample, evsel); } static void run_start_sub(void) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 53c20e7fd900..cd9774df3750 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -161,8 +161,7 @@ static void define_event_symbols(struct event_format *event, zero_flag_atom = 0; break; case PRINT_FIELD: - if (cur_field_name) - free(cur_field_name); + free(cur_field_name); cur_field_name = strdup(args->field.name); break; case PRINT_FLAGS: @@ -231,13 +230,10 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel) return event; } -static void python_process_tracepoint(union perf_event *perf_event - __maybe_unused, - struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread, - struct addr_location *al) +static void python_process_tracepoint(struct perf_sample *sample, + struct perf_evsel *evsel, + struct thread *thread, + struct addr_location *al) { PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; static char handler_name[256]; @@ -351,11 +347,8 @@ static void python_process_tracepoint(union perf_event *perf_event Py_DECREF(t); } -static void python_process_general_event(union perf_event *perf_event - __maybe_unused, - struct perf_sample *sample, +static void python_process_general_event(struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine __maybe_unused, struct thread *thread, struct addr_location *al) { @@ -411,22 +404,19 @@ exit: Py_DECREF(t); } -static void python_process_event(union perf_event *perf_event, +static void python_process_event(union perf_event *event __maybe_unused, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, struct thread *thread, struct addr_location *al) { switch (evsel->attr.type) { case PERF_TYPE_TRACEPOINT: - python_process_tracepoint(perf_event, sample, evsel, - machine, thread, al); + python_process_tracepoint(sample, evsel, thread, al); break; /* Reserve for future process_hw/sw/raw APIs */ default: - python_process_general_event(perf_event, sample, evsel, - machine, thread, al); + python_process_general_event(sample, evsel, thread, al); } } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f36d24a02445..7acc03e8f3b2 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -132,18 +132,18 @@ static void perf_session__delete_threads(struct perf_session *session) static void perf_session_env__delete(struct perf_session_env *env) { - free(env->hostname); - free(env->os_release); - free(env->version); - free(env->arch); - free(env->cpu_desc); - free(env->cpuid); + zfree(&env->hostname); + zfree(&env->os_release); + zfree(&env->version); + zfree(&env->arch); + zfree(&env->cpu_desc); + zfree(&env->cpuid); - free(env->cmdline); - free(env->sibling_cores); - free(env->sibling_threads); - free(env->numa_nodes); - free(env->pmu_mappings); + zfree(&env->cmdline); + zfree(&env->sibling_cores); + zfree(&env->sibling_threads); + zfree(&env->numa_nodes); + zfree(&env->pmu_mappings); } void perf_session__delete(struct perf_session *session) @@ -247,27 +247,6 @@ void perf_tool__fill_defaults(struct perf_tool *tool) } } -void mem_bswap_32(void *src, int byte_size) -{ - u32 *m = src; - while (byte_size > 0) { - *m = bswap_32(*m); - byte_size -= sizeof(u32); - ++m; - } -} - -void mem_bswap_64(void *src, int byte_size) -{ - u64 *m = src; - - while (byte_size > 0) { - *m = bswap_64(*m); - byte_size -= sizeof(u64); - ++m; - } -} - static void swap_sample_id_all(union perf_event *event, void *data) { void *end = (void *) event + event->header.size; @@ -851,6 +830,7 @@ static struct machine * struct perf_sample *sample) { const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + struct machine *machine; if (perf_guest && ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || @@ -863,7 +843,11 @@ static struct machine * else pid = sample->pid; - return perf_session__findnew_machine(session, pid); + machine = perf_session__find_machine(session, pid); + if (!machine) + machine = perf_session__findnew_machine(session, + DEFAULT_GUEST_KERNEL_ID); + return machine; } return &session->machines.host; @@ -1158,7 +1142,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session, void *buf = NULL; int skip = 0; u64 head; - int err; + ssize_t err; void *p; perf_tool__fill_defaults(tool); @@ -1400,7 +1384,7 @@ bool perf_session__has_traces(struct perf_session *session, const char *msg) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (evsel->attr.type == PERF_TYPE_TRACEPOINT) return true; } @@ -1458,7 +1442,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) ret += events_stats__fprintf(&session->stats, fp); - list_for_each_entry(pos, &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); ret += events_stats__fprintf(&pos->hists.stats, fp); } @@ -1480,35 +1464,30 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, { struct perf_evsel *pos; - list_for_each_entry(pos, &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { if (pos->attr.type == type) return pos; } return NULL; } -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, - struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, + struct addr_location *al, unsigned int print_opts, unsigned int stack_depth) { - struct addr_location al; struct callchain_cursor_node *node; int print_ip = print_opts & PRINT_IP_OPT_IP; int print_sym = print_opts & PRINT_IP_OPT_SYM; int print_dso = print_opts & PRINT_IP_OPT_DSO; int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET; int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; + int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE; char s = print_oneline ? ' ' : '\t'; - if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { - error("problem processing %d event, skipping it.\n", - event->header.type); - return; - } - if (symbol_conf.use_callchain && sample->callchain) { + struct addr_location node_al; - if (machine__resolve_callchain(machine, evsel, al.thread, + if (machine__resolve_callchain(al->machine, evsel, al->thread, sample, NULL, NULL, PERF_MAX_STACK_DEPTH) != 0) { if (verbose) @@ -1517,20 +1496,31 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, } callchain_cursor_commit(&callchain_cursor); + if (print_symoffset) + node_al = *al; + while (stack_depth) { + u64 addr = 0; + node = callchain_cursor_current(&callchain_cursor); if (!node) break; + if (node->sym && node->sym->ignore) + goto next; + if (print_ip) printf("%c%16" PRIx64, s, node->ip); + if (node->map) + addr = node->map->map_ip(node->map, node->ip); + if (print_sym) { printf(" "); if (print_symoffset) { - al.addr = node->ip; - al.map = node->map; - symbol__fprintf_symname_offs(node->sym, &al, stdout); + node_al.addr = addr; + node_al.map = node->map; + symbol__fprintf_symname_offs(node->sym, &node_al, stdout); } else symbol__fprintf_symname(node->sym, stdout); } @@ -1541,32 +1531,42 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, printf(")"); } + if (print_srcline) + map__fprintf_srcline(node->map, addr, "\n ", + stdout); + if (!print_oneline) printf("\n"); - callchain_cursor_advance(&callchain_cursor); - stack_depth--; +next: + callchain_cursor_advance(&callchain_cursor); } } else { + if (al->sym && al->sym->ignore) + return; + if (print_ip) printf("%16" PRIx64, sample->ip); if (print_sym) { printf(" "); if (print_symoffset) - symbol__fprintf_symname_offs(al.sym, &al, + symbol__fprintf_symname_offs(al->sym, al, stdout); else - symbol__fprintf_symname(al.sym, stdout); + symbol__fprintf_symname(al->sym, stdout); } if (print_dso) { printf(" ("); - map__fprintf_dsoname(al.map, stdout); + map__fprintf_dsoname(al->map, stdout); printf(")"); } + + if (print_srcline) + map__fprintf_srcline(al->map, al->addr, "\n ", stdout); } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 50f640958f0f..3140f8ae6148 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,6 +1,7 @@ #ifndef __PERF_SESSION_H #define __PERF_SESSION_H +#include "trace-event.h" #include "hist.h" #include "event.h" #include "header.h" @@ -32,7 +33,7 @@ struct perf_session { struct perf_header header; struct machines machines; struct perf_evlist *evlist; - struct pevent *pevent; + struct trace_event tevent; struct events_stats stats; bool repipe; struct ordered_samples ordered_samples; @@ -44,6 +45,7 @@ struct perf_session { #define PRINT_IP_OPT_DSO (1<<2) #define PRINT_IP_OPT_SYMOFFSET (1<<3) #define PRINT_IP_OPT_ONELINE (1<<4) +#define PRINT_IP_OPT_SRCLINE (1<<5) struct perf_tool; @@ -72,8 +74,6 @@ int perf_session__resolve_callchain(struct perf_session *session, bool perf_session__has_traces(struct perf_session *session, const char *msg); -void mem_bswap_64(void *src, int byte_size); -void mem_bswap_32(void *src, int byte_size); void perf_event__attr_swap(struct perf_event_attr *attr); int perf_session__create_kernel_maps(struct perf_session *session); @@ -105,8 +105,8 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, unsigned int type); -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, - struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, + struct addr_location *al, unsigned int print_opts, unsigned int stack_depth); int perf_session__cpu_bitmap(struct perf_session *session, diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 58ea5ca6c255..d0aee4b9dfd4 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -25,7 +25,7 @@ cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') libtraceevent = getenv('LIBTRACEEVENT') -liblk = getenv('LIBLK') +libapikfs = getenv('LIBAPIKFS') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] @@ -34,7 +34,7 @@ perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, - extra_objects = [libtraceevent, liblk], + extra_objects = [libtraceevent, libapikfs], ) setup(name='perf', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 8b0bb1f4494a..635cd8f8b22e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -13,6 +13,7 @@ int have_ignore_callees = 0; int sort__need_collapse = 0; int sort__has_parent = 0; int sort__has_sym = 0; +int sort__has_dso = 0; enum sort_mode sort__mode = SORT_MODE__NORMAL; enum sort_type sort__first_dimension; @@ -161,6 +162,11 @@ struct sort_entry sort_dso = { /* --sort symbol */ +static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) +{ + return (int64_t)(right_ip - left_ip); +} + static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) { u64 ip_l, ip_r; @@ -183,15 +189,17 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) int64_t ret; if (!left->ms.sym && !right->ms.sym) - return right->level - left->level; + return _sort__addr_cmp(left->ip, right->ip); /* * comparing symbol address alone is not enough since it's a * relative address within a dso. */ - ret = sort__dso_cmp(left, right); - if (ret != 0) - return ret; + if (!sort__has_dso) { + ret = sort__dso_cmp(left, right); + if (ret != 0) + return ret; + } return _sort__sym_cmp(left->ms.sym, right->ms.sym); } @@ -372,7 +380,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) struct addr_map_symbol *from_r = &right->branch_info->from; if (!from_l->sym && !from_r->sym) - return right->level - left->level; + return _sort__addr_cmp(from_l->addr, from_r->addr); return _sort__sym_cmp(from_l->sym, from_r->sym); } @@ -384,7 +392,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) struct addr_map_symbol *to_r = &right->branch_info->to; if (!to_l->sym && !to_r->sym) - return right->level - left->level; + return _sort__addr_cmp(to_l->addr, to_r->addr); return _sort__sym_cmp(to_l->sym, to_r->sym); } @@ -1056,6 +1064,8 @@ int sort_dimension__add(const char *tok) sort__has_parent = 1; } else if (sd->entry == &sort_sym) { sort__has_sym = 1; + } else if (sd->entry == &sort_dso) { + sort__has_dso = 1; } __sort_dimension__add(sd, i); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index d11aefbc4b8d..f3e4bc5fe5d2 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -129,7 +129,7 @@ static struct a2l_data *addr2line_init(const char *path) out: if (a2l) { - free((void *)a2l->input); + zfree((char **)&a2l->input); free(a2l); } bfd_close(abfd); @@ -140,24 +140,30 @@ static void addr2line_cleanup(struct a2l_data *a2l) { if (a2l->abfd) bfd_close(a2l->abfd); - free((void *)a2l->input); - free(a2l->syms); + zfree((char **)&a2l->input); + zfree(&a2l->syms); free(a2l); } static int addr2line(const char *dso_name, unsigned long addr, - char **file, unsigned int *line) + char **file, unsigned int *line, struct dso *dso) { int ret = 0; - struct a2l_data *a2l; + struct a2l_data *a2l = dso->a2l; + + if (!a2l) { + dso->a2l = addr2line_init(dso_name); + a2l = dso->a2l; + } - a2l = addr2line_init(dso_name); if (a2l == NULL) { pr_warning("addr2line_init failed for %s\n", dso_name); return 0; } a2l->addr = addr; + a2l->found = false; + bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); if (a2l->found && a2l->filename) { @@ -168,14 +174,26 @@ static int addr2line(const char *dso_name, unsigned long addr, ret = 1; } - addr2line_cleanup(a2l); return ret; } +void dso__free_a2l(struct dso *dso) +{ + struct a2l_data *a2l = dso->a2l; + + if (!a2l) + return; + + addr2line_cleanup(a2l); + + dso->a2l = NULL; +} + #else /* HAVE_LIBBFD_SUPPORT */ static int addr2line(const char *dso_name, unsigned long addr, - char **file, unsigned int *line_nr) + char **file, unsigned int *line_nr, + struct dso *dso __maybe_unused) { FILE *fp; char cmd[PATH_MAX]; @@ -219,42 +237,58 @@ out: pclose(fp); return ret; } + +void dso__free_a2l(struct dso *dso __maybe_unused) +{ +} + #endif /* HAVE_LIBBFD_SUPPORT */ +/* + * Number of addr2line failures (without success) before disabling it for that + * dso. + */ +#define A2L_FAIL_LIMIT 123 + char *get_srcline(struct dso *dso, unsigned long addr) { char *file = NULL; unsigned line = 0; char *srcline; - char *dso_name = dso->long_name; - size_t size; + const char *dso_name; if (!dso->has_srcline) return SRCLINE_UNKNOWN; + if (dso->symsrc_filename) + dso_name = dso->symsrc_filename; + else + dso_name = dso->long_name; + if (dso_name[0] == '[') goto out; if (!strncmp(dso_name, "/tmp/perf-", 10)) goto out; - if (!addr2line(dso_name, addr, &file, &line)) + if (!addr2line(dso_name, addr, &file, &line, dso)) goto out; - /* just calculate actual length */ - size = snprintf(NULL, 0, "%s:%u", file, line) + 1; + if (asprintf(&srcline, "%s:%u", file, line) < 0) { + free(file); + goto out; + } - srcline = malloc(size); - if (srcline) - snprintf(srcline, size, "%s:%u", file, line); - else - srcline = SRCLINE_UNKNOWN; + dso->a2l_fails = 0; free(file); return srcline; out: - dso->has_srcline = 0; + if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { + dso->has_srcline = 0; + dso__free_a2l(dso); + } return SRCLINE_UNKNOWN; } diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index cfa906882e2c..4abe23550c73 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -28,7 +28,7 @@ void strbuf_init(struct strbuf *sb, ssize_t hint) void strbuf_release(struct strbuf *sb) { if (sb->alloc) { - free(sb->buf); + zfree(&sb->buf); strbuf_init(sb, 0); } } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 3edd0538161f..79a757a2a15c 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -14,7 +14,7 @@ static void strfilter_node__delete(struct strfilter_node *node) { if (node) { if (node->p && !is_operator(*node->p)) - free((char *)node->p); + zfree((char **)&node->p); strfilter_node__delete(node->l); strfilter_node__delete(node->r); free(node); diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f0b0c008c507..2553e5b55b89 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -128,7 +128,7 @@ void argv_free(char **argv) { char **p; for (p = argv; *p; p++) - free(*p); + zfree(p); free(argv); } diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index eabdce0a2daa..71f9d102b96f 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -5,6 +5,7 @@ */ #include "strlist.h" +#include "util.h" #include #include #include @@ -38,7 +39,7 @@ out_delete: static void str_node__delete(struct str_node *snode, bool dupstr) { if (dupstr) - free((void *)snode->s); + zfree((char **)&snode->s); free(snode); } diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 96c866045d60..43262b83c541 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,8 +17,12 @@ #include #include #include +#include +#include "perf.h" #include "svghelper.h" +#include "util.h" +#include "cpumap.h" static u64 first_time, last_time; static u64 turbo_frequency, max_freq; @@ -28,6 +32,8 @@ static u64 turbo_frequency, max_freq; #define SLOT_HEIGHT 25.0 int svg_page_width = 1000; +u64 svg_highlight; +const char *svg_highlight_name; #define MIN_TEXT_SIZE 0.01 @@ -39,9 +45,14 @@ static double cpu2slot(int cpu) return 2 * cpu + 1; } +static int *topology_map; + static double cpu2y(int cpu) { - return cpu2slot(cpu) * SLOT_MULT; + if (topology_map) + return cpu2slot(topology_map[cpu]) * SLOT_MULT; + else + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -95,6 +106,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; fprintf(svgfile, " \n"); + fprintf(svgfile, "\n"); fprintf(svgfile, "\n", svg_page_width, total_height); fprintf(svgfile, "\n