diff options
52 files changed, 2531 insertions, 584 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-format b/Documentation/ABI/testing/sysfs-bus-event_source-devices-format new file mode 100644 index 000000000000..079afc71363d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-format | |||
@@ -0,0 +1,14 @@ | |||
1 | Where: /sys/bus/event_source/devices/<dev>/format | ||
2 | Date: January 2012 | ||
3 | Kernel Version: 3.3 | ||
4 | Contact: Jiri Olsa <jolsa@redhat.com> | ||
5 | Description: | ||
6 | Attribute group to describe the magic bits that go into | ||
7 | perf_event_attr::config[012] for a particular pmu. | ||
8 | Each attribute of this group defines the 'hardware' bitmask | ||
9 | we want to export, so that userspace can deal with sane | ||
10 | name/value pairs. | ||
11 | |||
12 | Example: 'config1:1,6-10,44' | ||
13 | Defines contents of attribute that occupies bits 1,6-10,44 of | ||
14 | perf_event_attr::config1. | ||
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 40883ffe2da9..bb8e03407e18 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -1313,6 +1313,11 @@ static void __init pmu_check_apic(void) | |||
1313 | pr_info("no hardware sampling interrupt available.\n"); | 1313 | pr_info("no hardware sampling interrupt available.\n"); |
1314 | } | 1314 | } |
1315 | 1315 | ||
1316 | static struct attribute_group x86_pmu_format_group = { | ||
1317 | .name = "format", | ||
1318 | .attrs = NULL, | ||
1319 | }; | ||
1320 | |||
1316 | static int __init init_hw_perf_events(void) | 1321 | static int __init init_hw_perf_events(void) |
1317 | { | 1322 | { |
1318 | struct x86_pmu_quirk *quirk; | 1323 | struct x86_pmu_quirk *quirk; |
@@ -1387,6 +1392,7 @@ static int __init init_hw_perf_events(void) | |||
1387 | } | 1392 | } |
1388 | 1393 | ||
1389 | x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ | 1394 | x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ |
1395 | x86_pmu_format_group.attrs = x86_pmu.format_attrs; | ||
1390 | 1396 | ||
1391 | pr_info("... version: %d\n", x86_pmu.version); | 1397 | pr_info("... version: %d\n", x86_pmu.version); |
1392 | pr_info("... bit width: %d\n", x86_pmu.cntval_bits); | 1398 | pr_info("... bit width: %d\n", x86_pmu.cntval_bits); |
@@ -1615,6 +1621,9 @@ static int x86_pmu_event_idx(struct perf_event *event) | |||
1615 | { | 1621 | { |
1616 | int idx = event->hw.idx; | 1622 | int idx = event->hw.idx; |
1617 | 1623 | ||
1624 | if (!x86_pmu.attr_rdpmc) | ||
1625 | return 0; | ||
1626 | |||
1618 | if (x86_pmu.num_counters_fixed && idx >= X86_PMC_IDX_FIXED) { | 1627 | if (x86_pmu.num_counters_fixed && idx >= X86_PMC_IDX_FIXED) { |
1619 | idx -= X86_PMC_IDX_FIXED; | 1628 | idx -= X86_PMC_IDX_FIXED; |
1620 | idx |= 1 << 30; | 1629 | idx |= 1 << 30; |
@@ -1667,6 +1676,7 @@ static struct attribute_group x86_pmu_attr_group = { | |||
1667 | 1676 | ||
1668 | static const struct attribute_group *x86_pmu_attr_groups[] = { | 1677 | static const struct attribute_group *x86_pmu_attr_groups[] = { |
1669 | &x86_pmu_attr_group, | 1678 | &x86_pmu_attr_group, |
1679 | &x86_pmu_format_group, | ||
1670 | NULL, | 1680 | NULL, |
1671 | }; | 1681 | }; |
1672 | 1682 | ||
@@ -1698,14 +1708,19 @@ static struct pmu pmu = { | |||
1698 | .flush_branch_stack = x86_pmu_flush_branch_stack, | 1708 | .flush_branch_stack = x86_pmu_flush_branch_stack, |
1699 | }; | 1709 | }; |
1700 | 1710 | ||
1701 | void perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now) | 1711 | void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) |
1702 | { | 1712 | { |
1713 | userpg->cap_usr_time = 0; | ||
1714 | userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc; | ||
1715 | userpg->pmc_width = x86_pmu.cntval_bits; | ||
1716 | |||
1703 | if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) | 1717 | if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) |
1704 | return; | 1718 | return; |
1705 | 1719 | ||
1706 | if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) | 1720 | if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) |
1707 | return; | 1721 | return; |
1708 | 1722 | ||
1723 | userpg->cap_usr_time = 1; | ||
1709 | userpg->time_mult = this_cpu_read(cyc2ns); | 1724 | userpg->time_mult = this_cpu_read(cyc2ns); |
1710 | userpg->time_shift = CYC2NS_SCALE_FACTOR; | 1725 | userpg->time_shift = CYC2NS_SCALE_FACTOR; |
1711 | userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; | 1726 | userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; |
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 8484e77c211e..6638aaf54493 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h | |||
@@ -339,6 +339,7 @@ struct x86_pmu { | |||
339 | * sysfs attrs | 339 | * sysfs attrs |
340 | */ | 340 | */ |
341 | int attr_rdpmc; | 341 | int attr_rdpmc; |
342 | struct attribute **format_attrs; | ||
342 | 343 | ||
343 | /* | 344 | /* |
344 | * CPU Hotplug hooks | 345 | * CPU Hotplug hooks |
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index dd002faff7a6..95e7fe1c5f0b 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c | |||
@@ -404,6 +404,21 @@ static void amd_pmu_cpu_dead(int cpu) | |||
404 | } | 404 | } |
405 | } | 405 | } |
406 | 406 | ||
407 | PMU_FORMAT_ATTR(event, "config:0-7,32-35"); | ||
408 | PMU_FORMAT_ATTR(umask, "config:8-15" ); | ||
409 | PMU_FORMAT_ATTR(edge, "config:18" ); | ||
410 | PMU_FORMAT_ATTR(inv, "config:23" ); | ||
411 | PMU_FORMAT_ATTR(cmask, "config:24-31" ); | ||
412 | |||
413 | static struct attribute *amd_format_attr[] = { | ||
414 | &format_attr_event.attr, | ||
415 | &format_attr_umask.attr, | ||
416 | &format_attr_edge.attr, | ||
417 | &format_attr_inv.attr, | ||
418 | &format_attr_cmask.attr, | ||
419 | NULL, | ||
420 | }; | ||
421 | |||
407 | static __initconst const struct x86_pmu amd_pmu = { | 422 | static __initconst const struct x86_pmu amd_pmu = { |
408 | .name = "AMD", | 423 | .name = "AMD", |
409 | .handle_irq = x86_pmu_handle_irq, | 424 | .handle_irq = x86_pmu_handle_irq, |
@@ -426,6 +441,8 @@ static __initconst const struct x86_pmu amd_pmu = { | |||
426 | .get_event_constraints = amd_get_event_constraints, | 441 | .get_event_constraints = amd_get_event_constraints, |
427 | .put_event_constraints = amd_put_event_constraints, | 442 | .put_event_constraints = amd_put_event_constraints, |
428 | 443 | ||
444 | .format_attrs = amd_format_attr, | ||
445 | |||
429 | .cpu_prepare = amd_pmu_cpu_prepare, | 446 | .cpu_prepare = amd_pmu_cpu_prepare, |
430 | .cpu_starting = amd_pmu_cpu_starting, | 447 | .cpu_starting = amd_pmu_cpu_starting, |
431 | .cpu_dead = amd_pmu_cpu_dead, | 448 | .cpu_dead = amd_pmu_cpu_dead, |
@@ -596,6 +613,7 @@ static __initconst const struct x86_pmu amd_pmu_f15h = { | |||
596 | .cpu_dead = amd_pmu_cpu_dead, | 613 | .cpu_dead = amd_pmu_cpu_dead, |
597 | #endif | 614 | #endif |
598 | .cpu_starting = amd_pmu_cpu_starting, | 615 | .cpu_starting = amd_pmu_cpu_starting, |
616 | .format_attrs = amd_format_attr, | ||
599 | }; | 617 | }; |
600 | 618 | ||
601 | __init int amd_pmu_init(void) | 619 | __init int amd_pmu_init(void) |
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 6a84e7f28f05..26b3e2fef104 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -1431,6 +1431,24 @@ static void core_pmu_enable_all(int added) | |||
1431 | } | 1431 | } |
1432 | } | 1432 | } |
1433 | 1433 | ||
1434 | PMU_FORMAT_ATTR(event, "config:0-7" ); | ||
1435 | PMU_FORMAT_ATTR(umask, "config:8-15" ); | ||
1436 | PMU_FORMAT_ATTR(edge, "config:18" ); | ||
1437 | PMU_FORMAT_ATTR(pc, "config:19" ); | ||
1438 | PMU_FORMAT_ATTR(any, "config:21" ); /* v3 + */ | ||
1439 | PMU_FORMAT_ATTR(inv, "config:23" ); | ||
1440 | PMU_FORMAT_ATTR(cmask, "config:24-31" ); | ||
1441 | |||
1442 | static struct attribute *intel_arch_formats_attr[] = { | ||
1443 | &format_attr_event.attr, | ||
1444 | &format_attr_umask.attr, | ||
1445 | &format_attr_edge.attr, | ||
1446 | &format_attr_pc.attr, | ||
1447 | &format_attr_inv.attr, | ||
1448 | &format_attr_cmask.attr, | ||
1449 | NULL, | ||
1450 | }; | ||
1451 | |||
1434 | static __initconst const struct x86_pmu core_pmu = { | 1452 | static __initconst const struct x86_pmu core_pmu = { |
1435 | .name = "core", | 1453 | .name = "core", |
1436 | .handle_irq = x86_pmu_handle_irq, | 1454 | .handle_irq = x86_pmu_handle_irq, |
@@ -1455,6 +1473,7 @@ static __initconst const struct x86_pmu core_pmu = { | |||
1455 | .put_event_constraints = intel_put_event_constraints, | 1473 | .put_event_constraints = intel_put_event_constraints, |
1456 | .event_constraints = intel_core_event_constraints, | 1474 | .event_constraints = intel_core_event_constraints, |
1457 | .guest_get_msrs = core_guest_get_msrs, | 1475 | .guest_get_msrs = core_guest_get_msrs, |
1476 | .format_attrs = intel_arch_formats_attr, | ||
1458 | }; | 1477 | }; |
1459 | 1478 | ||
1460 | struct intel_shared_regs *allocate_shared_regs(int cpu) | 1479 | struct intel_shared_regs *allocate_shared_regs(int cpu) |
@@ -1553,6 +1572,21 @@ static void intel_pmu_flush_branch_stack(void) | |||
1553 | intel_pmu_lbr_reset(); | 1572 | intel_pmu_lbr_reset(); |
1554 | } | 1573 | } |
1555 | 1574 | ||
1575 | PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63"); | ||
1576 | |||
1577 | static struct attribute *intel_arch3_formats_attr[] = { | ||
1578 | &format_attr_event.attr, | ||
1579 | &format_attr_umask.attr, | ||
1580 | &format_attr_edge.attr, | ||
1581 | &format_attr_pc.attr, | ||
1582 | &format_attr_any.attr, | ||
1583 | &format_attr_inv.attr, | ||
1584 | &format_attr_cmask.attr, | ||
1585 | |||
1586 | &format_attr_offcore_rsp.attr, /* XXX do NHM/WSM + SNB breakout */ | ||
1587 | NULL, | ||
1588 | }; | ||
1589 | |||
1556 | static __initconst const struct x86_pmu intel_pmu = { | 1590 | static __initconst const struct x86_pmu intel_pmu = { |
1557 | .name = "Intel", | 1591 | .name = "Intel", |
1558 | .handle_irq = intel_pmu_handle_irq, | 1592 | .handle_irq = intel_pmu_handle_irq, |
@@ -1576,6 +1610,8 @@ static __initconst const struct x86_pmu intel_pmu = { | |||
1576 | .get_event_constraints = intel_get_event_constraints, | 1610 | .get_event_constraints = intel_get_event_constraints, |
1577 | .put_event_constraints = intel_put_event_constraints, | 1611 | .put_event_constraints = intel_put_event_constraints, |
1578 | 1612 | ||
1613 | .format_attrs = intel_arch3_formats_attr, | ||
1614 | |||
1579 | .cpu_prepare = intel_pmu_cpu_prepare, | 1615 | .cpu_prepare = intel_pmu_cpu_prepare, |
1580 | .cpu_starting = intel_pmu_cpu_starting, | 1616 | .cpu_starting = intel_pmu_cpu_starting, |
1581 | .cpu_dying = intel_pmu_cpu_dying, | 1617 | .cpu_dying = intel_pmu_cpu_dying, |
diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index c7181befecde..32bcfc7dd230 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c | |||
@@ -87,6 +87,23 @@ static void p6_pmu_enable_event(struct perf_event *event) | |||
87 | (void)checking_wrmsrl(hwc->config_base, val); | 87 | (void)checking_wrmsrl(hwc->config_base, val); |
88 | } | 88 | } |
89 | 89 | ||
90 | PMU_FORMAT_ATTR(event, "config:0-7" ); | ||
91 | PMU_FORMAT_ATTR(umask, "config:8-15" ); | ||
92 | PMU_FORMAT_ATTR(edge, "config:18" ); | ||
93 | PMU_FORMAT_ATTR(pc, "config:19" ); | ||
94 | PMU_FORMAT_ATTR(inv, "config:23" ); | ||
95 | PMU_FORMAT_ATTR(cmask, "config:24-31" ); | ||
96 | |||
97 | static struct attribute *intel_p6_formats_attr[] = { | ||
98 | &format_attr_event.attr, | ||
99 | &format_attr_umask.attr, | ||
100 | &format_attr_edge.attr, | ||
101 | &format_attr_pc.attr, | ||
102 | &format_attr_inv.attr, | ||
103 | &format_attr_cmask.attr, | ||
104 | NULL, | ||
105 | }; | ||
106 | |||
90 | static __initconst const struct x86_pmu p6_pmu = { | 107 | static __initconst const struct x86_pmu p6_pmu = { |
91 | .name = "p6", | 108 | .name = "p6", |
92 | .handle_irq = x86_pmu_handle_irq, | 109 | .handle_irq = x86_pmu_handle_irq, |
@@ -115,6 +132,8 @@ static __initconst const struct x86_pmu p6_pmu = { | |||
115 | .cntval_mask = (1ULL << 32) - 1, | 132 | .cntval_mask = (1ULL << 32) - 1, |
116 | .get_event_constraints = x86_get_event_constraints, | 133 | .get_event_constraints = x86_get_event_constraints, |
117 | .event_constraints = p6_event_constraints, | 134 | .event_constraints = p6_event_constraints, |
135 | |||
136 | .format_attrs = intel_p6_formats_attr, | ||
118 | }; | 137 | }; |
119 | 138 | ||
120 | __init int p6_pmu_init(void) | 139 | __init int p6_pmu_init(void) |
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index dd478fc8f9f5..5f3f3be5af09 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
@@ -144,12 +144,14 @@ struct event_filter; | |||
144 | enum trace_reg { | 144 | enum trace_reg { |
145 | TRACE_REG_REGISTER, | 145 | TRACE_REG_REGISTER, |
146 | TRACE_REG_UNREGISTER, | 146 | TRACE_REG_UNREGISTER, |
147 | #ifdef CONFIG_PERF_EVENTS | ||
147 | TRACE_REG_PERF_REGISTER, | 148 | TRACE_REG_PERF_REGISTER, |
148 | TRACE_REG_PERF_UNREGISTER, | 149 | TRACE_REG_PERF_UNREGISTER, |
149 | TRACE_REG_PERF_OPEN, | 150 | TRACE_REG_PERF_OPEN, |
150 | TRACE_REG_PERF_CLOSE, | 151 | TRACE_REG_PERF_CLOSE, |
151 | TRACE_REG_PERF_ADD, | 152 | TRACE_REG_PERF_ADD, |
152 | TRACE_REG_PERF_DEL, | 153 | TRACE_REG_PERF_DEL, |
154 | #endif | ||
153 | }; | 155 | }; |
154 | 156 | ||
155 | struct ftrace_event_call; | 157 | struct ftrace_event_call; |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index a5375e7f3fea..645231c373c8 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -430,16 +430,10 @@ extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); | |||
430 | * Most likely, you want to use tracing_on/tracing_off. | 430 | * Most likely, you want to use tracing_on/tracing_off. |
431 | */ | 431 | */ |
432 | #ifdef CONFIG_RING_BUFFER | 432 | #ifdef CONFIG_RING_BUFFER |
433 | void tracing_on(void); | ||
434 | void tracing_off(void); | ||
435 | /* trace_off_permanent stops recording with no way to bring it back */ | 433 | /* trace_off_permanent stops recording with no way to bring it back */ |
436 | void tracing_off_permanent(void); | 434 | void tracing_off_permanent(void); |
437 | int tracing_is_on(void); | ||
438 | #else | 435 | #else |
439 | static inline void tracing_on(void) { } | ||
440 | static inline void tracing_off(void) { } | ||
441 | static inline void tracing_off_permanent(void) { } | 436 | static inline void tracing_off_permanent(void) { } |
442 | static inline int tracing_is_on(void) { return 0; } | ||
443 | #endif | 437 | #endif |
444 | 438 | ||
445 | enum ftrace_dump_mode { | 439 | enum ftrace_dump_mode { |
@@ -449,6 +443,10 @@ enum ftrace_dump_mode { | |||
449 | }; | 443 | }; |
450 | 444 | ||
451 | #ifdef CONFIG_TRACING | 445 | #ifdef CONFIG_TRACING |
446 | void tracing_on(void); | ||
447 | void tracing_off(void); | ||
448 | int tracing_is_on(void); | ||
449 | |||
452 | extern void tracing_start(void); | 450 | extern void tracing_start(void); |
453 | extern void tracing_stop(void); | 451 | extern void tracing_stop(void); |
454 | extern void ftrace_off_permanent(void); | 452 | extern void ftrace_off_permanent(void); |
@@ -533,6 +531,11 @@ static inline void tracing_start(void) { } | |||
533 | static inline void tracing_stop(void) { } | 531 | static inline void tracing_stop(void) { } |
534 | static inline void ftrace_off_permanent(void) { } | 532 | static inline void ftrace_off_permanent(void) { } |
535 | static inline void trace_dump_stack(void) { } | 533 | static inline void trace_dump_stack(void) { } |
534 | |||
535 | static inline void tracing_on(void) { } | ||
536 | static inline void tracing_off(void) { } | ||
537 | static inline int tracing_is_on(void) { return 0; } | ||
538 | |||
536 | static inline int | 539 | static inline int |
537 | trace_printk(const char *fmt, ...) | 540 | trace_printk(const char *fmt, ...) |
538 | { | 541 | { |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index bd9f55a5958d..ddbb6a901f65 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -299,18 +299,31 @@ struct perf_event_mmap_page { | |||
299 | /* | 299 | /* |
300 | * Bits needed to read the hw events in user-space. | 300 | * Bits needed to read the hw events in user-space. |
301 | * | 301 | * |
302 | * u32 seq; | 302 | * u32 seq, time_mult, time_shift, idx, width; |
303 | * s64 count; | 303 | * u64 count, enabled, running; |
304 | * u64 cyc, time_offset; | ||
305 | * s64 pmc = 0; | ||
304 | * | 306 | * |
305 | * do { | 307 | * do { |
306 | * seq = pc->lock; | 308 | * seq = pc->lock; |
307 | * | ||
308 | * barrier() | 309 | * barrier() |
309 | * if (pc->index) { | 310 | * |
310 | * count = pmc_read(pc->index - 1); | 311 | * enabled = pc->time_enabled; |
311 | * count += pc->offset; | 312 | * running = pc->time_running; |
312 | * } else | 313 | * |
313 | * goto regular_read; | 314 | * if (pc->cap_usr_time && enabled != running) { |
315 | * cyc = rdtsc(); | ||
316 | * time_offset = pc->time_offset; | ||
317 | * time_mult = pc->time_mult; | ||
318 | * time_shift = pc->time_shift; | ||
319 | * } | ||
320 | * | ||
321 | * idx = pc->index; | ||
322 | * count = pc->offset; | ||
323 | * if (pc->cap_usr_rdpmc && idx) { | ||
324 | * width = pc->pmc_width; | ||
325 | * pmc = rdpmc(idx - 1); | ||
326 | * } | ||
314 | * | 327 | * |
315 | * barrier(); | 328 | * barrier(); |
316 | * } while (pc->lock != seq); | 329 | * } while (pc->lock != seq); |
@@ -323,14 +336,57 @@ struct perf_event_mmap_page { | |||
323 | __s64 offset; /* add to hardware event value */ | 336 | __s64 offset; /* add to hardware event value */ |
324 | __u64 time_enabled; /* time event active */ | 337 | __u64 time_enabled; /* time event active */ |
325 | __u64 time_running; /* time event on cpu */ | 338 | __u64 time_running; /* time event on cpu */ |
326 | __u32 time_mult, time_shift; | 339 | union { |
340 | __u64 capabilities; | ||
341 | __u64 cap_usr_time : 1, | ||
342 | cap_usr_rdpmc : 1, | ||
343 | cap_____res : 62; | ||
344 | }; | ||
345 | |||
346 | /* | ||
347 | * If cap_usr_rdpmc this field provides the bit-width of the value | ||
348 | * read using the rdpmc() or equivalent instruction. This can be used | ||
349 | * to sign extend the result like: | ||
350 | * | ||
351 | * pmc <<= 64 - width; | ||
352 | * pmc >>= 64 - width; // signed shift right | ||
353 | * count += pmc; | ||
354 | */ | ||
355 | __u16 pmc_width; | ||
356 | |||
357 | /* | ||
358 | * If cap_usr_time the below fields can be used to compute the time | ||
359 | * delta since time_enabled (in ns) using rdtsc or similar. | ||
360 | * | ||
361 | * u64 quot, rem; | ||
362 | * u64 delta; | ||
363 | * | ||
364 | * quot = (cyc >> time_shift); | ||
365 | * rem = cyc & ((1 << time_shift) - 1); | ||
366 | * delta = time_offset + quot * time_mult + | ||
367 | * ((rem * time_mult) >> time_shift); | ||
368 | * | ||
369 | * Where time_offset,time_mult,time_shift and cyc are read in the | ||
370 | * seqcount loop described above. This delta can then be added to | ||
371 | * enabled and possible running (if idx), improving the scaling: | ||
372 | * | ||
373 | * enabled += delta; | ||
374 | * if (idx) | ||
375 | * running += delta; | ||
376 | * | ||
377 | * quot = count / running; | ||
378 | * rem = count % running; | ||
379 | * count = quot * enabled + (rem * enabled) / running; | ||
380 | */ | ||
381 | __u16 time_shift; | ||
382 | __u32 time_mult; | ||
327 | __u64 time_offset; | 383 | __u64 time_offset; |
328 | 384 | ||
329 | /* | 385 | /* |
330 | * Hole for extension of the self monitor capabilities | 386 | * Hole for extension of the self monitor capabilities |
331 | */ | 387 | */ |
332 | 388 | ||
333 | __u64 __reserved[121]; /* align to 1k */ | 389 | __u64 __reserved[120]; /* align to 1k */ |
334 | 390 | ||
335 | /* | 391 | /* |
336 | * Control data for the mmap() data buffer. | 392 | * Control data for the mmap() data buffer. |
@@ -550,6 +606,7 @@ struct perf_guest_info_callbacks { | |||
550 | #include <linux/irq_work.h> | 606 | #include <linux/irq_work.h> |
551 | #include <linux/static_key.h> | 607 | #include <linux/static_key.h> |
552 | #include <linux/atomic.h> | 608 | #include <linux/atomic.h> |
609 | #include <linux/sysfs.h> | ||
553 | #include <asm/local.h> | 610 | #include <asm/local.h> |
554 | 611 | ||
555 | #define PERF_MAX_STACK_DEPTH 255 | 612 | #define PERF_MAX_STACK_DEPTH 255 |
@@ -1291,5 +1348,18 @@ do { \ | |||
1291 | register_cpu_notifier(&fn##_nb); \ | 1348 | register_cpu_notifier(&fn##_nb); \ |
1292 | } while (0) | 1349 | } while (0) |
1293 | 1350 | ||
1351 | |||
1352 | #define PMU_FORMAT_ATTR(_name, _format) \ | ||
1353 | static ssize_t \ | ||
1354 | _name##_show(struct device *dev, \ | ||
1355 | struct device_attribute *attr, \ | ||
1356 | char *page) \ | ||
1357 | { \ | ||
1358 | BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \ | ||
1359 | return sprintf(page, _format "\n"); \ | ||
1360 | } \ | ||
1361 | \ | ||
1362 | static struct device_attribute format_attr_##_name = __ATTR_RO(_name) | ||
1363 | |||
1294 | #endif /* __KERNEL__ */ | 1364 | #endif /* __KERNEL__ */ |
1295 | #endif /* _LINUX_PERF_EVENT_H */ | 1365 | #endif /* _LINUX_PERF_EVENT_H */ |
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 67be0376d8e3..7be2e88f23fd 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h | |||
@@ -151,6 +151,9 @@ int ring_buffer_empty_cpu(struct ring_buffer *buffer, int cpu); | |||
151 | 151 | ||
152 | void ring_buffer_record_disable(struct ring_buffer *buffer); | 152 | void ring_buffer_record_disable(struct ring_buffer *buffer); |
153 | void ring_buffer_record_enable(struct ring_buffer *buffer); | 153 | void ring_buffer_record_enable(struct ring_buffer *buffer); |
154 | void ring_buffer_record_off(struct ring_buffer *buffer); | ||
155 | void ring_buffer_record_on(struct ring_buffer *buffer); | ||
156 | int ring_buffer_record_is_on(struct ring_buffer *buffer); | ||
154 | void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu); | 157 | void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu); |
155 | void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu); | 158 | void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu); |
156 | 159 | ||
diff --git a/kernel/events/core.c b/kernel/events/core.c index 4b50357914fb..a6a9ec4cd8f5 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -3348,7 +3348,7 @@ static void calc_timer_values(struct perf_event *event, | |||
3348 | *running = ctx_time - event->tstamp_running; | 3348 | *running = ctx_time - event->tstamp_running; |
3349 | } | 3349 | } |
3350 | 3350 | ||
3351 | void __weak perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now) | 3351 | void __weak arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) |
3352 | { | 3352 | { |
3353 | } | 3353 | } |
3354 | 3354 | ||
@@ -3398,7 +3398,7 @@ void perf_event_update_userpage(struct perf_event *event) | |||
3398 | userpg->time_running = running + | 3398 | userpg->time_running = running + |
3399 | atomic64_read(&event->child_total_time_running); | 3399 | atomic64_read(&event->child_total_time_running); |
3400 | 3400 | ||
3401 | perf_update_user_clock(userpg, now); | 3401 | arch_perf_update_userpage(userpg, now); |
3402 | 3402 | ||
3403 | barrier(); | 3403 | barrier(); |
3404 | ++userpg->lock; | 3404 | ++userpg->lock; |
@@ -7116,6 +7116,13 @@ void __init perf_event_init(void) | |||
7116 | 7116 | ||
7117 | /* do not patch jump label more than once per second */ | 7117 | /* do not patch jump label more than once per second */ |
7118 | jump_label_rate_limit(&perf_sched_events, HZ); | 7118 | jump_label_rate_limit(&perf_sched_events, HZ); |
7119 | |||
7120 | /* | ||
7121 | * Build time assertion that we keep the data_head at the intended | ||
7122 | * location. IOW, validation we got the __reserved[] size right. | ||
7123 | */ | ||
7124 | BUILD_BUG_ON((offsetof(struct perf_event_mmap_page, data_head)) | ||
7125 | != 1024); | ||
7119 | } | 7126 | } |
7120 | 7127 | ||
7121 | static int __init perf_event_sysfs_init(void) | 7128 | static int __init perf_event_sysfs_init(void) |
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index cd3134510f3d..a1d2849f2473 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -141,7 +141,7 @@ if FTRACE | |||
141 | config FUNCTION_TRACER | 141 | config FUNCTION_TRACER |
142 | bool "Kernel Function Tracer" | 142 | bool "Kernel Function Tracer" |
143 | depends on HAVE_FUNCTION_TRACER | 143 | depends on HAVE_FUNCTION_TRACER |
144 | select FRAME_POINTER if !ARM_UNWIND && !S390 && !MICROBLAZE | 144 | select FRAME_POINTER if !ARM_UNWIND && !PPC && !S390 && !MICROBLAZE |
145 | select KALLSYMS | 145 | select KALLSYMS |
146 | select GENERIC_TRACER | 146 | select GENERIC_TRACER |
147 | select CONTEXT_SWITCH_TRACER | 147 | select CONTEXT_SWITCH_TRACER |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 867bd1dd2dd0..0fa92f677c92 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -249,7 +249,8 @@ static void update_ftrace_function(void) | |||
249 | #else | 249 | #else |
250 | __ftrace_trace_function = func; | 250 | __ftrace_trace_function = func; |
251 | #endif | 251 | #endif |
252 | ftrace_trace_function = ftrace_test_stop_func; | 252 | ftrace_trace_function = |
253 | (func == ftrace_stub) ? func : ftrace_test_stop_func; | ||
253 | #endif | 254 | #endif |
254 | } | 255 | } |
255 | 256 | ||
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index f5b7b5c1195b..cf8d11e91efd 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c | |||
@@ -154,33 +154,10 @@ enum { | |||
154 | 154 | ||
155 | static unsigned long ring_buffer_flags __read_mostly = RB_BUFFERS_ON; | 155 | static unsigned long ring_buffer_flags __read_mostly = RB_BUFFERS_ON; |
156 | 156 | ||
157 | #define BUF_PAGE_HDR_SIZE offsetof(struct buffer_data_page, data) | 157 | /* Used for individual buffers (after the counter) */ |
158 | 158 | #define RB_BUFFER_OFF (1 << 20) | |
159 | /** | ||
160 | * tracing_on - enable all tracing buffers | ||
161 | * | ||
162 | * This function enables all tracing buffers that may have been | ||
163 | * disabled with tracing_off. | ||
164 | */ | ||
165 | void tracing_on(void) | ||
166 | { | ||
167 | set_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags); | ||
168 | } | ||
169 | EXPORT_SYMBOL_GPL(tracing_on); | ||
170 | 159 | ||
171 | /** | 160 | #define BUF_PAGE_HDR_SIZE offsetof(struct buffer_data_page, data) |
172 | * tracing_off - turn off all tracing buffers | ||
173 | * | ||
174 | * This function stops all tracing buffers from recording data. | ||
175 | * It does not disable any overhead the tracers themselves may | ||
176 | * be causing. This function simply causes all recording to | ||
177 | * the ring buffers to fail. | ||
178 | */ | ||
179 | void tracing_off(void) | ||
180 | { | ||
181 | clear_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags); | ||
182 | } | ||
183 | EXPORT_SYMBOL_GPL(tracing_off); | ||
184 | 161 | ||
185 | /** | 162 | /** |
186 | * tracing_off_permanent - permanently disable ring buffers | 163 | * tracing_off_permanent - permanently disable ring buffers |
@@ -193,15 +170,6 @@ void tracing_off_permanent(void) | |||
193 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); | 170 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); |
194 | } | 171 | } |
195 | 172 | ||
196 | /** | ||
197 | * tracing_is_on - show state of ring buffers enabled | ||
198 | */ | ||
199 | int tracing_is_on(void) | ||
200 | { | ||
201 | return ring_buffer_flags == RB_BUFFERS_ON; | ||
202 | } | ||
203 | EXPORT_SYMBOL_GPL(tracing_is_on); | ||
204 | |||
205 | #define RB_EVNT_HDR_SIZE (offsetof(struct ring_buffer_event, array)) | 173 | #define RB_EVNT_HDR_SIZE (offsetof(struct ring_buffer_event, array)) |
206 | #define RB_ALIGNMENT 4U | 174 | #define RB_ALIGNMENT 4U |
207 | #define RB_MAX_SMALL_DATA (RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX) | 175 | #define RB_MAX_SMALL_DATA (RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX) |
@@ -2619,6 +2587,63 @@ void ring_buffer_record_enable(struct ring_buffer *buffer) | |||
2619 | EXPORT_SYMBOL_GPL(ring_buffer_record_enable); | 2587 | EXPORT_SYMBOL_GPL(ring_buffer_record_enable); |
2620 | 2588 | ||
2621 | /** | 2589 | /** |
2590 | * ring_buffer_record_off - stop all writes into the buffer | ||
2591 | * @buffer: The ring buffer to stop writes to. | ||
2592 | * | ||
2593 | * This prevents all writes to the buffer. Any attempt to write | ||
2594 | * to the buffer after this will fail and return NULL. | ||
2595 | * | ||
2596 | * This is different than ring_buffer_record_disable() as | ||
2597 | * it works like an on/off switch, where as the disable() verison | ||
2598 | * must be paired with a enable(). | ||
2599 | */ | ||
2600 | void ring_buffer_record_off(struct ring_buffer *buffer) | ||
2601 | { | ||
2602 | unsigned int rd; | ||
2603 | unsigned int new_rd; | ||
2604 | |||
2605 | do { | ||
2606 | rd = atomic_read(&buffer->record_disabled); | ||
2607 | new_rd = rd | RB_BUFFER_OFF; | ||
2608 | } while (atomic_cmpxchg(&buffer->record_disabled, rd, new_rd) != rd); | ||
2609 | } | ||
2610 | EXPORT_SYMBOL_GPL(ring_buffer_record_off); | ||
2611 | |||
2612 | /** | ||
2613 | * ring_buffer_record_on - restart writes into the buffer | ||
2614 | * @buffer: The ring buffer to start writes to. | ||
2615 | * | ||
2616 | * This enables all writes to the buffer that was disabled by | ||
2617 | * ring_buffer_record_off(). | ||
2618 | * | ||
2619 | * This is different than ring_buffer_record_enable() as | ||
2620 | * it works like an on/off switch, where as the enable() verison | ||
2621 | * must be paired with a disable(). | ||
2622 | */ | ||
2623 | void ring_buffer_record_on(struct ring_buffer *buffer) | ||
2624 | { | ||
2625 | unsigned int rd; | ||
2626 | unsigned int new_rd; | ||
2627 | |||
2628 | do { | ||
2629 | rd = atomic_read(&buffer->record_disabled); | ||
2630 | new_rd = rd & ~RB_BUFFER_OFF; | ||
2631 | } while (atomic_cmpxchg(&buffer->record_disabled, rd, new_rd) != rd); | ||
2632 | } | ||
2633 | EXPORT_SYMBOL_GPL(ring_buffer_record_on); | ||
2634 | |||
2635 | /** | ||
2636 | * ring_buffer_record_is_on - return true if the ring buffer can write | ||
2637 | * @buffer: The ring buffer to see if write is enabled | ||
2638 | * | ||
2639 | * Returns true if the ring buffer is in a state that it accepts writes. | ||
2640 | */ | ||
2641 | int ring_buffer_record_is_on(struct ring_buffer *buffer) | ||
2642 | { | ||
2643 | return !atomic_read(&buffer->record_disabled); | ||
2644 | } | ||
2645 | |||
2646 | /** | ||
2622 | * ring_buffer_record_disable_cpu - stop all writes into the cpu_buffer | 2647 | * ring_buffer_record_disable_cpu - stop all writes into the cpu_buffer |
2623 | * @buffer: The ring buffer to stop writes to. | 2648 | * @buffer: The ring buffer to stop writes to. |
2624 | * @cpu: The CPU buffer to stop | 2649 | * @cpu: The CPU buffer to stop |
@@ -4039,68 +4064,6 @@ int ring_buffer_read_page(struct ring_buffer *buffer, | |||
4039 | } | 4064 | } |
4040 | EXPORT_SYMBOL_GPL(ring_buffer_read_page); | 4065 | EXPORT_SYMBOL_GPL(ring_buffer_read_page); |
4041 | 4066 | ||
4042 | #ifdef CONFIG_TRACING | ||
4043 | static ssize_t | ||
4044 | rb_simple_read(struct file *filp, char __user *ubuf, | ||
4045 | size_t cnt, loff_t *ppos) | ||
4046 | { | ||
4047 | unsigned long *p = filp->private_data; | ||
4048 | char buf[64]; | ||
4049 | int r; | ||
4050 | |||
4051 | if (test_bit(RB_BUFFERS_DISABLED_BIT, p)) | ||
4052 | r = sprintf(buf, "permanently disabled\n"); | ||
4053 | else | ||
4054 | r = sprintf(buf, "%d\n", test_bit(RB_BUFFERS_ON_BIT, p)); | ||
4055 | |||
4056 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
4057 | } | ||
4058 | |||
4059 | static ssize_t | ||
4060 | rb_simple_write(struct file *filp, const char __user *ubuf, | ||
4061 | size_t cnt, loff_t *ppos) | ||
4062 | { | ||
4063 | unsigned long *p = filp->private_data; | ||
4064 | unsigned long val; | ||
4065 | int ret; | ||
4066 | |||
4067 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); | ||
4068 | if (ret) | ||
4069 | return ret; | ||
4070 | |||
4071 | if (val) | ||
4072 | set_bit(RB_BUFFERS_ON_BIT, p); | ||
4073 | else | ||
4074 | clear_bit(RB_BUFFERS_ON_BIT, p); | ||
4075 | |||
4076 | (*ppos)++; | ||
4077 | |||
4078 | return cnt; | ||
4079 | } | ||
4080 | |||
4081 | static const struct file_operations rb_simple_fops = { | ||
4082 | .open = tracing_open_generic, | ||
4083 | .read = rb_simple_read, | ||
4084 | .write = rb_simple_write, | ||
4085 | .llseek = default_llseek, | ||
4086 | }; | ||
4087 | |||
4088 | |||
4089 | static __init int rb_init_debugfs(void) | ||
4090 | { | ||
4091 | struct dentry *d_tracer; | ||
4092 | |||
4093 | d_tracer = tracing_init_dentry(); | ||
4094 | |||
4095 | trace_create_file("tracing_on", 0644, d_tracer, | ||
4096 | &ring_buffer_flags, &rb_simple_fops); | ||
4097 | |||
4098 | return 0; | ||
4099 | } | ||
4100 | |||
4101 | fs_initcall(rb_init_debugfs); | ||
4102 | #endif | ||
4103 | |||
4104 | #ifdef CONFIG_HOTPLUG_CPU | 4067 | #ifdef CONFIG_HOTPLUG_CPU |
4105 | static int rb_cpu_notify(struct notifier_block *self, | 4068 | static int rb_cpu_notify(struct notifier_block *self, |
4106 | unsigned long action, void *hcpu) | 4069 | unsigned long action, void *hcpu) |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 10d5503f0d04..ed7b5d1e12f4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/ctype.h> | 36 | #include <linux/ctype.h> |
37 | #include <linux/init.h> | 37 | #include <linux/init.h> |
38 | #include <linux/poll.h> | 38 | #include <linux/poll.h> |
39 | #include <linux/nmi.h> | ||
39 | #include <linux/fs.h> | 40 | #include <linux/fs.h> |
40 | 41 | ||
41 | #include "trace.h" | 42 | #include "trace.h" |
@@ -352,6 +353,59 @@ static void wakeup_work_handler(struct work_struct *work) | |||
352 | static DECLARE_DELAYED_WORK(wakeup_work, wakeup_work_handler); | 353 | static DECLARE_DELAYED_WORK(wakeup_work, wakeup_work_handler); |
353 | 354 | ||
354 | /** | 355 | /** |
356 | * tracing_on - enable tracing buffers | ||
357 | * | ||
358 | * This function enables tracing buffers that may have been | ||
359 | * disabled with tracing_off. | ||
360 | */ | ||
361 | void tracing_on(void) | ||
362 | { | ||
363 | if (global_trace.buffer) | ||
364 | ring_buffer_record_on(global_trace.buffer); | ||
365 | /* | ||
366 | * This flag is only looked at when buffers haven't been | ||
367 | * allocated yet. We don't really care about the race | ||
368 | * between setting this flag and actually turning | ||
369 | * on the buffer. | ||
370 | */ | ||
371 | global_trace.buffer_disabled = 0; | ||
372 | } | ||
373 | EXPORT_SYMBOL_GPL(tracing_on); | ||
374 | |||
375 | /** | ||
376 | * tracing_off - turn off tracing buffers | ||
377 | * | ||
378 | * This function stops the tracing buffers from recording data. | ||
379 | * It does not disable any overhead the tracers themselves may | ||
380 | * be causing. This function simply causes all recording to | ||
381 | * the ring buffers to fail. | ||
382 | */ | ||
383 | void tracing_off(void) | ||
384 | { | ||
385 | if (global_trace.buffer) | ||
386 | ring_buffer_record_on(global_trace.buffer); | ||
387 | /* | ||
388 | * This flag is only looked at when buffers haven't been | ||
389 | * allocated yet. We don't really care about the race | ||
390 | * between setting this flag and actually turning | ||
391 | * on the buffer. | ||
392 | */ | ||
393 | global_trace.buffer_disabled = 1; | ||
394 | } | ||
395 | EXPORT_SYMBOL_GPL(tracing_off); | ||
396 | |||
397 | /** | ||
398 | * tracing_is_on - show state of ring buffers enabled | ||
399 | */ | ||
400 | int tracing_is_on(void) | ||
401 | { | ||
402 | if (global_trace.buffer) | ||
403 | return ring_buffer_record_is_on(global_trace.buffer); | ||
404 | return !global_trace.buffer_disabled; | ||
405 | } | ||
406 | EXPORT_SYMBOL_GPL(tracing_is_on); | ||
407 | |||
408 | /** | ||
355 | * trace_wake_up - wake up tasks waiting for trace input | 409 | * trace_wake_up - wake up tasks waiting for trace input |
356 | * | 410 | * |
357 | * Schedules a delayed work to wake up any task that is blocked on the | 411 | * Schedules a delayed work to wake up any task that is blocked on the |
@@ -1644,6 +1698,7 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu, | |||
1644 | int cpu_file = iter->cpu_file; | 1698 | int cpu_file = iter->cpu_file; |
1645 | u64 next_ts = 0, ts; | 1699 | u64 next_ts = 0, ts; |
1646 | int next_cpu = -1; | 1700 | int next_cpu = -1; |
1701 | int next_size = 0; | ||
1647 | int cpu; | 1702 | int cpu; |
1648 | 1703 | ||
1649 | /* | 1704 | /* |
@@ -1675,9 +1730,12 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu, | |||
1675 | next_cpu = cpu; | 1730 | next_cpu = cpu; |
1676 | next_ts = ts; | 1731 | next_ts = ts; |
1677 | next_lost = lost_events; | 1732 | next_lost = lost_events; |
1733 | next_size = iter->ent_size; | ||
1678 | } | 1734 | } |
1679 | } | 1735 | } |
1680 | 1736 | ||
1737 | iter->ent_size = next_size; | ||
1738 | |||
1681 | if (ent_cpu) | 1739 | if (ent_cpu) |
1682 | *ent_cpu = next_cpu; | 1740 | *ent_cpu = next_cpu; |
1683 | 1741 | ||
@@ -4567,6 +4625,55 @@ static __init void create_trace_options_dir(void) | |||
4567 | create_trace_option_core_file(trace_options[i], i); | 4625 | create_trace_option_core_file(trace_options[i], i); |
4568 | } | 4626 | } |
4569 | 4627 | ||
4628 | static ssize_t | ||
4629 | rb_simple_read(struct file *filp, char __user *ubuf, | ||
4630 | size_t cnt, loff_t *ppos) | ||
4631 | { | ||
4632 | struct ring_buffer *buffer = filp->private_data; | ||
4633 | char buf[64]; | ||
4634 | int r; | ||
4635 | |||
4636 | if (buffer) | ||
4637 | r = ring_buffer_record_is_on(buffer); | ||
4638 | else | ||
4639 | r = 0; | ||
4640 | |||
4641 | r = sprintf(buf, "%d\n", r); | ||
4642 | |||
4643 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
4644 | } | ||
4645 | |||
4646 | static ssize_t | ||
4647 | rb_simple_write(struct file *filp, const char __user *ubuf, | ||
4648 | size_t cnt, loff_t *ppos) | ||
4649 | { | ||
4650 | struct ring_buffer *buffer = filp->private_data; | ||
4651 | unsigned long val; | ||
4652 | int ret; | ||
4653 | |||
4654 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); | ||
4655 | if (ret) | ||
4656 | return ret; | ||
4657 | |||
4658 | if (buffer) { | ||
4659 | if (val) | ||
4660 | ring_buffer_record_on(buffer); | ||
4661 | else | ||
4662 | ring_buffer_record_off(buffer); | ||
4663 | } | ||
4664 | |||
4665 | (*ppos)++; | ||
4666 | |||
4667 | return cnt; | ||
4668 | } | ||
4669 | |||
4670 | static const struct file_operations rb_simple_fops = { | ||
4671 | .open = tracing_open_generic, | ||
4672 | .read = rb_simple_read, | ||
4673 | .write = rb_simple_write, | ||
4674 | .llseek = default_llseek, | ||
4675 | }; | ||
4676 | |||
4570 | static __init int tracer_init_debugfs(void) | 4677 | static __init int tracer_init_debugfs(void) |
4571 | { | 4678 | { |
4572 | struct dentry *d_tracer; | 4679 | struct dentry *d_tracer; |
@@ -4626,6 +4733,9 @@ static __init int tracer_init_debugfs(void) | |||
4626 | trace_create_file("trace_clock", 0644, d_tracer, NULL, | 4733 | trace_create_file("trace_clock", 0644, d_tracer, NULL, |
4627 | &trace_clock_fops); | 4734 | &trace_clock_fops); |
4628 | 4735 | ||
4736 | trace_create_file("tracing_on", 0644, d_tracer, | ||
4737 | global_trace.buffer, &rb_simple_fops); | ||
4738 | |||
4629 | #ifdef CONFIG_DYNAMIC_FTRACE | 4739 | #ifdef CONFIG_DYNAMIC_FTRACE |
4630 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, | 4740 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, |
4631 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); | 4741 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); |
@@ -4798,6 +4908,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | |||
4798 | if (ret != TRACE_TYPE_NO_CONSUME) | 4908 | if (ret != TRACE_TYPE_NO_CONSUME) |
4799 | trace_consume(&iter); | 4909 | trace_consume(&iter); |
4800 | } | 4910 | } |
4911 | touch_nmi_watchdog(); | ||
4801 | 4912 | ||
4802 | trace_printk_seq(&iter.seq); | 4913 | trace_printk_seq(&iter.seq); |
4803 | } | 4914 | } |
@@ -4863,6 +4974,8 @@ __init static int tracer_alloc_buffers(void) | |||
4863 | goto out_free_cpumask; | 4974 | goto out_free_cpumask; |
4864 | } | 4975 | } |
4865 | global_trace.entries = ring_buffer_size(global_trace.buffer); | 4976 | global_trace.entries = ring_buffer_size(global_trace.buffer); |
4977 | if (global_trace.buffer_disabled) | ||
4978 | tracing_off(); | ||
4866 | 4979 | ||
4867 | 4980 | ||
4868 | #ifdef CONFIG_TRACER_MAX_TRACE | 4981 | #ifdef CONFIG_TRACER_MAX_TRACE |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 54faec790bc1..95059f091a24 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -154,6 +154,7 @@ struct trace_array { | |||
154 | struct ring_buffer *buffer; | 154 | struct ring_buffer *buffer; |
155 | unsigned long entries; | 155 | unsigned long entries; |
156 | int cpu; | 156 | int cpu; |
157 | int buffer_disabled; | ||
157 | cycle_t time_start; | 158 | cycle_t time_start; |
158 | struct task_struct *waiter; | 159 | struct task_struct *waiter; |
159 | struct trace_array_cpu *data[NR_CPUS]; | 160 | struct trace_array_cpu *data[NR_CPUS]; |
@@ -835,13 +836,11 @@ extern const char *__stop___trace_bprintk_fmt[]; | |||
835 | filter) | 836 | filter) |
836 | #include "trace_entries.h" | 837 | #include "trace_entries.h" |
837 | 838 | ||
838 | #ifdef CONFIG_PERF_EVENTS | ||
839 | #ifdef CONFIG_FUNCTION_TRACER | 839 | #ifdef CONFIG_FUNCTION_TRACER |
840 | int perf_ftrace_event_register(struct ftrace_event_call *call, | 840 | int perf_ftrace_event_register(struct ftrace_event_call *call, |
841 | enum trace_reg type, void *data); | 841 | enum trace_reg type, void *data); |
842 | #else | 842 | #else |
843 | #define perf_ftrace_event_register NULL | 843 | #define perf_ftrace_event_register NULL |
844 | #endif /* CONFIG_FUNCTION_TRACER */ | 844 | #endif /* CONFIG_FUNCTION_TRACER */ |
845 | #endif /* CONFIG_PERF_EVENTS */ | ||
846 | 845 | ||
847 | #endif /* _LINUX_KERNEL_TRACE_H */ | 846 | #endif /* _LINUX_KERNEL_TRACE_H */ |
diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index d91eb0541b3a..4108e1250ca2 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h | |||
@@ -166,6 +166,12 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry, | |||
166 | 166 | ||
167 | #define FTRACE_STACK_ENTRIES 8 | 167 | #define FTRACE_STACK_ENTRIES 8 |
168 | 168 | ||
169 | #ifndef CONFIG_64BIT | ||
170 | # define IP_FMT "%08lx" | ||
171 | #else | ||
172 | # define IP_FMT "%016lx" | ||
173 | #endif | ||
174 | |||
169 | FTRACE_ENTRY(kernel_stack, stack_entry, | 175 | FTRACE_ENTRY(kernel_stack, stack_entry, |
170 | 176 | ||
171 | TRACE_STACK, | 177 | TRACE_STACK, |
@@ -175,8 +181,9 @@ FTRACE_ENTRY(kernel_stack, stack_entry, | |||
175 | __dynamic_array(unsigned long, caller ) | 181 | __dynamic_array(unsigned long, caller ) |
176 | ), | 182 | ), |
177 | 183 | ||
178 | F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n" | 184 | F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" |
179 | "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n", | 185 | "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" |
186 | "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n", | ||
180 | __entry->caller[0], __entry->caller[1], __entry->caller[2], | 187 | __entry->caller[0], __entry->caller[1], __entry->caller[2], |
181 | __entry->caller[3], __entry->caller[4], __entry->caller[5], | 188 | __entry->caller[3], __entry->caller[4], __entry->caller[5], |
182 | __entry->caller[6], __entry->caller[7]), | 189 | __entry->caller[6], __entry->caller[7]), |
@@ -193,8 +200,9 @@ FTRACE_ENTRY(user_stack, userstack_entry, | |||
193 | __array( unsigned long, caller, FTRACE_STACK_ENTRIES ) | 200 | __array( unsigned long, caller, FTRACE_STACK_ENTRIES ) |
194 | ), | 201 | ), |
195 | 202 | ||
196 | F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n" | 203 | F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" |
197 | "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n", | 204 | "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" |
205 | "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n", | ||
198 | __entry->caller[0], __entry->caller[1], __entry->caller[2], | 206 | __entry->caller[0], __entry->caller[1], __entry->caller[2], |
199 | __entry->caller[3], __entry->caller[4], __entry->caller[5], | 207 | __entry->caller[3], __entry->caller[4], __entry->caller[5], |
200 | __entry->caller[6], __entry->caller[7]), | 208 | __entry->caller[6], __entry->caller[7]), |
diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index 7b46c9bd22ae..3dd15e8bc856 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c | |||
@@ -162,7 +162,7 @@ ftrace_define_fields_##name(struct ftrace_event_call *event_call) \ | |||
162 | #define __dynamic_array(type, item) | 162 | #define __dynamic_array(type, item) |
163 | 163 | ||
164 | #undef F_printk | 164 | #undef F_printk |
165 | #define F_printk(fmt, args...) #fmt ", " __stringify(args) | 165 | #define F_printk(fmt, args...) __stringify(fmt) ", " __stringify(args) |
166 | 166 | ||
167 | #undef FTRACE_ENTRY_REG | 167 | #undef FTRACE_ENTRY_REG |
168 | #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\ | 168 | #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\ |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 87feeee8b90c..2d89f02719b5 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -48,6 +48,9 @@ OPTIONS | |||
48 | Only consider these symbols. CSV that understands | 48 | Only consider these symbols. CSV that understands |
49 | file://filename entries. | 49 | file://filename entries. |
50 | 50 | ||
51 | --symbol-filter=:: | ||
52 | Only show symbols that match (partially) with this filter. | ||
53 | |||
51 | -U:: | 54 | -U:: |
52 | --hide-unresolved:: | 55 | --hide-unresolved:: |
53 | Only display entries resolved to a symbol. | 56 | Only display entries resolved to a symbol. |
@@ -110,6 +113,8 @@ OPTIONS | |||
110 | requires a tty, if one is not present, as when piping to other | 113 | requires a tty, if one is not present, as when piping to other |
111 | commands, the stdio interface is used. | 114 | commands, the stdio interface is used. |
112 | 115 | ||
116 | --gtk:: Use the GTK2 interface. | ||
117 | |||
113 | -k:: | 118 | -k:: |
114 | --vmlinux=<file>:: | 119 | --vmlinux=<file>:: |
115 | vmlinux pathname | 120 | vmlinux pathname |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 74fd7f89208a..820371f10d1b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -182,7 +182,7 @@ endif | |||
182 | 182 | ||
183 | ### --- END CONFIGURATION SECTION --- | 183 | ### --- END CONFIGURATION SECTION --- |
184 | 184 | ||
185 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 185 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
186 | BASIC_LDFLAGS = | 186 | BASIC_LDFLAGS = |
187 | 187 | ||
188 | # Guard against environment variables | 188 | # Guard against environment variables |
@@ -234,6 +234,25 @@ endif | |||
234 | 234 | ||
235 | export PERL_PATH | 235 | export PERL_PATH |
236 | 236 | ||
237 | FLEX = $(CROSS_COMPILE)flex | ||
238 | BISON= $(CROSS_COMPILE)bison | ||
239 | |||
240 | event-parser: | ||
241 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c | ||
242 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c | ||
243 | |||
244 | $(OUTPUT)util/parse-events-flex.c: event-parser | ||
245 | $(OUTPUT)util/parse-events-bison.c: event-parser | ||
246 | |||
247 | pmu-parser: | ||
248 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c | ||
249 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c | ||
250 | |||
251 | $(OUTPUT)util/pmu-flex.c: pmu-parser | ||
252 | $(OUTPUT)util/pmu-bison.c: pmu-parser | ||
253 | |||
254 | $(OUTPUT)util/parse-events.o: event-parser pmu-parser | ||
255 | |||
237 | LIB_FILE=$(OUTPUT)libperf.a | 256 | LIB_FILE=$(OUTPUT)libperf.a |
238 | 257 | ||
239 | LIB_H += ../../include/linux/perf_event.h | 258 | LIB_H += ../../include/linux/perf_event.h |
@@ -249,7 +268,7 @@ LIB_H += util/include/linux/const.h | |||
249 | LIB_H += util/include/linux/ctype.h | 268 | LIB_H += util/include/linux/ctype.h |
250 | LIB_H += util/include/linux/kernel.h | 269 | LIB_H += util/include/linux/kernel.h |
251 | LIB_H += util/include/linux/list.h | 270 | LIB_H += util/include/linux/list.h |
252 | LIB_H += util/include/linux/module.h | 271 | LIB_H += util/include/linux/export.h |
253 | LIB_H += util/include/linux/poison.h | 272 | LIB_H += util/include/linux/poison.h |
254 | LIB_H += util/include/linux/prefetch.h | 273 | LIB_H += util/include/linux/prefetch.h |
255 | LIB_H += util/include/linux/rbtree.h | 274 | LIB_H += util/include/linux/rbtree.h |
@@ -276,6 +295,7 @@ LIB_H += util/build-id.h | |||
276 | LIB_H += util/debug.h | 295 | LIB_H += util/debug.h |
277 | LIB_H += util/debugfs.h | 296 | LIB_H += util/debugfs.h |
278 | LIB_H += util/sysfs.h | 297 | LIB_H += util/sysfs.h |
298 | LIB_H += util/pmu.h | ||
279 | LIB_H += util/event.h | 299 | LIB_H += util/event.h |
280 | LIB_H += util/evsel.h | 300 | LIB_H += util/evsel.h |
281 | LIB_H += util/evlist.h | 301 | LIB_H += util/evlist.h |
@@ -323,6 +343,7 @@ LIB_OBJS += $(OUTPUT)util/config.o | |||
323 | LIB_OBJS += $(OUTPUT)util/ctype.o | 343 | LIB_OBJS += $(OUTPUT)util/ctype.o |
324 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 344 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
325 | LIB_OBJS += $(OUTPUT)util/sysfs.o | 345 | LIB_OBJS += $(OUTPUT)util/sysfs.o |
346 | LIB_OBJS += $(OUTPUT)util/pmu.o | ||
326 | LIB_OBJS += $(OUTPUT)util/environment.o | 347 | LIB_OBJS += $(OUTPUT)util/environment.o |
327 | LIB_OBJS += $(OUTPUT)util/event.o | 348 | LIB_OBJS += $(OUTPUT)util/event.o |
328 | LIB_OBJS += $(OUTPUT)util/evlist.o | 349 | LIB_OBJS += $(OUTPUT)util/evlist.o |
@@ -359,6 +380,10 @@ LIB_OBJS += $(OUTPUT)util/session.o | |||
359 | LIB_OBJS += $(OUTPUT)util/thread.o | 380 | LIB_OBJS += $(OUTPUT)util/thread.o |
360 | LIB_OBJS += $(OUTPUT)util/thread_map.o | 381 | LIB_OBJS += $(OUTPUT)util/thread_map.o |
361 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 382 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
383 | LIB_OBJS += $(OUTPUT)util/parse-events-flex.o | ||
384 | LIB_OBJS += $(OUTPUT)util/parse-events-bison.o | ||
385 | LIB_OBJS += $(OUTPUT)util/pmu-flex.o | ||
386 | LIB_OBJS += $(OUTPUT)util/pmu-bison.o | ||
362 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 387 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
363 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 388 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
364 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o | 389 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o |
@@ -501,6 +526,20 @@ else | |||
501 | endif | 526 | endif |
502 | endif | 527 | endif |
503 | 528 | ||
529 | ifdef NO_GTK2 | ||
530 | BASIC_CFLAGS += -DNO_GTK2 | ||
531 | else | ||
532 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0) | ||
533 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) | ||
534 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | ||
535 | BASIC_CFLAGS += -DNO_GTK2_SUPPORT | ||
536 | else | ||
537 | BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0) | ||
538 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0) | ||
539 | LIB_OBJS += $(OUTPUT)util/gtk/browser.o | ||
540 | endif | ||
541 | endif | ||
542 | |||
504 | ifdef NO_LIBPERL | 543 | ifdef NO_LIBPERL |
505 | BASIC_CFLAGS += -DNO_LIBPERL | 544 | BASIC_CFLAGS += -DNO_LIBPERL |
506 | else | 545 | else |
@@ -647,6 +686,8 @@ ifndef V | |||
647 | QUIET_LINK = @echo ' ' LINK $@; | 686 | QUIET_LINK = @echo ' ' LINK $@; |
648 | QUIET_MKDIR = @echo ' ' MKDIR $@; | 687 | QUIET_MKDIR = @echo ' ' MKDIR $@; |
649 | QUIET_GEN = @echo ' ' GEN $@; | 688 | QUIET_GEN = @echo ' ' GEN $@; |
689 | QUIET_FLEX = @echo ' ' FLEX $@; | ||
690 | QUIET_BISON = @echo ' ' BISON $@; | ||
650 | endif | 691 | endif |
651 | endif | 692 | endif |
652 | 693 | ||
@@ -727,12 +768,28 @@ $(OUTPUT)perf.o perf.spec \ | |||
727 | $(SCRIPTS) \ | 768 | $(SCRIPTS) \ |
728 | : $(OUTPUT)PERF-VERSION-FILE | 769 | : $(OUTPUT)PERF-VERSION-FILE |
729 | 770 | ||
771 | .SUFFIXES: | ||
772 | .SUFFIXES: .o .c .S .s | ||
773 | |||
774 | # These two need to be here so that when O= is not used they take precedence | ||
775 | # over the general rule for .o | ||
776 | |||
777 | $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS | ||
778 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< | ||
779 | |||
780 | $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS | ||
781 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< | ||
782 | |||
730 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS | 783 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS |
731 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 784 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
785 | $(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS | ||
786 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | ||
732 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS | 787 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS |
733 | $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< | 788 | $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $< |
734 | $(OUTPUT)%.o: %.S | 789 | $(OUTPUT)%.o: %.S |
735 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 790 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
791 | $(OUTPUT)%.s: %.S | ||
792 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | ||
736 | 793 | ||
737 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 794 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
738 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 795 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
@@ -795,6 +852,8 @@ help: | |||
795 | @echo ' html - make html documentation' | 852 | @echo ' html - make html documentation' |
796 | @echo ' info - make GNU info documentation (access with info <foo>)' | 853 | @echo ' info - make GNU info documentation (access with info <foo>)' |
797 | @echo ' pdf - make pdf documentation' | 854 | @echo ' pdf - make pdf documentation' |
855 | @echo ' event-parser - make event parser code' | ||
856 | @echo ' pmu-parser - make pmu format parser code' | ||
798 | @echo ' TAGS - use etags to make tag information for source browsing' | 857 | @echo ' TAGS - use etags to make tag information for source browsing' |
799 | @echo ' tags - use ctags to make tag information for source browsing' | 858 | @echo ' tags - use ctags to make tag information for source browsing' |
800 | @echo ' cscope - use cscope to make interactive browsing database' | 859 | @echo ' cscope - use cscope to make interactive browsing database' |
@@ -931,6 +990,7 @@ clean: | |||
931 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 990 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
932 | $(MAKE) -C Documentation/ clean | 991 | $(MAKE) -C Documentation/ clean |
933 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS | 992 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
993 | $(RM) $(OUTPUT)util/*-{bison,flex}* | ||
934 | $(python-clean) | 994 | $(python-clean) |
935 | 995 | ||
936 | .PHONY: all install clean strip | 996 | .PHONY: all install clean strip |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4f19513d7dda..d29d350fb2b7 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -24,6 +24,11 @@ static char diff__default_sort_order[] = "dso,symbol"; | |||
24 | static bool force; | 24 | static bool force; |
25 | static bool show_displacement; | 25 | static bool show_displacement; |
26 | 26 | ||
27 | struct perf_diff { | ||
28 | struct perf_tool tool; | ||
29 | struct perf_session *session; | ||
30 | }; | ||
31 | |||
27 | static int hists__add_entry(struct hists *self, | 32 | static int hists__add_entry(struct hists *self, |
28 | struct addr_location *al, u64 period) | 33 | struct addr_location *al, u64 period) |
29 | { | 34 | { |
@@ -32,12 +37,14 @@ static int hists__add_entry(struct hists *self, | |||
32 | return -ENOMEM; | 37 | return -ENOMEM; |
33 | } | 38 | } |
34 | 39 | ||
35 | static int diff__process_sample_event(struct perf_tool *tool __used, | 40 | static int diff__process_sample_event(struct perf_tool *tool, |
36 | union perf_event *event, | 41 | union perf_event *event, |
37 | struct perf_sample *sample, | 42 | struct perf_sample *sample, |
38 | struct perf_evsel *evsel __used, | 43 | struct perf_evsel *evsel __used, |
39 | struct machine *machine) | 44 | struct machine *machine) |
40 | { | 45 | { |
46 | struct perf_diff *_diff = container_of(tool, struct perf_diff, tool); | ||
47 | struct perf_session *session = _diff->session; | ||
41 | struct addr_location al; | 48 | struct addr_location al; |
42 | 49 | ||
43 | if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { | 50 | if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { |
@@ -49,24 +56,26 @@ static int diff__process_sample_event(struct perf_tool *tool __used, | |||
49 | if (al.filtered || al.sym == NULL) | 56 | if (al.filtered || al.sym == NULL) |
50 | return 0; | 57 | return 0; |
51 | 58 | ||
52 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 59 | if (hists__add_entry(&session->hists, &al, sample->period)) { |
53 | pr_warning("problem incrementing symbol period, skipping event\n"); | 60 | pr_warning("problem incrementing symbol period, skipping event\n"); |
54 | return -1; | 61 | return -1; |
55 | } | 62 | } |
56 | 63 | ||
57 | evsel->hists.stats.total_period += sample->period; | 64 | session->hists.stats.total_period += sample->period; |
58 | return 0; | 65 | return 0; |
59 | } | 66 | } |
60 | 67 | ||
61 | static struct perf_tool perf_diff = { | 68 | static struct perf_diff diff = { |
62 | .sample = diff__process_sample_event, | 69 | .tool = { |
63 | .mmap = perf_event__process_mmap, | 70 | .sample = diff__process_sample_event, |
64 | .comm = perf_event__process_comm, | 71 | .mmap = perf_event__process_mmap, |
65 | .exit = perf_event__process_task, | 72 | .comm = perf_event__process_comm, |
66 | .fork = perf_event__process_task, | 73 | .exit = perf_event__process_task, |
67 | .lost = perf_event__process_lost, | 74 | .fork = perf_event__process_task, |
68 | .ordered_samples = true, | 75 | .lost = perf_event__process_lost, |
69 | .ordering_requires_timestamps = true, | 76 | .ordered_samples = true, |
77 | .ordering_requires_timestamps = true, | ||
78 | }, | ||
70 | }; | 79 | }; |
71 | 80 | ||
72 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | 81 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, |
@@ -107,12 +116,6 @@ static void hists__resort_entries(struct hists *self) | |||
107 | self->entries = tmp; | 116 | self->entries = tmp; |
108 | } | 117 | } |
109 | 118 | ||
110 | static void hists__set_positions(struct hists *self) | ||
111 | { | ||
112 | hists__output_resort(self); | ||
113 | hists__resort_entries(self); | ||
114 | } | ||
115 | |||
116 | static struct hist_entry *hists__find_entry(struct hists *self, | 119 | static struct hist_entry *hists__find_entry(struct hists *self, |
117 | struct hist_entry *he) | 120 | struct hist_entry *he) |
118 | { | 121 | { |
@@ -146,30 +149,37 @@ static void hists__match(struct hists *older, struct hists *newer) | |||
146 | static int __cmd_diff(void) | 149 | static int __cmd_diff(void) |
147 | { | 150 | { |
148 | int ret, i; | 151 | int ret, i; |
152 | #define older (session[0]) | ||
153 | #define newer (session[1]) | ||
149 | struct perf_session *session[2]; | 154 | struct perf_session *session[2]; |
150 | 155 | ||
151 | session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff); | 156 | older = perf_session__new(input_old, O_RDONLY, force, false, |
152 | session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff); | 157 | &diff.tool); |
158 | newer = perf_session__new(input_new, O_RDONLY, force, false, | ||
159 | &diff.tool); | ||
153 | if (session[0] == NULL || session[1] == NULL) | 160 | if (session[0] == NULL || session[1] == NULL) |
154 | return -ENOMEM; | 161 | return -ENOMEM; |
155 | 162 | ||
156 | for (i = 0; i < 2; ++i) { | 163 | for (i = 0; i < 2; ++i) { |
157 | ret = perf_session__process_events(session[i], &perf_diff); | 164 | diff.session = session[i]; |
165 | ret = perf_session__process_events(session[i], &diff.tool); | ||
158 | if (ret) | 166 | if (ret) |
159 | goto out_delete; | 167 | goto out_delete; |
168 | hists__output_resort(&session[i]->hists); | ||
160 | } | 169 | } |
161 | 170 | ||
162 | hists__output_resort(&session[1]->hists); | ||
163 | if (show_displacement) | 171 | if (show_displacement) |
164 | hists__set_positions(&session[0]->hists); | 172 | hists__resort_entries(&older->hists); |
165 | 173 | ||
166 | hists__match(&session[0]->hists, &session[1]->hists); | 174 | hists__match(&older->hists, &newer->hists); |
167 | hists__fprintf(&session[1]->hists, &session[0]->hists, | 175 | hists__fprintf(&newer->hists, &older->hists, |
168 | show_displacement, true, 0, 0, stdout); | 176 | show_displacement, true, 0, 0, stdout); |
169 | out_delete: | 177 | out_delete: |
170 | for (i = 0; i < 2; ++i) | 178 | for (i = 0; i < 2; ++i) |
171 | perf_session__delete(session[i]); | 179 | perf_session__delete(session[i]); |
172 | return ret; | 180 | return ret; |
181 | #undef older | ||
182 | #undef newer | ||
173 | } | 183 | } |
174 | 184 | ||
175 | static const char * const diff_usage[] = { | 185 | static const char * const diff_usage[] = { |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8e91c6eba18a..2e317438980b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -40,7 +40,7 @@ struct perf_report { | |||
40 | struct perf_tool tool; | 40 | struct perf_tool tool; |
41 | struct perf_session *session; | 41 | struct perf_session *session; |
42 | char const *input_name; | 42 | char const *input_name; |
43 | bool force, use_tui, use_stdio; | 43 | bool force, use_tui, use_gtk, use_stdio; |
44 | bool hide_unresolved; | 44 | bool hide_unresolved; |
45 | bool dont_use_callchains; | 45 | bool dont_use_callchains; |
46 | bool show_full_info; | 46 | bool show_full_info; |
@@ -50,6 +50,7 @@ struct perf_report { | |||
50 | const char *pretty_printing_style; | 50 | const char *pretty_printing_style; |
51 | symbol_filter_t annotate_init; | 51 | symbol_filter_t annotate_init; |
52 | const char *cpu_list; | 52 | const char *cpu_list; |
53 | const char *symbol_filter_str; | ||
53 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 54 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
54 | }; | 55 | }; |
55 | 56 | ||
@@ -400,6 +401,9 @@ static int __cmd_report(struct perf_report *rep) | |||
400 | list_for_each_entry(pos, &session->evlist->entries, node) { | 401 | list_for_each_entry(pos, &session->evlist->entries, node) { |
401 | struct hists *hists = &pos->hists; | 402 | struct hists *hists = &pos->hists; |
402 | 403 | ||
404 | if (pos->idx == 0) | ||
405 | hists->symbol_filter_str = rep->symbol_filter_str; | ||
406 | |||
403 | hists__collapse_resort(hists); | 407 | hists__collapse_resort(hists); |
404 | hists__output_resort(hists); | 408 | hists__output_resort(hists); |
405 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 409 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
@@ -411,8 +415,13 @@ static int __cmd_report(struct perf_report *rep) | |||
411 | } | 415 | } |
412 | 416 | ||
413 | if (use_browser > 0) { | 417 | if (use_browser > 0) { |
414 | perf_evlist__tui_browse_hists(session->evlist, help, | 418 | if (use_browser == 1) { |
415 | NULL, NULL, 0); | 419 | perf_evlist__tui_browse_hists(session->evlist, help, |
420 | NULL, NULL, 0); | ||
421 | } else if (use_browser == 2) { | ||
422 | perf_evlist__gtk_browse_hists(session->evlist, help, | ||
423 | NULL, NULL, 0); | ||
424 | } | ||
416 | } else | 425 | } else |
417 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 426 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
418 | 427 | ||
@@ -569,6 +578,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
569 | OPT_STRING(0, "pretty", &report.pretty_printing_style, "key", | 578 | OPT_STRING(0, "pretty", &report.pretty_printing_style, "key", |
570 | "pretty printing style key: normal raw"), | 579 | "pretty printing style key: normal raw"), |
571 | OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"), | 580 | OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"), |
581 | OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"), | ||
572 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, | 582 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, |
573 | "Use the stdio interface"), | 583 | "Use the stdio interface"), |
574 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 584 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
@@ -591,6 +601,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
591 | "only consider symbols in these comms"), | 601 | "only consider symbols in these comms"), |
592 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 602 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
593 | "only consider these symbols"), | 603 | "only consider these symbols"), |
604 | OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter", | ||
605 | "only show symbols that (partially) match with this filter"), | ||
594 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | 606 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, |
595 | "width[,width...]", | 607 | "width[,width...]", |
596 | "don't try to adjust column width, use these fixed values"), | 608 | "don't try to adjust column width, use these fixed values"), |
@@ -624,6 +636,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
624 | use_browser = 0; | 636 | use_browser = 0; |
625 | else if (report.use_tui) | 637 | else if (report.use_tui) |
626 | use_browser = 1; | 638 | use_browser = 1; |
639 | else if (report.use_gtk) | ||
640 | use_browser = 2; | ||
627 | 641 | ||
628 | if (report.inverted_callchain) | 642 | if (report.inverted_callchain) |
629 | callchain_param.order = ORDER_CALLER; | 643 | callchain_param.order = ORDER_CALLER; |
@@ -660,7 +674,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
660 | } | 674 | } |
661 | 675 | ||
662 | if (strcmp(report.input_name, "-") != 0) { | 676 | if (strcmp(report.input_name, "-") != 0) { |
663 | setup_browser(true); | 677 | if (report.use_gtk) |
678 | perf_gtk_setup_browser(argc, argv, true); | ||
679 | else | ||
680 | setup_browser(true); | ||
664 | } else { | 681 | } else { |
665 | use_browser = 0; | 682 | use_browser = 0; |
666 | } | 683 | } |
@@ -709,11 +726,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
709 | } else | 726 | } else |
710 | symbol_conf.exclude_other = false; | 727 | symbol_conf.exclude_other = false; |
711 | 728 | ||
712 | /* | 729 | if (argc) { |
713 | * Any (unrecognized) arguments left? | 730 | /* |
714 | */ | 731 | * Special case: if there's an argument left then assume that |
715 | if (argc) | 732 | * it's a symbol filter: |
716 | usage_with_options(report_usage, options); | 733 | */ |
734 | if (argc > 1) | ||
735 | usage_with_options(report_usage, options); | ||
736 | |||
737 | report.symbol_filter_str = argv[0]; | ||
738 | } | ||
717 | 739 | ||
718 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | 740 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); |
719 | 741 | ||
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index ea40e4e8b227..c941bb640f49 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, | |||
296 | if (system_wide) | 296 | if (system_wide) |
297 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, | 297 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, |
298 | group, group_fd); | 298 | group, group_fd); |
299 | if (!target_pid && !target_tid) { | 299 | if (!target_pid && !target_tid && (!group || evsel == first)) { |
300 | attr->disabled = 1; | 300 | attr->disabled = 1; |
301 | attr->enable_on_exec = 1; | 301 | attr->enable_on_exec = 1; |
302 | } | 302 | } |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 3e087ce8daa6..1c5b9801ac61 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "util/parse-events.h" | 13 | #include "util/parse-events.h" |
14 | #include "util/symbol.h" | 14 | #include "util/symbol.h" |
15 | #include "util/thread_map.h" | 15 | #include "util/thread_map.h" |
16 | #include "util/pmu.h" | ||
16 | #include "../../include/linux/hw_breakpoint.h" | 17 | #include "../../include/linux/hw_breakpoint.h" |
17 | 18 | ||
18 | #include <sys/mman.h> | 19 | #include <sys/mman.h> |
@@ -650,7 +651,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist) | |||
650 | 651 | ||
651 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 652 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
652 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 653 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
653 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | 654 | TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config); |
654 | return 0; | 655 | return 0; |
655 | } | 656 | } |
656 | 657 | ||
@@ -677,6 +678,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | |||
677 | return 0; | 678 | return 0; |
678 | } | 679 | } |
679 | 680 | ||
681 | static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) | ||
682 | { | ||
683 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
684 | struct perf_evsel, node); | ||
685 | |||
686 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
687 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
688 | TEST_ASSERT_VAL("wrong config", | ||
689 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
690 | TEST_ASSERT_VAL("wrong period", | ||
691 | 100000 == evsel->attr.sample_period); | ||
692 | TEST_ASSERT_VAL("wrong config1", | ||
693 | 0 == evsel->attr.config1); | ||
694 | TEST_ASSERT_VAL("wrong config2", | ||
695 | 1 == evsel->attr.config2); | ||
696 | return 0; | ||
697 | } | ||
698 | |||
680 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | 699 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) |
681 | { | 700 | { |
682 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 701 | struct perf_evsel *evsel = list_entry(evlist->entries.next, |
@@ -858,6 +877,115 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) | |||
858 | return test__checkevent_genhw(evlist); | 877 | return test__checkevent_genhw(evlist); |
859 | } | 878 | } |
860 | 879 | ||
880 | static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) | ||
881 | { | ||
882 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
883 | struct perf_evsel, node); | ||
884 | |||
885 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
886 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
887 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
888 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
889 | |||
890 | return test__checkevent_breakpoint(evlist); | ||
891 | } | ||
892 | |||
893 | static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) | ||
894 | { | ||
895 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
896 | struct perf_evsel, node); | ||
897 | |||
898 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
899 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
900 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
901 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
902 | |||
903 | return test__checkevent_breakpoint_x(evlist); | ||
904 | } | ||
905 | |||
906 | static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) | ||
907 | { | ||
908 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
909 | struct perf_evsel, node); | ||
910 | |||
911 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
912 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
913 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
914 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
915 | |||
916 | return test__checkevent_breakpoint_r(evlist); | ||
917 | } | ||
918 | |||
919 | static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) | ||
920 | { | ||
921 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
922 | struct perf_evsel, node); | ||
923 | |||
924 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
925 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
926 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
927 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
928 | |||
929 | return test__checkevent_breakpoint_w(evlist); | ||
930 | } | ||
931 | |||
932 | static int test__checkevent_pmu(struct perf_evlist *evlist) | ||
933 | { | ||
934 | |||
935 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
936 | struct perf_evsel, node); | ||
937 | |||
938 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
939 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
940 | TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config); | ||
941 | TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1); | ||
942 | TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2); | ||
943 | TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period); | ||
944 | |||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static int test__checkevent_list(struct perf_evlist *evlist) | ||
949 | { | ||
950 | struct perf_evsel *evsel; | ||
951 | |||
952 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
953 | |||
954 | /* r1 */ | ||
955 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
956 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
957 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
958 | TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); | ||
959 | TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2); | ||
960 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
961 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
962 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
963 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
964 | |||
965 | /* syscalls:sys_enter_open:k */ | ||
966 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | ||
967 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
968 | TEST_ASSERT_VAL("wrong sample_type", | ||
969 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == | ||
970 | evsel->attr.sample_type); | ||
971 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
972 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
973 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
974 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
975 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
976 | |||
977 | /* 1:1:hp */ | ||
978 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | ||
979 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
980 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
981 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
982 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
983 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
984 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
985 | |||
986 | return 0; | ||
987 | } | ||
988 | |||
861 | static struct test__event_st { | 989 | static struct test__event_st { |
862 | const char *name; | 990 | const char *name; |
863 | __u32 type; | 991 | __u32 type; |
@@ -872,7 +1000,7 @@ static struct test__event_st { | |||
872 | .check = test__checkevent_tracepoint_multi, | 1000 | .check = test__checkevent_tracepoint_multi, |
873 | }, | 1001 | }, |
874 | { | 1002 | { |
875 | .name = "r1", | 1003 | .name = "r1a", |
876 | .check = test__checkevent_raw, | 1004 | .check = test__checkevent_raw, |
877 | }, | 1005 | }, |
878 | { | 1006 | { |
@@ -884,6 +1012,10 @@ static struct test__event_st { | |||
884 | .check = test__checkevent_symbolic_name, | 1012 | .check = test__checkevent_symbolic_name, |
885 | }, | 1013 | }, |
886 | { | 1014 | { |
1015 | .name = "cycles/period=100000,config2/", | ||
1016 | .check = test__checkevent_symbolic_name_config, | ||
1017 | }, | ||
1018 | { | ||
887 | .name = "faults", | 1019 | .name = "faults", |
888 | .check = test__checkevent_symbolic_alias, | 1020 | .check = test__checkevent_symbolic_alias, |
889 | }, | 1021 | }, |
@@ -916,7 +1048,7 @@ static struct test__event_st { | |||
916 | .check = test__checkevent_tracepoint_multi_modifier, | 1048 | .check = test__checkevent_tracepoint_multi_modifier, |
917 | }, | 1049 | }, |
918 | { | 1050 | { |
919 | .name = "r1:kp", | 1051 | .name = "r1a:kp", |
920 | .check = test__checkevent_raw_modifier, | 1052 | .check = test__checkevent_raw_modifier, |
921 | }, | 1053 | }, |
922 | { | 1054 | { |
@@ -935,6 +1067,30 @@ static struct test__event_st { | |||
935 | .name = "L1-dcache-load-miss:kp", | 1067 | .name = "L1-dcache-load-miss:kp", |
936 | .check = test__checkevent_genhw_modifier, | 1068 | .check = test__checkevent_genhw_modifier, |
937 | }, | 1069 | }, |
1070 | { | ||
1071 | .name = "mem:0:u", | ||
1072 | .check = test__checkevent_breakpoint_modifier, | ||
1073 | }, | ||
1074 | { | ||
1075 | .name = "mem:0:x:k", | ||
1076 | .check = test__checkevent_breakpoint_x_modifier, | ||
1077 | }, | ||
1078 | { | ||
1079 | .name = "mem:0:r:hp", | ||
1080 | .check = test__checkevent_breakpoint_r_modifier, | ||
1081 | }, | ||
1082 | { | ||
1083 | .name = "mem:0:w:up", | ||
1084 | .check = test__checkevent_breakpoint_w_modifier, | ||
1085 | }, | ||
1086 | { | ||
1087 | .name = "cpu/config=10,config1,config2=3,period=1000/u", | ||
1088 | .check = test__checkevent_pmu, | ||
1089 | }, | ||
1090 | { | ||
1091 | .name = "r1,syscalls:sys_enter_open:k,1:1:hp", | ||
1092 | .check = test__checkevent_list, | ||
1093 | }, | ||
938 | }; | 1094 | }; |
939 | 1095 | ||
940 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) | 1096 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) |
@@ -960,10 +1116,9 @@ static int test__parse_events(void) | |||
960 | } | 1116 | } |
961 | 1117 | ||
962 | ret = e->check(evlist); | 1118 | ret = e->check(evlist); |
1119 | perf_evlist__delete(evlist); | ||
963 | if (ret) | 1120 | if (ret) |
964 | break; | 1121 | break; |
965 | |||
966 | perf_evlist__delete(evlist); | ||
967 | } | 1122 | } |
968 | 1123 | ||
969 | return ret; | 1124 | return ret; |
@@ -1462,6 +1617,11 @@ static int test__rdpmc(void) | |||
1462 | 1617 | ||
1463 | #endif | 1618 | #endif |
1464 | 1619 | ||
1620 | static int test__perf_pmu(void) | ||
1621 | { | ||
1622 | return perf_pmu__test(); | ||
1623 | } | ||
1624 | |||
1465 | static struct test { | 1625 | static struct test { |
1466 | const char *desc; | 1626 | const char *desc; |
1467 | int (*func)(void); | 1627 | int (*func)(void); |
@@ -1497,6 +1657,10 @@ static struct test { | |||
1497 | .func = test__PERF_RECORD, | 1657 | .func = test__PERF_RECORD, |
1498 | }, | 1658 | }, |
1499 | { | 1659 | { |
1660 | .desc = "Test perf pmu format parsing", | ||
1661 | .func = test__perf_pmu, | ||
1662 | }, | ||
1663 | { | ||
1500 | .func = NULL, | 1664 | .func = NULL, |
1501 | }, | 1665 | }, |
1502 | }; | 1666 | }; |
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index 6170fd2531b5..d9084e03ce56 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -65,6 +65,21 @@ int main(void) | |||
65 | endef | 65 | endef |
66 | endif | 66 | endif |
67 | 67 | ||
68 | ifndef NO_GTK2 | ||
69 | define SOURCE_GTK2 | ||
70 | #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" | ||
71 | #include <gtk/gtk.h> | ||
72 | #pragma GCC diagnostic error \"-Wstrict-prototypes\" | ||
73 | |||
74 | int main(int argc, char *argv[]) | ||
75 | { | ||
76 | gtk_init(&argc, &argv); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | endef | ||
81 | endif | ||
82 | |||
68 | ifndef NO_LIBPERL | 83 | ifndef NO_LIBPERL |
69 | define SOURCE_PERL_EMBED | 84 | define SOURCE_PERL_EMBED |
70 | #include <EXTERN.h> | 85 | #include <EXTERN.h> |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e5a462f1d07c..199f69ec656f 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -28,8 +28,8 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) | |||
28 | int symbol__alloc_hist(struct symbol *sym) | 28 | int symbol__alloc_hist(struct symbol *sym) |
29 | { | 29 | { |
30 | struct annotation *notes = symbol__annotation(sym); | 30 | struct annotation *notes = symbol__annotation(sym); |
31 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + | 31 | const size_t size = sym->end - sym->start + 1; |
32 | (sym->end - sym->start) * sizeof(u64)); | 32 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); |
33 | 33 | ||
34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); | 34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); |
35 | if (notes->src == NULL) | 35 | if (notes->src == NULL) |
@@ -64,7 +64,7 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
64 | 64 | ||
65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
66 | 66 | ||
67 | if (addr >= sym->end) | 67 | if (addr > sym->end) |
68 | return 0; | 68 | return 0; |
69 | 69 | ||
70 | offset = addr - sym->start; | 70 | offset = addr - sym->start; |
@@ -408,7 +408,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
408 | if (!notes->src->lines) | 408 | if (!notes->src->lines) |
409 | return -1; | 409 | return -1; |
410 | 410 | ||
411 | start = map->unmap_ip(map, sym->start); | 411 | start = map__rip_2objdump(map, sym->start); |
412 | 412 | ||
413 | for (i = 0; i < len; i++) { | 413 | for (i = 0; i < len; i++) { |
414 | char *path = NULL; | 414 | char *path = NULL; |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index fc5e5a09d5b9..8dd224df3e54 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager); | |||
45 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | #ifdef NO_GTK2_SUPPORT | ||
49 | static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager) | ||
50 | { | ||
51 | if (fallback_to_pager) | ||
52 | setup_pager(); | ||
53 | } | ||
54 | static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {} | ||
55 | #else | ||
56 | void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager); | ||
57 | void perf_gtk_exit_browser(bool wait_for_ok); | ||
58 | #endif | ||
59 | |||
48 | char *alias_lookup(const char *alias); | 60 | char *alias_lookup(const char *alias); |
49 | int split_cmdline(char *cmdline, const char ***argv); | 61 | int split_cmdline(char *cmdline, const char ***argv); |
50 | 62 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 159263d17c2d..1986d8051bd1 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
52 | struct perf_record_opts *opts) | 52 | struct perf_record_opts *opts) |
53 | { | 53 | { |
54 | struct perf_evsel *evsel; | 54 | struct perf_evsel *evsel, *first; |
55 | 55 | ||
56 | if (evlist->cpus->map[0] < 0) | 56 | if (evlist->cpus->map[0] < 0) |
57 | opts->no_inherit = true; | 57 | opts->no_inherit = true; |
58 | 58 | ||
59 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
60 | |||
59 | list_for_each_entry(evsel, &evlist->entries, node) { | 61 | list_for_each_entry(evsel, &evlist->entries, node) { |
60 | perf_evsel__config(evsel, opts); | 62 | perf_evsel__config(evsel, opts, first); |
61 | 63 | ||
62 | if (evlist->nr_entries > 1) | 64 | if (evlist->nr_entries > 1) |
63 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 65 | evsel->attr.sample_type |= PERF_SAMPLE_ID; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f421f7cbc0d3..8c13dbcb84b9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type) | |||
34 | return size; | 34 | return size; |
35 | } | 35 | } |
36 | 36 | ||
37 | static void hists__init(struct hists *hists) | 37 | void hists__init(struct hists *hists) |
38 | { | 38 | { |
39 | memset(hists, 0, sizeof(*hists)); | 39 | memset(hists, 0, sizeof(*hists)); |
40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | 40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; |
@@ -63,7 +63,8 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | |||
63 | return evsel; | 63 | return evsel; |
64 | } | 64 | } |
65 | 65 | ||
66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | 66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, |
67 | struct perf_evsel *first) | ||
67 | { | 68 | { |
68 | struct perf_event_attr *attr = &evsel->attr; | 69 | struct perf_event_attr *attr = &evsel->attr; |
69 | int track = !evsel->idx; /* only the first counter needs these */ | 70 | int track = !evsel->idx; /* only the first counter needs these */ |
@@ -134,7 +135,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
134 | attr->mmap = track; | 135 | attr->mmap = track; |
135 | attr->comm = track; | 136 | attr->comm = track; |
136 | 137 | ||
137 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide) { | 138 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide && |
139 | (!opts->group || evsel == first)) { | ||
138 | attr->disabled = 1; | 140 | attr->disabled = 1; |
139 | attr->enable_on_exec = 1; | 141 | attr->enable_on_exec = 1; |
140 | } | 142 | } |
@@ -578,6 +580,8 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
578 | return -EFAULT; | 580 | return -EFAULT; |
579 | 581 | ||
580 | data->raw_data = (void *) pdata; | 582 | data->raw_data = (void *) pdata; |
583 | |||
584 | array = (void *)array + data->raw_size + sizeof(u32); | ||
581 | } | 585 | } |
582 | 586 | ||
583 | if (type & PERF_SAMPLE_BRANCH_STACK) { | 587 | if (type & PERF_SAMPLE_BRANCH_STACK) { |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 326b8e4d5035..3d6b3e4cb66b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel); | |||
80 | void perf_evsel__delete(struct perf_evsel *evsel); | 80 | void perf_evsel__delete(struct perf_evsel *evsel); |
81 | 81 | ||
82 | void perf_evsel__config(struct perf_evsel *evsel, | 82 | void perf_evsel__config(struct perf_evsel *evsel, |
83 | struct perf_record_opts *opts); | 83 | struct perf_record_opts *opts, |
84 | struct perf_evsel *first); | ||
84 | 85 | ||
85 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 86 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
86 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 87 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
@@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel) | |||
169 | return __perf_evsel__sample_size(evsel->attr.sample_type); | 170 | return __perf_evsel__sample_size(evsel->attr.sample_type); |
170 | } | 171 | } |
171 | 172 | ||
173 | void hists__init(struct hists *hists); | ||
174 | |||
172 | #endif /* __PERF_EVSEL_H */ | 175 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/gtk/browser.c b/tools/perf/util/gtk/browser.c new file mode 100644 index 000000000000..258352a2356c --- /dev/null +++ b/tools/perf/util/gtk/browser.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include "../evlist.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../evsel.h" | ||
4 | #include "../sort.h" | ||
5 | #include "../hist.h" | ||
6 | #include "gtk.h" | ||
7 | |||
8 | #include <signal.h> | ||
9 | |||
10 | #define MAX_COLUMNS 32 | ||
11 | |||
12 | void perf_gtk_setup_browser(int argc, const char *argv[], | ||
13 | bool fallback_to_pager __used) | ||
14 | { | ||
15 | gtk_init(&argc, (char ***)&argv); | ||
16 | } | ||
17 | |||
18 | void perf_gtk_exit_browser(bool wait_for_ok __used) | ||
19 | { | ||
20 | gtk_main_quit(); | ||
21 | } | ||
22 | |||
23 | static void perf_gtk_signal(int sig) | ||
24 | { | ||
25 | psignal(sig, "perf"); | ||
26 | gtk_main_quit(); | ||
27 | } | ||
28 | |||
29 | static void perf_gtk_resize_window(GtkWidget *window) | ||
30 | { | ||
31 | GdkRectangle rect; | ||
32 | GdkScreen *screen; | ||
33 | int monitor; | ||
34 | int height; | ||
35 | int width; | ||
36 | |||
37 | screen = gtk_widget_get_screen(window); | ||
38 | |||
39 | monitor = gdk_screen_get_monitor_at_window(screen, window->window); | ||
40 | |||
41 | gdk_screen_get_monitor_geometry(screen, monitor, &rect); | ||
42 | |||
43 | width = rect.width * 3 / 4; | ||
44 | height = rect.height * 3 / 4; | ||
45 | |||
46 | gtk_window_resize(GTK_WINDOW(window), width, height); | ||
47 | } | ||
48 | |||
49 | static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists) | ||
50 | { | ||
51 | GType col_types[MAX_COLUMNS]; | ||
52 | GtkCellRenderer *renderer; | ||
53 | struct sort_entry *se; | ||
54 | GtkListStore *store; | ||
55 | struct rb_node *nd; | ||
56 | u64 total_period; | ||
57 | GtkWidget *view; | ||
58 | int col_idx; | ||
59 | int nr_cols; | ||
60 | |||
61 | nr_cols = 0; | ||
62 | |||
63 | /* The percentage column */ | ||
64 | col_types[nr_cols++] = G_TYPE_STRING; | ||
65 | |||
66 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
67 | if (se->elide) | ||
68 | continue; | ||
69 | |||
70 | col_types[nr_cols++] = G_TYPE_STRING; | ||
71 | } | ||
72 | |||
73 | store = gtk_list_store_newv(nr_cols, col_types); | ||
74 | |||
75 | view = gtk_tree_view_new(); | ||
76 | |||
77 | renderer = gtk_cell_renderer_text_new(); | ||
78 | |||
79 | col_idx = 0; | ||
80 | |||
81 | /* The percentage column */ | ||
82 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
83 | -1, "Overhead (%)", | ||
84 | renderer, "text", | ||
85 | col_idx++, NULL); | ||
86 | |||
87 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
88 | if (se->elide) | ||
89 | continue; | ||
90 | |||
91 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
92 | -1, se->se_header, | ||
93 | renderer, "text", | ||
94 | col_idx++, NULL); | ||
95 | } | ||
96 | |||
97 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
98 | |||
99 | g_object_unref(GTK_TREE_MODEL(store)); | ||
100 | |||
101 | total_period = hists->stats.total_period; | ||
102 | |||
103 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
104 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
105 | GtkTreeIter iter; | ||
106 | double percent; | ||
107 | char s[512]; | ||
108 | |||
109 | if (h->filtered) | ||
110 | continue; | ||
111 | |||
112 | gtk_list_store_append(store, &iter); | ||
113 | |||
114 | col_idx = 0; | ||
115 | |||
116 | percent = (h->period * 100.0) / total_period; | ||
117 | |||
118 | snprintf(s, ARRAY_SIZE(s), "%.2f", percent); | ||
119 | |||
120 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
121 | |||
122 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
123 | if (se->elide) | ||
124 | continue; | ||
125 | |||
126 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
127 | hists__col_len(hists, se->se_width_idx)); | ||
128 | |||
129 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | gtk_container_add(GTK_CONTAINER(window), view); | ||
134 | } | ||
135 | |||
136 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
137 | const char *help __used, | ||
138 | void (*timer) (void *arg)__used, | ||
139 | void *arg __used, int delay_secs __used) | ||
140 | { | ||
141 | struct perf_evsel *pos; | ||
142 | GtkWidget *notebook; | ||
143 | GtkWidget *window; | ||
144 | |||
145 | signal(SIGSEGV, perf_gtk_signal); | ||
146 | signal(SIGFPE, perf_gtk_signal); | ||
147 | signal(SIGINT, perf_gtk_signal); | ||
148 | signal(SIGQUIT, perf_gtk_signal); | ||
149 | signal(SIGTERM, perf_gtk_signal); | ||
150 | |||
151 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
152 | |||
153 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
154 | |||
155 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
156 | |||
157 | notebook = gtk_notebook_new(); | ||
158 | |||
159 | list_for_each_entry(pos, &evlist->entries, node) { | ||
160 | struct hists *hists = &pos->hists; | ||
161 | const char *evname = event_name(pos); | ||
162 | GtkWidget *scrolled_window; | ||
163 | GtkWidget *tab_label; | ||
164 | |||
165 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
166 | |||
167 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
168 | GTK_POLICY_AUTOMATIC, | ||
169 | GTK_POLICY_AUTOMATIC); | ||
170 | |||
171 | perf_gtk_show_hists(scrolled_window, hists); | ||
172 | |||
173 | tab_label = gtk_label_new(evname); | ||
174 | |||
175 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
176 | } | ||
177 | |||
178 | gtk_container_add(GTK_CONTAINER(window), notebook); | ||
179 | |||
180 | gtk_widget_show_all(window); | ||
181 | |||
182 | perf_gtk_resize_window(window); | ||
183 | |||
184 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
185 | |||
186 | gtk_main(); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
diff --git a/tools/perf/util/gtk/gtk.h b/tools/perf/util/gtk/gtk.h new file mode 100644 index 000000000000..75177ee04032 --- /dev/null +++ b/tools/perf/util/gtk/gtk.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_GTK_H_ | ||
2 | #define _PERF_GTK_H_ 1 | ||
3 | |||
4 | #pragma GCC diagnostic ignored "-Wstrict-prototypes" | ||
5 | #include <gtk/gtk.h> | ||
6 | #pragma GCC diagnostic error "-Wstrict-prototypes" | ||
7 | |||
8 | #endif /* _PERF_GTK_H_ */ | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fcd9cf3ea63e..4c7c2d73251f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1177,7 +1177,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1177 | goto error; | 1177 | goto error; |
1178 | 1178 | ||
1179 | msz = sizeof(attr); | 1179 | msz = sizeof(attr); |
1180 | if (sz < (ssize_t)msz) | 1180 | if (sz < msz) |
1181 | msz = sz; | 1181 | msz = sz; |
1182 | 1182 | ||
1183 | for (i = 0 ; i < nre; i++) { | 1183 | for (i = 0 ; i < nre; i++) { |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3dc99a9b71f5..2ec4b60aff6c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists, | |||
10 | struct hist_entry *he); | 10 | struct hist_entry *he); |
11 | static bool hists__filter_entry_by_thread(struct hists *hists, | 11 | static bool hists__filter_entry_by_thread(struct hists *hists, |
12 | struct hist_entry *he); | 12 | struct hist_entry *he); |
13 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
14 | struct hist_entry *he); | ||
13 | 15 | ||
14 | enum hist_filter { | 16 | enum hist_filter { |
15 | HIST_FILTER__DSO, | 17 | HIST_FILTER__DSO, |
16 | HIST_FILTER__THREAD, | 18 | HIST_FILTER__THREAD, |
17 | HIST_FILTER__PARENT, | 19 | HIST_FILTER__PARENT, |
20 | HIST_FILTER__SYMBOL, | ||
18 | }; | 21 | }; |
19 | 22 | ||
20 | struct callchain_param callchain_param = { | 23 | struct callchain_param callchain_param = { |
@@ -420,6 +423,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
420 | { | 423 | { |
421 | hists__filter_entry_by_dso(hists, he); | 424 | hists__filter_entry_by_dso(hists, he); |
422 | hists__filter_entry_by_thread(hists, he); | 425 | hists__filter_entry_by_thread(hists, he); |
426 | hists__filter_entry_by_symbol(hists, he); | ||
423 | } | 427 | } |
424 | 428 | ||
425 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | 429 | static void __hists__collapse_resort(struct hists *hists, bool threaded) |
@@ -603,7 +607,7 @@ static void init_rem_hits(void) | |||
603 | rem_hits.ms.sym = rem_sq_bracket; | 607 | rem_hits.ms.sym = rem_sq_bracket; |
604 | } | 608 | } |
605 | 609 | ||
606 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 610 | static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
607 | u64 total_samples, int depth, | 611 | u64 total_samples, int depth, |
608 | int depth_mask, int left_margin) | 612 | int depth_mask, int left_margin) |
609 | { | 613 | { |
@@ -611,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
611 | struct callchain_node *child; | 615 | struct callchain_node *child; |
612 | struct callchain_list *chain; | 616 | struct callchain_list *chain; |
613 | int new_depth_mask = depth_mask; | 617 | int new_depth_mask = depth_mask; |
614 | u64 new_total; | ||
615 | u64 remaining; | 618 | u64 remaining; |
616 | size_t ret = 0; | 619 | size_t ret = 0; |
617 | int i; | 620 | int i; |
618 | uint entries_printed = 0; | 621 | uint entries_printed = 0; |
619 | 622 | ||
620 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 623 | remaining = total_samples; |
621 | new_total = self->children_hit; | ||
622 | else | ||
623 | new_total = total_samples; | ||
624 | |||
625 | remaining = new_total; | ||
626 | 624 | ||
627 | node = rb_first(&self->rb_root); | 625 | node = rb_first(root); |
628 | while (node) { | 626 | while (node) { |
627 | u64 new_total; | ||
629 | u64 cumul; | 628 | u64 cumul; |
630 | 629 | ||
631 | child = rb_entry(node, struct callchain_node, rb_node); | 630 | child = rb_entry(node, struct callchain_node, rb_node); |
@@ -653,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
653 | list_for_each_entry(chain, &child->val, list) { | 652 | list_for_each_entry(chain, &child->val, list) { |
654 | ret += ipchain__fprintf_graph(fp, chain, depth, | 653 | ret += ipchain__fprintf_graph(fp, chain, depth, |
655 | new_depth_mask, i++, | 654 | new_depth_mask, i++, |
656 | new_total, | 655 | total_samples, |
657 | cumul, | 656 | cumul, |
658 | left_margin); | 657 | left_margin); |
659 | } | 658 | } |
660 | ret += __callchain__fprintf_graph(fp, child, new_total, | 659 | |
660 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
661 | new_total = child->children_hit; | ||
662 | else | ||
663 | new_total = total_samples; | ||
664 | |||
665 | ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, | ||
661 | depth + 1, | 666 | depth + 1, |
662 | new_depth_mask | (1 << depth), | 667 | new_depth_mask | (1 << depth), |
663 | left_margin); | 668 | left_margin); |
@@ -667,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
667 | } | 672 | } |
668 | 673 | ||
669 | if (callchain_param.mode == CHAIN_GRAPH_REL && | 674 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
670 | remaining && remaining != new_total) { | 675 | remaining && remaining != total_samples) { |
671 | 676 | ||
672 | if (!rem_sq_bracket) | 677 | if (!rem_sq_bracket) |
673 | return ret; | 678 | return ret; |
674 | 679 | ||
675 | new_depth_mask &= ~(1 << (depth - 1)); | 680 | new_depth_mask &= ~(1 << (depth - 1)); |
676 | |||
677 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | 681 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, |
678 | new_depth_mask, 0, new_total, | 682 | new_depth_mask, 0, total_samples, |
679 | remaining, left_margin); | 683 | remaining, left_margin); |
680 | } | 684 | } |
681 | 685 | ||
682 | return ret; | 686 | return ret; |
683 | } | 687 | } |
684 | 688 | ||
685 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 689 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
686 | u64 total_samples, int left_margin) | 690 | u64 total_samples, int left_margin) |
687 | { | 691 | { |
692 | struct callchain_node *cnode; | ||
688 | struct callchain_list *chain; | 693 | struct callchain_list *chain; |
694 | u32 entries_printed = 0; | ||
689 | bool printed = false; | 695 | bool printed = false; |
696 | struct rb_node *node; | ||
690 | int i = 0; | 697 | int i = 0; |
691 | int ret = 0; | 698 | int ret; |
692 | u32 entries_printed = 0; | ||
693 | |||
694 | list_for_each_entry(chain, &self->val, list) { | ||
695 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
696 | continue; | ||
697 | |||
698 | if (!printed) { | ||
699 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
700 | ret += fprintf(fp, "|\n"); | ||
701 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
702 | ret += fprintf(fp, "---"); | ||
703 | |||
704 | left_margin += 3; | ||
705 | printed = true; | ||
706 | } else | ||
707 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
708 | 699 | ||
709 | if (chain->ms.sym) | 700 | /* |
710 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 701 | * If have one single callchain root, don't bother printing |
711 | else | 702 | * its percentage (100 % in fractal mode and the same percentage |
712 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 703 | * than the hist in graph mode). This also avoid one level of column. |
704 | */ | ||
705 | node = rb_first(root); | ||
706 | if (node && !rb_next(node)) { | ||
707 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
708 | list_for_each_entry(chain, &cnode->val, list) { | ||
709 | /* | ||
710 | * If we sort by symbol, the first entry is the same than | ||
711 | * the symbol. No need to print it otherwise it appears as | ||
712 | * displayed twice. | ||
713 | */ | ||
714 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
715 | continue; | ||
716 | if (!printed) { | ||
717 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
718 | ret += fprintf(fp, "|\n"); | ||
719 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
720 | ret += fprintf(fp, "---"); | ||
721 | left_margin += 3; | ||
722 | printed = true; | ||
723 | } else | ||
724 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
725 | |||
726 | if (chain->ms.sym) | ||
727 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
728 | else | ||
729 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
713 | 730 | ||
714 | if (++entries_printed == callchain_param.print_limit) | 731 | if (++entries_printed == callchain_param.print_limit) |
715 | break; | 732 | break; |
733 | } | ||
734 | root = &cnode->rb_root; | ||
716 | } | 735 | } |
717 | 736 | ||
718 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | 737 | return __callchain__fprintf_graph(fp, root, total_samples, |
719 | 738 | 1, 1, left_margin); | |
720 | return ret; | ||
721 | } | 739 | } |
722 | 740 | ||
723 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | 741 | static size_t __callchain__fprintf_flat(FILE *fp, |
724 | u64 total_samples) | 742 | struct callchain_node *self, |
743 | u64 total_samples) | ||
725 | { | 744 | { |
726 | struct callchain_list *chain; | 745 | struct callchain_list *chain; |
727 | size_t ret = 0; | 746 | size_t ret = 0; |
@@ -729,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
729 | if (!self) | 748 | if (!self) |
730 | return 0; | 749 | return 0; |
731 | 750 | ||
732 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | 751 | ret += __callchain__fprintf_flat(fp, self->parent, total_samples); |
733 | 752 | ||
734 | 753 | ||
735 | list_for_each_entry(chain, &self->val, list) { | 754 | list_for_each_entry(chain, &self->val, list) { |
@@ -745,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
745 | return ret; | 764 | return ret; |
746 | } | 765 | } |
747 | 766 | ||
748 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | 767 | static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, |
749 | u64 total_samples, int left_margin, | 768 | u64 total_samples) |
750 | FILE *fp) | ||
751 | { | 769 | { |
752 | struct rb_node *rb_node; | ||
753 | struct callchain_node *chain; | ||
754 | size_t ret = 0; | 770 | size_t ret = 0; |
755 | u32 entries_printed = 0; | 771 | u32 entries_printed = 0; |
772 | struct rb_node *rb_node; | ||
773 | struct callchain_node *chain; | ||
756 | 774 | ||
757 | rb_node = rb_first(&he->sorted_chain); | 775 | rb_node = rb_first(self); |
758 | while (rb_node) { | 776 | while (rb_node) { |
759 | double percent; | 777 | double percent; |
760 | 778 | ||
761 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 779 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
762 | percent = chain->hit * 100.0 / total_samples; | 780 | percent = chain->hit * 100.0 / total_samples; |
763 | switch (callchain_param.mode) { | 781 | |
764 | case CHAIN_FLAT: | 782 | ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); |
765 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | 783 | ret += __callchain__fprintf_flat(fp, chain, total_samples); |
766 | percent); | ||
767 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
768 | break; | ||
769 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
770 | case CHAIN_GRAPH_REL: | ||
771 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
772 | left_margin); | ||
773 | case CHAIN_NONE: | ||
774 | default: | ||
775 | break; | ||
776 | } | ||
777 | ret += fprintf(fp, "\n"); | 784 | ret += fprintf(fp, "\n"); |
778 | if (++entries_printed == callchain_param.print_limit) | 785 | if (++entries_printed == callchain_param.print_limit) |
779 | break; | 786 | break; |
787 | |||
780 | rb_node = rb_next(rb_node); | 788 | rb_node = rb_next(rb_node); |
781 | } | 789 | } |
782 | 790 | ||
783 | return ret; | 791 | return ret; |
784 | } | 792 | } |
785 | 793 | ||
794 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | ||
795 | u64 total_samples, int left_margin, | ||
796 | FILE *fp) | ||
797 | { | ||
798 | switch (callchain_param.mode) { | ||
799 | case CHAIN_GRAPH_REL: | ||
800 | return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, | ||
801 | left_margin); | ||
802 | break; | ||
803 | case CHAIN_GRAPH_ABS: | ||
804 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | ||
805 | left_margin); | ||
806 | break; | ||
807 | case CHAIN_FLAT: | ||
808 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | ||
809 | break; | ||
810 | case CHAIN_NONE: | ||
811 | break; | ||
812 | default: | ||
813 | pr_err("Bad callchain mode\n"); | ||
814 | } | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | |||
786 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 819 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
787 | { | 820 | { |
788 | struct rb_node *next = rb_first(&hists->entries); | 821 | struct rb_node *next = rb_first(&hists->entries); |
@@ -887,9 +920,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, | |||
887 | diff = new_percent - old_percent; | 920 | diff = new_percent - old_percent; |
888 | 921 | ||
889 | if (fabs(diff) >= 0.01) | 922 | if (fabs(diff) >= 0.01) |
890 | ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); | 923 | scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); |
891 | else | 924 | else |
892 | ret += scnprintf(bf, sizeof(bf), " "); | 925 | scnprintf(bf, sizeof(bf), " "); |
893 | 926 | ||
894 | if (sep) | 927 | if (sep) |
895 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | 928 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
@@ -898,9 +931,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, | |||
898 | 931 | ||
899 | if (show_displacement) { | 932 | if (show_displacement) { |
900 | if (displacement) | 933 | if (displacement) |
901 | ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement); | 934 | scnprintf(bf, sizeof(bf), "%+4ld", displacement); |
902 | else | 935 | else |
903 | ret += scnprintf(bf, sizeof(bf), " "); | 936 | scnprintf(bf, sizeof(bf), " "); |
904 | 937 | ||
905 | if (sep) | 938 | if (sep) |
906 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | 939 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
@@ -1247,6 +1280,37 @@ void hists__filter_by_thread(struct hists *hists) | |||
1247 | } | 1280 | } |
1248 | } | 1281 | } |
1249 | 1282 | ||
1283 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
1284 | struct hist_entry *he) | ||
1285 | { | ||
1286 | if (hists->symbol_filter_str != NULL && | ||
1287 | (!he->ms.sym || strstr(he->ms.sym->name, | ||
1288 | hists->symbol_filter_str) == NULL)) { | ||
1289 | he->filtered |= (1 << HIST_FILTER__SYMBOL); | ||
1290 | return true; | ||
1291 | } | ||
1292 | |||
1293 | return false; | ||
1294 | } | ||
1295 | |||
1296 | void hists__filter_by_symbol(struct hists *hists) | ||
1297 | { | ||
1298 | struct rb_node *nd; | ||
1299 | |||
1300 | hists->nr_entries = hists->stats.total_period = 0; | ||
1301 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | ||
1302 | hists__reset_col_len(hists); | ||
1303 | |||
1304 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1305 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1306 | |||
1307 | if (hists__filter_entry_by_symbol(hists, h)) | ||
1308 | continue; | ||
1309 | |||
1310 | hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL); | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1250 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | 1314 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
1251 | { | 1315 | { |
1252 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 1316 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9413f3e31fea..2cae9df40e04 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -62,6 +62,7 @@ struct hists { | |||
62 | const struct thread *thread_filter; | 62 | const struct thread *thread_filter; |
63 | const struct dso *dso_filter; | 63 | const struct dso *dso_filter; |
64 | const char *uid_filter_str; | 64 | const char *uid_filter_str; |
65 | const char *symbol_filter_str; | ||
65 | pthread_mutex_t lock; | 66 | pthread_mutex_t lock; |
66 | struct events_stats stats; | 67 | struct events_stats stats; |
67 | u64 event_stream; | 68 | u64 event_stream; |
@@ -107,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize); | |||
107 | 108 | ||
108 | void hists__filter_by_dso(struct hists *hists); | 109 | void hists__filter_by_dso(struct hists *hists); |
109 | void hists__filter_by_thread(struct hists *hists); | 110 | void hists__filter_by_thread(struct hists *hists); |
111 | void hists__filter_by_symbol(struct hists *hists); | ||
110 | 112 | ||
111 | u16 hists__col_len(struct hists *self, enum hist_column col); | 113 | u16 hists__col_len(struct hists *self, enum hist_column col); |
112 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 114 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
@@ -145,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
145 | int refresh); | 147 | int refresh); |
146 | #endif | 148 | #endif |
147 | 149 | ||
150 | #ifdef NO_GTK2_SUPPORT | ||
151 | static inline | ||
152 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used, | ||
153 | const char *help __used, | ||
154 | void(*timer)(void *arg) __used, | ||
155 | void *arg __used, | ||
156 | int refresh __used) | ||
157 | { | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | #else | ||
162 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
163 | void(*timer)(void *arg), void *arg, | ||
164 | int refresh); | ||
165 | #endif | ||
166 | |||
148 | unsigned int hists__sort_list_width(struct hists *self); | 167 | unsigned int hists__sort_list_width(struct hists *self); |
149 | 168 | ||
150 | #endif /* __PERF_HIST_H */ | 169 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/export.h index b43e2dc21e04..b43e2dc21e04 100644 --- a/tools/perf/util/include/linux/module.h +++ b/tools/perf/util/include/linux/export.h | |||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c7a6f6faf91e..5b3a0ef4e232 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -11,6 +11,10 @@ | |||
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include "debugfs.h" |
14 | #include "parse-events-flex.h" | ||
15 | #include "pmu.h" | ||
16 | |||
17 | #define MAX_NAME_LEN 100 | ||
14 | 18 | ||
15 | struct event_symbol { | 19 | struct event_symbol { |
16 | u8 type; | 20 | u8 type; |
@@ -19,11 +23,8 @@ struct event_symbol { | |||
19 | const char *alias; | 23 | const char *alias; |
20 | }; | 24 | }; |
21 | 25 | ||
22 | enum event_result { | 26 | int parse_events_parse(struct list_head *list, struct list_head *list_tmp, |
23 | EVT_FAILED, | 27 | int *idx); |
24 | EVT_HANDLED, | ||
25 | EVT_HANDLED_ALL | ||
26 | }; | ||
27 | 28 | ||
28 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 29 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
29 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 30 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
@@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config) | |||
354 | return "unknown"; | 355 | return "unknown"; |
355 | } | 356 | } |
356 | 357 | ||
357 | static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) | 358 | static int add_event(struct list_head *list, int *idx, |
359 | struct perf_event_attr *attr, char *name) | ||
360 | { | ||
361 | struct perf_evsel *evsel; | ||
362 | |||
363 | event_attr_init(attr); | ||
364 | |||
365 | evsel = perf_evsel__new(attr, (*idx)++); | ||
366 | if (!evsel) | ||
367 | return -ENOMEM; | ||
368 | |||
369 | list_add_tail(&evsel->node, list); | ||
370 | |||
371 | evsel->name = strdup(name); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) | ||
358 | { | 376 | { |
359 | int i, j; | 377 | int i, j; |
360 | int n, longest = -1; | 378 | int n, longest = -1; |
@@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int | |||
362 | for (i = 0; i < size; i++) { | 380 | for (i = 0; i < size; i++) { |
363 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | 381 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { |
364 | n = strlen(names[i][j]); | 382 | n = strlen(names[i][j]); |
365 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | 383 | if (n > longest && !strncasecmp(str, names[i][j], n)) |
366 | longest = n; | 384 | longest = n; |
367 | } | 385 | } |
368 | if (longest > 0) { | 386 | if (longest > 0) |
369 | *str += longest; | ||
370 | return i; | 387 | return i; |
371 | } | ||
372 | } | 388 | } |
373 | 389 | ||
374 | return -1; | 390 | return -1; |
375 | } | 391 | } |
376 | 392 | ||
377 | static enum event_result | 393 | int parse_events_add_cache(struct list_head *list, int *idx, |
378 | parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | 394 | char *type, char *op_result1, char *op_result2) |
379 | { | 395 | { |
380 | const char *s = *str; | 396 | struct perf_event_attr attr; |
397 | char name[MAX_NAME_LEN]; | ||
381 | int cache_type = -1, cache_op = -1, cache_result = -1; | 398 | int cache_type = -1, cache_op = -1, cache_result = -1; |
399 | char *op_result[2] = { op_result1, op_result2 }; | ||
400 | int i, n; | ||
382 | 401 | ||
383 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); | ||
384 | /* | 402 | /* |
385 | * No fallback - if we cannot get a clear cache type | 403 | * No fallback - if we cannot get a clear cache type |
386 | * then bail out: | 404 | * then bail out: |
387 | */ | 405 | */ |
406 | cache_type = parse_aliases(type, hw_cache, | ||
407 | PERF_COUNT_HW_CACHE_MAX); | ||
388 | if (cache_type == -1) | 408 | if (cache_type == -1) |
389 | return EVT_FAILED; | 409 | return -EINVAL; |
390 | 410 | ||
391 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | 411 | n = snprintf(name, MAX_NAME_LEN, "%s", type); |
392 | ++s; | 412 | |
413 | for (i = 0; (i < 2) && (op_result[i]); i++) { | ||
414 | char *str = op_result[i]; | ||
415 | |||
416 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); | ||
393 | 417 | ||
394 | if (cache_op == -1) { | 418 | if (cache_op == -1) { |
395 | cache_op = parse_aliases(&s, hw_cache_op, | 419 | cache_op = parse_aliases(str, hw_cache_op, |
396 | PERF_COUNT_HW_CACHE_OP_MAX); | 420 | PERF_COUNT_HW_CACHE_OP_MAX); |
397 | if (cache_op >= 0) { | 421 | if (cache_op >= 0) { |
398 | if (!is_cache_op_valid(cache_type, cache_op)) | 422 | if (!is_cache_op_valid(cache_type, cache_op)) |
399 | return EVT_FAILED; | 423 | return -EINVAL; |
400 | continue; | 424 | continue; |
401 | } | 425 | } |
402 | } | 426 | } |
403 | 427 | ||
404 | if (cache_result == -1) { | 428 | if (cache_result == -1) { |
405 | cache_result = parse_aliases(&s, hw_cache_result, | 429 | cache_result = parse_aliases(str, hw_cache_result, |
406 | PERF_COUNT_HW_CACHE_RESULT_MAX); | 430 | PERF_COUNT_HW_CACHE_RESULT_MAX); |
407 | if (cache_result >= 0) | 431 | if (cache_result >= 0) |
408 | continue; | 432 | continue; |
409 | } | 433 | } |
410 | |||
411 | /* | ||
412 | * Can't parse this as a cache op or result, so back up | ||
413 | * to the '-'. | ||
414 | */ | ||
415 | --s; | ||
416 | break; | ||
417 | } | 434 | } |
418 | 435 | ||
419 | /* | 436 | /* |
@@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
428 | if (cache_result == -1) | 445 | if (cache_result == -1) |
429 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | 446 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; |
430 | 447 | ||
431 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | 448 | memset(&attr, 0, sizeof(attr)); |
432 | attr->type = PERF_TYPE_HW_CACHE; | 449 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
433 | 450 | attr.type = PERF_TYPE_HW_CACHE; | |
434 | *str = s; | 451 | return add_event(list, idx, &attr, name); |
435 | return EVT_HANDLED; | ||
436 | } | 452 | } |
437 | 453 | ||
438 | static enum event_result | 454 | static int add_tracepoint(struct list_head *list, int *idx, |
439 | parse_single_tracepoint_event(char *sys_name, | 455 | char *sys_name, char *evt_name) |
440 | const char *evt_name, | ||
441 | unsigned int evt_length, | ||
442 | struct perf_event_attr *attr, | ||
443 | const char **strp) | ||
444 | { | 456 | { |
457 | struct perf_event_attr attr; | ||
458 | char name[MAX_NAME_LEN]; | ||
445 | char evt_path[MAXPATHLEN]; | 459 | char evt_path[MAXPATHLEN]; |
446 | char id_buf[4]; | 460 | char id_buf[4]; |
447 | u64 id; | 461 | u64 id; |
@@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name, | |||
452 | 466 | ||
453 | fd = open(evt_path, O_RDONLY); | 467 | fd = open(evt_path, O_RDONLY); |
454 | if (fd < 0) | 468 | if (fd < 0) |
455 | return EVT_FAILED; | 469 | return -1; |
456 | 470 | ||
457 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 471 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { |
458 | close(fd); | 472 | close(fd); |
459 | return EVT_FAILED; | 473 | return -1; |
460 | } | 474 | } |
461 | 475 | ||
462 | close(fd); | 476 | close(fd); |
463 | id = atoll(id_buf); | 477 | id = atoll(id_buf); |
464 | attr->config = id; | ||
465 | attr->type = PERF_TYPE_TRACEPOINT; | ||
466 | *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */ | ||
467 | |||
468 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
469 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
470 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
471 | |||
472 | attr->sample_period = 1; | ||
473 | 478 | ||
479 | memset(&attr, 0, sizeof(attr)); | ||
480 | attr.config = id; | ||
481 | attr.type = PERF_TYPE_TRACEPOINT; | ||
482 | attr.sample_type |= PERF_SAMPLE_RAW; | ||
483 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
484 | attr.sample_type |= PERF_SAMPLE_CPU; | ||
485 | attr.sample_period = 1; | ||
474 | 486 | ||
475 | return EVT_HANDLED; | 487 | snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name); |
488 | return add_event(list, idx, &attr, name); | ||
476 | } | 489 | } |
477 | 490 | ||
478 | /* sys + ':' + event + ':' + flags*/ | 491 | static int add_tracepoint_multi(struct list_head *list, int *idx, |
479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 492 | char *sys_name, char *evt_name) |
480 | static enum event_result | ||
481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, | ||
482 | const char *evt_exp, char *flags) | ||
483 | { | 493 | { |
484 | char evt_path[MAXPATHLEN]; | 494 | char evt_path[MAXPATHLEN]; |
485 | struct dirent *evt_ent; | 495 | struct dirent *evt_ent; |
486 | DIR *evt_dir; | 496 | DIR *evt_dir; |
497 | int ret = 0; | ||
487 | 498 | ||
488 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); | 499 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); |
489 | evt_dir = opendir(evt_path); | 500 | evt_dir = opendir(evt_path); |
490 | |||
491 | if (!evt_dir) { | 501 | if (!evt_dir) { |
492 | perror("Can't open event dir"); | 502 | perror("Can't open event dir"); |
493 | return EVT_FAILED; | 503 | return -1; |
494 | } | 504 | } |
495 | 505 | ||
496 | while ((evt_ent = readdir(evt_dir))) { | 506 | while (!ret && (evt_ent = readdir(evt_dir))) { |
497 | char event_opt[MAX_EVOPT_LEN + 1]; | ||
498 | int len; | ||
499 | |||
500 | if (!strcmp(evt_ent->d_name, ".") | 507 | if (!strcmp(evt_ent->d_name, ".") |
501 | || !strcmp(evt_ent->d_name, "..") | 508 | || !strcmp(evt_ent->d_name, "..") |
502 | || !strcmp(evt_ent->d_name, "enable") | 509 | || !strcmp(evt_ent->d_name, "enable") |
503 | || !strcmp(evt_ent->d_name, "filter")) | 510 | || !strcmp(evt_ent->d_name, "filter")) |
504 | continue; | 511 | continue; |
505 | 512 | ||
506 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | 513 | if (!strglobmatch(evt_ent->d_name, evt_name)) |
507 | continue; | 514 | continue; |
508 | 515 | ||
509 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 516 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
510 | evt_ent->d_name, flags ? ":" : "", | ||
511 | flags ?: ""); | ||
512 | if (len < 0) | ||
513 | return EVT_FAILED; | ||
514 | |||
515 | if (parse_events(evlist, event_opt, 0)) | ||
516 | return EVT_FAILED; | ||
517 | } | 517 | } |
518 | 518 | ||
519 | return EVT_HANDLED_ALL; | 519 | return ret; |
520 | } | 520 | } |
521 | 521 | ||
522 | static enum event_result | 522 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, | 523 | char *sys, char *event) |
524 | struct perf_event_attr *attr) | ||
525 | { | 524 | { |
526 | const char *evt_name; | 525 | int ret; |
527 | char *flags = NULL, *comma_loc; | ||
528 | char sys_name[MAX_EVENT_LENGTH]; | ||
529 | unsigned int sys_length, evt_length; | ||
530 | |||
531 | if (debugfs_valid_mountpoint(tracing_events_path)) | ||
532 | return 0; | ||
533 | |||
534 | evt_name = strchr(*strp, ':'); | ||
535 | if (!evt_name) | ||
536 | return EVT_FAILED; | ||
537 | |||
538 | sys_length = evt_name - *strp; | ||
539 | if (sys_length >= MAX_EVENT_LENGTH) | ||
540 | return 0; | ||
541 | 526 | ||
542 | strncpy(sys_name, *strp, sys_length); | 527 | ret = debugfs_valid_mountpoint(tracing_events_path); |
543 | sys_name[sys_length] = '\0'; | 528 | if (ret) |
544 | evt_name = evt_name + 1; | 529 | return ret; |
545 | 530 | ||
546 | comma_loc = strchr(evt_name, ','); | 531 | return strpbrk(event, "*?") ? |
547 | if (comma_loc) { | 532 | add_tracepoint_multi(list, idx, sys, event) : |
548 | /* take the event name up to the comma */ | 533 | add_tracepoint(list, idx, sys, event); |
549 | evt_name = strndup(evt_name, comma_loc - evt_name); | ||
550 | } | ||
551 | flags = strchr(evt_name, ':'); | ||
552 | if (flags) { | ||
553 | /* split it out: */ | ||
554 | evt_name = strndup(evt_name, flags - evt_name); | ||
555 | flags++; | ||
556 | } | ||
557 | |||
558 | evt_length = strlen(evt_name); | ||
559 | if (evt_length >= MAX_EVENT_LENGTH) | ||
560 | return EVT_FAILED; | ||
561 | if (strpbrk(evt_name, "*?")) { | ||
562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | ||
563 | return parse_multiple_tracepoint_event(evlist, sys_name, | ||
564 | evt_name, flags); | ||
565 | } else { | ||
566 | return parse_single_tracepoint_event(sys_name, evt_name, | ||
567 | evt_length, attr, strp); | ||
568 | } | ||
569 | } | 534 | } |
570 | 535 | ||
571 | static enum event_result | 536 | static int |
572 | parse_breakpoint_type(const char *type, const char **strp, | 537 | parse_breakpoint_type(const char *type, struct perf_event_attr *attr) |
573 | struct perf_event_attr *attr) | ||
574 | { | 538 | { |
575 | int i; | 539 | int i; |
576 | 540 | ||
577 | for (i = 0; i < 3; i++) { | 541 | for (i = 0; i < 3; i++) { |
578 | if (!type[i]) | 542 | if (!type || !type[i]) |
579 | break; | 543 | break; |
580 | 544 | ||
581 | switch (type[i]) { | 545 | switch (type[i]) { |
@@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp, | |||
589 | attr->bp_type |= HW_BREAKPOINT_X; | 553 | attr->bp_type |= HW_BREAKPOINT_X; |
590 | break; | 554 | break; |
591 | default: | 555 | default: |
592 | return EVT_FAILED; | 556 | return -EINVAL; |
593 | } | 557 | } |
594 | } | 558 | } |
559 | |||
595 | if (!attr->bp_type) /* Default */ | 560 | if (!attr->bp_type) /* Default */ |
596 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | 561 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; |
597 | 562 | ||
598 | *strp = type + i; | 563 | return 0; |
599 | |||
600 | return EVT_HANDLED; | ||
601 | } | 564 | } |
602 | 565 | ||
603 | static enum event_result | 566 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
604 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | 567 | void *ptr, char *type) |
605 | { | 568 | { |
606 | const char *target; | 569 | struct perf_event_attr attr; |
607 | const char *type; | 570 | char name[MAX_NAME_LEN]; |
608 | char *endaddr; | ||
609 | u64 addr; | ||
610 | enum event_result err; | ||
611 | |||
612 | target = strchr(*strp, ':'); | ||
613 | if (!target) | ||
614 | return EVT_FAILED; | ||
615 | |||
616 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
617 | return EVT_FAILED; | ||
618 | |||
619 | target++; | ||
620 | |||
621 | addr = strtoull(target, &endaddr, 0); | ||
622 | if (target == endaddr) | ||
623 | return EVT_FAILED; | ||
624 | |||
625 | attr->bp_addr = addr; | ||
626 | *strp = endaddr; | ||
627 | 571 | ||
628 | type = strchr(target, ':'); | 572 | memset(&attr, 0, sizeof(attr)); |
573 | attr.bp_addr = (unsigned long) ptr; | ||
629 | 574 | ||
630 | /* If no type is defined, just rw as default */ | 575 | if (parse_breakpoint_type(type, &attr)) |
631 | if (!type) { | 576 | return -EINVAL; |
632 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
633 | } else { | ||
634 | err = parse_breakpoint_type(++type, strp, attr); | ||
635 | if (err == EVT_FAILED) | ||
636 | return EVT_FAILED; | ||
637 | } | ||
638 | 577 | ||
639 | /* | 578 | /* |
640 | * We should find a nice way to override the access length | 579 | * We should find a nice way to override the access length |
641 | * Provide some defaults for now | 580 | * Provide some defaults for now |
642 | */ | 581 | */ |
643 | if (attr->bp_type == HW_BREAKPOINT_X) | 582 | if (attr.bp_type == HW_BREAKPOINT_X) |
644 | attr->bp_len = sizeof(long); | 583 | attr.bp_len = sizeof(long); |
645 | else | 584 | else |
646 | attr->bp_len = HW_BREAKPOINT_LEN_4; | 585 | attr.bp_len = HW_BREAKPOINT_LEN_4; |
647 | 586 | ||
648 | attr->type = PERF_TYPE_BREAKPOINT; | 587 | attr.type = PERF_TYPE_BREAKPOINT; |
649 | 588 | ||
650 | return EVT_HANDLED; | 589 | snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); |
590 | return add_event(list, idx, &attr, name); | ||
651 | } | 591 | } |
652 | 592 | ||
653 | static int check_events(const char *str, unsigned int i) | 593 | static int config_term(struct perf_event_attr *attr, |
594 | struct parse_events__term *term) | ||
654 | { | 595 | { |
655 | int n; | 596 | switch (term->type) { |
597 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
598 | attr->config = term->val.num; | ||
599 | break; | ||
600 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
601 | attr->config1 = term->val.num; | ||
602 | break; | ||
603 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
604 | attr->config2 = term->val.num; | ||
605 | break; | ||
606 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
607 | attr->sample_period = term->val.num; | ||
608 | break; | ||
609 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
610 | /* | ||
611 | * TODO uncomment when the field is available | ||
612 | * attr->branch_sample_type = term->val.num; | ||
613 | */ | ||
614 | break; | ||
615 | default: | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||
656 | 620 | ||
657 | n = strlen(event_symbols[i].symbol); | 621 | static int config_attr(struct perf_event_attr *attr, |
658 | if (!strncasecmp(str, event_symbols[i].symbol, n)) | 622 | struct list_head *head, int fail) |
659 | return n; | 623 | { |
624 | struct parse_events__term *term; | ||
660 | 625 | ||
661 | n = strlen(event_symbols[i].alias); | 626 | list_for_each_entry(term, head, list) |
662 | if (n) { | 627 | if (config_term(attr, term) && fail) |
663 | if (!strncasecmp(str, event_symbols[i].alias, n)) | 628 | return -EINVAL; |
664 | return n; | ||
665 | } | ||
666 | 629 | ||
667 | return 0; | 630 | return 0; |
668 | } | 631 | } |
669 | 632 | ||
670 | static enum event_result | 633 | int parse_events_add_numeric(struct list_head *list, int *idx, |
671 | parse_symbolic_event(const char **strp, struct perf_event_attr *attr) | 634 | unsigned long type, unsigned long config, |
635 | struct list_head *head_config) | ||
672 | { | 636 | { |
673 | const char *str = *strp; | 637 | struct perf_event_attr attr; |
674 | unsigned int i; | 638 | |
675 | int n; | 639 | memset(&attr, 0, sizeof(attr)); |
676 | 640 | attr.type = type; | |
677 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | 641 | attr.config = config; |
678 | n = check_events(str, i); | 642 | |
679 | if (n > 0) { | 643 | if (head_config && |
680 | attr->type = event_symbols[i].type; | 644 | config_attr(&attr, head_config, 1)) |
681 | attr->config = event_symbols[i].config; | 645 | return -EINVAL; |
682 | *strp = str + n; | 646 | |
683 | return EVT_HANDLED; | 647 | return add_event(list, idx, &attr, |
684 | } | 648 | (char *) __event_name(type, config)); |
685 | } | ||
686 | return EVT_FAILED; | ||
687 | } | 649 | } |
688 | 650 | ||
689 | static enum event_result | 651 | int parse_events_add_pmu(struct list_head *list, int *idx, |
690 | parse_raw_event(const char **strp, struct perf_event_attr *attr) | 652 | char *name, struct list_head *head_config) |
691 | { | 653 | { |
692 | const char *str = *strp; | 654 | struct perf_event_attr attr; |
693 | u64 config; | 655 | struct perf_pmu *pmu; |
694 | int n; | 656 | |
695 | 657 | pmu = perf_pmu__find(name); | |
696 | if (*str != 'r') | 658 | if (!pmu) |
697 | return EVT_FAILED; | 659 | return -EINVAL; |
698 | n = hex2u64(str + 1, &config); | 660 | |
699 | if (n > 0) { | 661 | memset(&attr, 0, sizeof(attr)); |
700 | const char *end = str + n + 1; | 662 | |
701 | if (*end != '\0' && *end != ',' && *end != ':') | 663 | /* |
702 | return EVT_FAILED; | 664 | * Configure hardcoded terms first, no need to check |
703 | 665 | * return value when called with fail == 0 ;) | |
704 | *strp = end; | 666 | */ |
705 | attr->type = PERF_TYPE_RAW; | 667 | config_attr(&attr, head_config, 0); |
706 | attr->config = config; | 668 | |
707 | return EVT_HANDLED; | 669 | if (perf_pmu__config(pmu, &attr, head_config)) |
708 | } | 670 | return -EINVAL; |
709 | return EVT_FAILED; | 671 | |
672 | return add_event(list, idx, &attr, (char *) "pmu"); | ||
710 | } | 673 | } |
711 | 674 | ||
712 | static enum event_result | 675 | void parse_events_update_lists(struct list_head *list_event, |
713 | parse_numeric_event(const char **strp, struct perf_event_attr *attr) | 676 | struct list_head *list_all) |
714 | { | 677 | { |
715 | const char *str = *strp; | 678 | /* |
716 | char *endp; | 679 | * Called for single event definition. Update the |
717 | unsigned long type; | 680 | * 'all event' list, and reinit the 'signle event' |
718 | u64 config; | 681 | * list, for next event definition. |
719 | 682 | */ | |
720 | type = strtoul(str, &endp, 0); | 683 | list_splice_tail(list_event, list_all); |
721 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | 684 | INIT_LIST_HEAD(list_event); |
722 | str = endp + 1; | ||
723 | config = strtoul(str, &endp, 0); | ||
724 | if (endp > str) { | ||
725 | attr->type = type; | ||
726 | attr->config = config; | ||
727 | *strp = endp; | ||
728 | return EVT_HANDLED; | ||
729 | } | ||
730 | } | ||
731 | return EVT_FAILED; | ||
732 | } | 685 | } |
733 | 686 | ||
734 | static int | 687 | int parse_events_modifier(struct list_head *list, char *str) |
735 | parse_event_modifier(const char **strp, struct perf_event_attr *attr) | ||
736 | { | 688 | { |
737 | const char *str = *strp; | 689 | struct perf_evsel *evsel; |
738 | int exclude = 0, exclude_GH = 0; | 690 | int exclude = 0, exclude_GH = 0; |
739 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; | 691 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; |
740 | 692 | ||
741 | if (!*str) | 693 | if (str == NULL) |
742 | return 0; | 694 | return 0; |
743 | 695 | ||
744 | if (*str == ',') | ||
745 | return 0; | ||
746 | |||
747 | if (*str++ != ':') | ||
748 | return -1; | ||
749 | |||
750 | while (*str) { | 696 | while (*str) { |
751 | if (*str == 'u') { | 697 | if (*str == 'u') { |
752 | if (!exclude) | 698 | if (!exclude) |
@@ -775,111 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
775 | 721 | ||
776 | ++str; | 722 | ++str; |
777 | } | 723 | } |
778 | if (str < *strp + 2) | ||
779 | return -1; | ||
780 | 724 | ||
781 | *strp = str; | 725 | /* |
726 | * precise ip: | ||
727 | * | ||
728 | * 0 - SAMPLE_IP can have arbitrary skid | ||
729 | * 1 - SAMPLE_IP must have constant skid | ||
730 | * 2 - SAMPLE_IP requested to have 0 skid | ||
731 | * 3 - SAMPLE_IP must have 0 skid | ||
732 | * | ||
733 | * See also PERF_RECORD_MISC_EXACT_IP | ||
734 | */ | ||
735 | if (precise > 3) | ||
736 | return -EINVAL; | ||
782 | 737 | ||
783 | attr->exclude_user = eu; | 738 | list_for_each_entry(evsel, list, node) { |
784 | attr->exclude_kernel = ek; | 739 | evsel->attr.exclude_user = eu; |
785 | attr->exclude_hv = eh; | 740 | evsel->attr.exclude_kernel = ek; |
786 | attr->precise_ip = precise; | 741 | evsel->attr.exclude_hv = eh; |
787 | attr->exclude_host = eH; | 742 | evsel->attr.precise_ip = precise; |
788 | attr->exclude_guest = eG; | 743 | evsel->attr.exclude_host = eH; |
744 | evsel->attr.exclude_guest = eG; | ||
745 | } | ||
789 | 746 | ||
790 | return 0; | 747 | return 0; |
791 | } | 748 | } |
792 | 749 | ||
793 | /* | 750 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) |
794 | * Each event can have multiple symbolic names. | ||
795 | * Symbolic names are (almost) exactly matched. | ||
796 | */ | ||
797 | static enum event_result | ||
798 | parse_event_symbols(struct perf_evlist *evlist, const char **str, | ||
799 | struct perf_event_attr *attr) | ||
800 | { | 751 | { |
801 | enum event_result ret; | 752 | LIST_HEAD(list); |
802 | 753 | LIST_HEAD(list_tmp); | |
803 | ret = parse_tracepoint_event(evlist, str, attr); | 754 | YY_BUFFER_STATE buffer; |
804 | if (ret != EVT_FAILED) | 755 | int ret, idx = evlist->nr_entries; |
805 | goto modifier; | ||
806 | |||
807 | ret = parse_raw_event(str, attr); | ||
808 | if (ret != EVT_FAILED) | ||
809 | goto modifier; | ||
810 | 756 | ||
811 | ret = parse_numeric_event(str, attr); | 757 | buffer = parse_events__scan_string(str); |
812 | if (ret != EVT_FAILED) | ||
813 | goto modifier; | ||
814 | 758 | ||
815 | ret = parse_symbolic_event(str, attr); | 759 | ret = parse_events_parse(&list, &list_tmp, &idx); |
816 | if (ret != EVT_FAILED) | ||
817 | goto modifier; | ||
818 | 760 | ||
819 | ret = parse_generic_hw_event(str, attr); | 761 | parse_events__flush_buffer(buffer); |
820 | if (ret != EVT_FAILED) | 762 | parse_events__delete_buffer(buffer); |
821 | goto modifier; | ||
822 | 763 | ||
823 | ret = parse_breakpoint_event(str, attr); | 764 | if (!ret) { |
824 | if (ret != EVT_FAILED) | 765 | int entries = idx - evlist->nr_entries; |
825 | goto modifier; | 766 | perf_evlist__splice_list_tail(evlist, &list, entries); |
826 | 767 | return 0; | |
827 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
828 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
829 | return EVT_FAILED; | ||
830 | |||
831 | modifier: | ||
832 | if (parse_event_modifier(str, attr) < 0) { | ||
833 | fprintf(stderr, "invalid event modifier: '%s'\n", *str); | ||
834 | fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n"); | ||
835 | |||
836 | return EVT_FAILED; | ||
837 | } | 768 | } |
838 | 769 | ||
770 | /* | ||
771 | * There are 2 users - builtin-record and builtin-test objects. | ||
772 | * Both call perf_evlist__delete in case of error, so we dont | ||
773 | * need to bother. | ||
774 | */ | ||
775 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
776 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
839 | return ret; | 777 | return ret; |
840 | } | 778 | } |
841 | 779 | ||
842 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) | ||
843 | { | ||
844 | struct perf_event_attr attr; | ||
845 | enum event_result ret; | ||
846 | const char *ostr; | ||
847 | |||
848 | for (;;) { | ||
849 | ostr = str; | ||
850 | memset(&attr, 0, sizeof(attr)); | ||
851 | event_attr_init(&attr); | ||
852 | ret = parse_event_symbols(evlist, &str, &attr); | ||
853 | if (ret == EVT_FAILED) | ||
854 | return -1; | ||
855 | |||
856 | if (!(*str == 0 || *str == ',' || isspace(*str))) | ||
857 | return -1; | ||
858 | |||
859 | if (ret != EVT_HANDLED_ALL) { | ||
860 | struct perf_evsel *evsel; | ||
861 | evsel = perf_evsel__new(&attr, evlist->nr_entries); | ||
862 | if (evsel == NULL) | ||
863 | return -1; | ||
864 | perf_evlist__add(evlist, evsel); | ||
865 | |||
866 | evsel->name = calloc(str - ostr + 1, 1); | ||
867 | if (!evsel->name) | ||
868 | return -1; | ||
869 | strncpy(evsel->name, ostr, str - ostr); | ||
870 | } | ||
871 | |||
872 | if (*str == 0) | ||
873 | break; | ||
874 | if (*str == ',') | ||
875 | ++str; | ||
876 | while (isspace(*str)) | ||
877 | ++str; | ||
878 | } | ||
879 | |||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | int parse_events_option(const struct option *opt, const char *str, | 780 | int parse_events_option(const struct option *opt, const char *str, |
884 | int unset __used) | 781 | int unset __used) |
885 | { | 782 | { |
@@ -1052,8 +949,6 @@ int print_hwcache_events(const char *event_glob) | |||
1052 | return printed; | 949 | return printed; |
1053 | } | 950 | } |
1054 | 951 | ||
1055 | #define MAX_NAME_LEN 100 | ||
1056 | |||
1057 | /* | 952 | /* |
1058 | * Print the help text for the event symbols: | 953 | * Print the help text for the event symbols: |
1059 | */ | 954 | */ |
@@ -1102,8 +997,12 @@ void print_events(const char *event_glob) | |||
1102 | 997 | ||
1103 | printf("\n"); | 998 | printf("\n"); |
1104 | printf(" %-50s [%s]\n", | 999 | printf(" %-50s [%s]\n", |
1105 | "rNNN (see 'perf list --help' on how to encode it)", | 1000 | "rNNN", |
1106 | event_type_descriptors[PERF_TYPE_RAW]); | 1001 | event_type_descriptors[PERF_TYPE_RAW]); |
1002 | printf(" %-50s [%s]\n", | ||
1003 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | ||
1004 | event_type_descriptors[PERF_TYPE_RAW]); | ||
1005 | printf(" (see 'perf list --help' on how to encode it)\n"); | ||
1107 | printf("\n"); | 1006 | printf("\n"); |
1108 | 1007 | ||
1109 | printf(" %-50s [%s]\n", | 1008 | printf(" %-50s [%s]\n", |
@@ -1113,3 +1012,51 @@ void print_events(const char *event_glob) | |||
1113 | 1012 | ||
1114 | print_tracepoint_events(NULL, NULL); | 1013 | print_tracepoint_events(NULL, NULL); |
1115 | } | 1014 | } |
1015 | |||
1016 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | ||
1017 | { | ||
1018 | return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX; | ||
1019 | } | ||
1020 | |||
1021 | int parse_events__new_term(struct parse_events__term **_term, int type, | ||
1022 | char *config, char *str, long num) | ||
1023 | { | ||
1024 | struct parse_events__term *term; | ||
1025 | |||
1026 | term = zalloc(sizeof(*term)); | ||
1027 | if (!term) | ||
1028 | return -ENOMEM; | ||
1029 | |||
1030 | INIT_LIST_HEAD(&term->list); | ||
1031 | term->type = type; | ||
1032 | term->config = config; | ||
1033 | |||
1034 | switch (type) { | ||
1035 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
1036 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
1037 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
1038 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
1039 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
1040 | case PARSE_EVENTS__TERM_TYPE_NUM: | ||
1041 | term->val.num = num; | ||
1042 | break; | ||
1043 | case PARSE_EVENTS__TERM_TYPE_STR: | ||
1044 | term->val.str = str; | ||
1045 | break; | ||
1046 | default: | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | |||
1050 | *_term = term; | ||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | void parse_events__free_terms(struct list_head *terms) | ||
1055 | { | ||
1056 | struct parse_events__term *term, *h; | ||
1057 | |||
1058 | list_for_each_entry_safe(term, h, terms, list) | ||
1059 | free(term); | ||
1060 | |||
1061 | free(terms); | ||
1062 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 7e0cbe75d5f1..ca069f893381 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
33 | 33 | ||
34 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
35 | 35 | ||
36 | enum { | ||
37 | PARSE_EVENTS__TERM_TYPE_CONFIG, | ||
38 | PARSE_EVENTS__TERM_TYPE_CONFIG1, | ||
39 | PARSE_EVENTS__TERM_TYPE_CONFIG2, | ||
40 | PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD, | ||
41 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
42 | PARSE_EVENTS__TERM_TYPE_NUM, | ||
43 | PARSE_EVENTS__TERM_TYPE_STR, | ||
44 | |||
45 | PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX = | ||
46 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
47 | }; | ||
48 | |||
49 | struct parse_events__term { | ||
50 | char *config; | ||
51 | union { | ||
52 | char *str; | ||
53 | long num; | ||
54 | } val; | ||
55 | int type; | ||
56 | |||
57 | struct list_head list; | ||
58 | }; | ||
59 | |||
60 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | ||
61 | int parse_events__new_term(struct parse_events__term **term, int type, | ||
62 | char *config, char *str, long num); | ||
63 | void parse_events__free_terms(struct list_head *terms); | ||
64 | int parse_events_modifier(struct list_head *list __used, char *str __used); | ||
65 | int parse_events_add_tracepoint(struct list_head *list, int *idx, | ||
66 | char *sys, char *event); | ||
67 | int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config, | ||
68 | unsigned long config1, unsigned long config2, | ||
69 | char *mod); | ||
70 | int parse_events_add_numeric(struct list_head *list, int *idx, | ||
71 | unsigned long type, unsigned long config, | ||
72 | struct list_head *head_config); | ||
73 | int parse_events_add_cache(struct list_head *list, int *idx, | ||
74 | char *type, char *op_result1, char *op_result2); | ||
75 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | ||
76 | void *ptr, char *type); | ||
77 | int parse_events_add_pmu(struct list_head *list, int *idx, | ||
78 | char *pmu , struct list_head *head_config); | ||
79 | void parse_events_update_lists(struct list_head *list_event, | ||
80 | struct list_head *list_all); | ||
81 | void parse_events_error(struct list_head *list_all, | ||
82 | struct list_head *list_event, | ||
83 | int *idx, char const *msg); | ||
84 | |||
36 | void print_events(const char *event_glob); | 85 | void print_events(const char *event_glob); |
37 | void print_events_type(u8 type); | 86 | void print_events_type(u8 type); |
38 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | 87 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l new file mode 100644 index 000000000000..05d766e3ecb5 --- /dev/null +++ b/tools/perf/util/parse-events.l | |||
@@ -0,0 +1,127 @@ | |||
1 | |||
2 | %option prefix="parse_events_" | ||
3 | |||
4 | %{ | ||
5 | #include <errno.h> | ||
6 | #include "../perf.h" | ||
7 | #include "parse-events-bison.h" | ||
8 | #include "parse-events.h" | ||
9 | |||
10 | static int __value(char *str, int base, int token) | ||
11 | { | ||
12 | long num; | ||
13 | |||
14 | errno = 0; | ||
15 | num = strtoul(str, NULL, base); | ||
16 | if (errno) | ||
17 | return PE_ERROR; | ||
18 | |||
19 | parse_events_lval.num = num; | ||
20 | return token; | ||
21 | } | ||
22 | |||
23 | static int value(int base) | ||
24 | { | ||
25 | return __value(parse_events_text, base, PE_VALUE); | ||
26 | } | ||
27 | |||
28 | static int raw(void) | ||
29 | { | ||
30 | return __value(parse_events_text + 1, 16, PE_RAW); | ||
31 | } | ||
32 | |||
33 | static int str(int token) | ||
34 | { | ||
35 | parse_events_lval.str = strdup(parse_events_text); | ||
36 | return token; | ||
37 | } | ||
38 | |||
39 | static int sym(int type, int config) | ||
40 | { | ||
41 | parse_events_lval.num = (type << 16) + config; | ||
42 | return PE_VALUE_SYM; | ||
43 | } | ||
44 | |||
45 | static int term(int type) | ||
46 | { | ||
47 | parse_events_lval.num = type; | ||
48 | return PE_TERM; | ||
49 | } | ||
50 | |||
51 | %} | ||
52 | |||
53 | num_dec [0-9]+ | ||
54 | num_hex 0x[a-fA-F0-9]+ | ||
55 | num_raw_hex [a-fA-F0-9]+ | ||
56 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* | ||
57 | modifier_event [ukhp]{1,5} | ||
58 | modifier_bp [rwx] | ||
59 | |||
60 | %% | ||
61 | cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | ||
62 | stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | ||
63 | stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | ||
64 | instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } | ||
65 | cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } | ||
66 | cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } | ||
67 | branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } | ||
68 | branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } | ||
69 | bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } | ||
70 | ref-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } | ||
71 | cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } | ||
72 | task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } | ||
73 | page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } | ||
74 | minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } | ||
75 | major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } | ||
76 | context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } | ||
77 | cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } | ||
78 | alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } | ||
79 | emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | ||
80 | |||
81 | L1-dcache|l1-d|l1d|L1-data | | ||
82 | L1-icache|l1-i|l1i|L1-instruction | | ||
83 | LLC|L2 | | ||
84 | dTLB|d-tlb|Data-TLB | | ||
85 | iTLB|i-tlb|Instruction-TLB | | ||
86 | branch|branches|bpu|btb|bpc | | ||
87 | node { return str(PE_NAME_CACHE_TYPE); } | ||
88 | |||
89 | load|loads|read | | ||
90 | store|stores|write | | ||
91 | prefetch|prefetches | | ||
92 | speculative-read|speculative-load | | ||
93 | refs|Reference|ops|access | | ||
94 | misses|miss { return str(PE_NAME_CACHE_OP_RESULT); } | ||
95 | |||
96 | /* | ||
97 | * These are event config hardcoded term names to be specified | ||
98 | * within xxx/.../ syntax. So far we dont clash with other names, | ||
99 | * so we can put them here directly. In case the we have a conflict | ||
100 | * in future, this needs to go into '//' condition block. | ||
101 | */ | ||
102 | config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } | ||
103 | config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } | ||
104 | config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } | ||
105 | period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | ||
106 | branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | ||
107 | |||
108 | mem: { return PE_PREFIX_MEM; } | ||
109 | r{num_raw_hex} { return raw(); } | ||
110 | {num_dec} { return value(10); } | ||
111 | {num_hex} { return value(16); } | ||
112 | |||
113 | {modifier_event} { return str(PE_MODIFIER_EVENT); } | ||
114 | {modifier_bp} { return str(PE_MODIFIER_BP); } | ||
115 | {name} { return str(PE_NAME); } | ||
116 | "/" { return '/'; } | ||
117 | - { return '-'; } | ||
118 | , { return ','; } | ||
119 | : { return ':'; } | ||
120 | = { return '='; } | ||
121 | |||
122 | %% | ||
123 | |||
124 | int parse_events_wrap(void) | ||
125 | { | ||
126 | return 1; | ||
127 | } | ||
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y new file mode 100644 index 000000000000..d9637da7333c --- /dev/null +++ b/tools/perf/util/parse-events.y | |||
@@ -0,0 +1,229 @@ | |||
1 | |||
2 | %name-prefix "parse_events_" | ||
3 | %parse-param {struct list_head *list_all} | ||
4 | %parse-param {struct list_head *list_event} | ||
5 | %parse-param {int *idx} | ||
6 | |||
7 | %{ | ||
8 | |||
9 | #define YYDEBUG 1 | ||
10 | |||
11 | #include <linux/compiler.h> | ||
12 | #include <linux/list.h> | ||
13 | #include "types.h" | ||
14 | #include "util.h" | ||
15 | #include "parse-events.h" | ||
16 | |||
17 | extern int parse_events_lex (void); | ||
18 | |||
19 | #define ABORT_ON(val) \ | ||
20 | do { \ | ||
21 | if (val) \ | ||
22 | YYABORT; \ | ||
23 | } while (0) | ||
24 | |||
25 | %} | ||
26 | |||
27 | %token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM | ||
28 | %token PE_NAME | ||
29 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP | ||
30 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | ||
31 | %token PE_PREFIX_MEM PE_PREFIX_RAW | ||
32 | %token PE_ERROR | ||
33 | %type <num> PE_VALUE | ||
34 | %type <num> PE_VALUE_SYM | ||
35 | %type <num> PE_RAW | ||
36 | %type <num> PE_TERM | ||
37 | %type <str> PE_NAME | ||
38 | %type <str> PE_NAME_CACHE_TYPE | ||
39 | %type <str> PE_NAME_CACHE_OP_RESULT | ||
40 | %type <str> PE_MODIFIER_EVENT | ||
41 | %type <str> PE_MODIFIER_BP | ||
42 | %type <head> event_config | ||
43 | %type <term> event_term | ||
44 | |||
45 | %union | ||
46 | { | ||
47 | char *str; | ||
48 | unsigned long num; | ||
49 | struct list_head *head; | ||
50 | struct parse_events__term *term; | ||
51 | } | ||
52 | %% | ||
53 | |||
54 | events: | ||
55 | events ',' event | event | ||
56 | |||
57 | event: | ||
58 | event_def PE_MODIFIER_EVENT | ||
59 | { | ||
60 | /* | ||
61 | * Apply modifier on all events added by single event definition | ||
62 | * (there could be more events added for multiple tracepoint | ||
63 | * definitions via '*?'. | ||
64 | */ | ||
65 | ABORT_ON(parse_events_modifier(list_event, $2)); | ||
66 | parse_events_update_lists(list_event, list_all); | ||
67 | } | ||
68 | | | ||
69 | event_def | ||
70 | { | ||
71 | parse_events_update_lists(list_event, list_all); | ||
72 | } | ||
73 | |||
74 | event_def: event_pmu | | ||
75 | event_legacy_symbol | | ||
76 | event_legacy_cache sep_dc | | ||
77 | event_legacy_mem | | ||
78 | event_legacy_tracepoint sep_dc | | ||
79 | event_legacy_numeric sep_dc | | ||
80 | event_legacy_raw sep_dc | ||
81 | |||
82 | event_pmu: | ||
83 | PE_NAME '/' event_config '/' | ||
84 | { | ||
85 | ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3)); | ||
86 | parse_events__free_terms($3); | ||
87 | } | ||
88 | |||
89 | event_legacy_symbol: | ||
90 | PE_VALUE_SYM '/' event_config '/' | ||
91 | { | ||
92 | int type = $1 >> 16; | ||
93 | int config = $1 & 255; | ||
94 | |||
95 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3)); | ||
96 | parse_events__free_terms($3); | ||
97 | } | ||
98 | | | ||
99 | PE_VALUE_SYM sep_slash_dc | ||
100 | { | ||
101 | int type = $1 >> 16; | ||
102 | int config = $1 & 255; | ||
103 | |||
104 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL)); | ||
105 | } | ||
106 | |||
107 | event_legacy_cache: | ||
108 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | ||
109 | { | ||
110 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5)); | ||
111 | } | ||
112 | | | ||
113 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | ||
114 | { | ||
115 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL)); | ||
116 | } | ||
117 | | | ||
118 | PE_NAME_CACHE_TYPE | ||
119 | { | ||
120 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL)); | ||
121 | } | ||
122 | |||
123 | event_legacy_mem: | ||
124 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | ||
125 | { | ||
126 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4)); | ||
127 | } | ||
128 | | | ||
129 | PE_PREFIX_MEM PE_VALUE sep_dc | ||
130 | { | ||
131 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL)); | ||
132 | } | ||
133 | |||
134 | event_legacy_tracepoint: | ||
135 | PE_NAME ':' PE_NAME | ||
136 | { | ||
137 | ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3)); | ||
138 | } | ||
139 | |||
140 | event_legacy_numeric: | ||
141 | PE_VALUE ':' PE_VALUE | ||
142 | { | ||
143 | ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL)); | ||
144 | } | ||
145 | |||
146 | event_legacy_raw: | ||
147 | PE_RAW | ||
148 | { | ||
149 | ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL)); | ||
150 | } | ||
151 | |||
152 | event_config: | ||
153 | event_config ',' event_term | ||
154 | { | ||
155 | struct list_head *head = $1; | ||
156 | struct parse_events__term *term = $3; | ||
157 | |||
158 | ABORT_ON(!head); | ||
159 | list_add_tail(&term->list, head); | ||
160 | $$ = $1; | ||
161 | } | ||
162 | | | ||
163 | event_term | ||
164 | { | ||
165 | struct list_head *head = malloc(sizeof(*head)); | ||
166 | struct parse_events__term *term = $1; | ||
167 | |||
168 | ABORT_ON(!head); | ||
169 | INIT_LIST_HEAD(head); | ||
170 | list_add_tail(&term->list, head); | ||
171 | $$ = head; | ||
172 | } | ||
173 | |||
174 | event_term: | ||
175 | PE_NAME '=' PE_NAME | ||
176 | { | ||
177 | struct parse_events__term *term; | ||
178 | |||
179 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR, | ||
180 | $1, $3, 0)); | ||
181 | $$ = term; | ||
182 | } | ||
183 | | | ||
184 | PE_NAME '=' PE_VALUE | ||
185 | { | ||
186 | struct parse_events__term *term; | ||
187 | |||
188 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
189 | $1, NULL, $3)); | ||
190 | $$ = term; | ||
191 | } | ||
192 | | | ||
193 | PE_NAME | ||
194 | { | ||
195 | struct parse_events__term *term; | ||
196 | |||
197 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
198 | $1, NULL, 1)); | ||
199 | $$ = term; | ||
200 | } | ||
201 | | | ||
202 | PE_TERM '=' PE_VALUE | ||
203 | { | ||
204 | struct parse_events__term *term; | ||
205 | |||
206 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3)); | ||
207 | $$ = term; | ||
208 | } | ||
209 | | | ||
210 | PE_TERM | ||
211 | { | ||
212 | struct parse_events__term *term; | ||
213 | |||
214 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1)); | ||
215 | $$ = term; | ||
216 | } | ||
217 | |||
218 | sep_dc: ':' | | ||
219 | |||
220 | sep_slash_dc: '/' | ':' | | ||
221 | |||
222 | %% | ||
223 | |||
224 | void parse_events_error(struct list_head *list_all __used, | ||
225 | struct list_head *list_event __used, | ||
226 | int *idx __used, | ||
227 | char const *msg __used) | ||
228 | { | ||
229 | } | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c new file mode 100644 index 000000000000..cb08a118e811 --- /dev/null +++ b/tools/perf/util/pmu.c | |||
@@ -0,0 +1,469 @@ | |||
1 | |||
2 | #include <linux/list.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <stdio.h> | ||
7 | #include <dirent.h> | ||
8 | #include "sysfs.h" | ||
9 | #include "util.h" | ||
10 | #include "pmu.h" | ||
11 | #include "parse-events.h" | ||
12 | |||
13 | int perf_pmu_parse(struct list_head *list, char *name); | ||
14 | extern FILE *perf_pmu_in; | ||
15 | |||
16 | static LIST_HEAD(pmus); | ||
17 | |||
18 | /* | ||
19 | * Parse & process all the sysfs attributes located under | ||
20 | * the directory specified in 'dir' parameter. | ||
21 | */ | ||
22 | static int pmu_format_parse(char *dir, struct list_head *head) | ||
23 | { | ||
24 | struct dirent *evt_ent; | ||
25 | DIR *format_dir; | ||
26 | int ret = 0; | ||
27 | |||
28 | format_dir = opendir(dir); | ||
29 | if (!format_dir) | ||
30 | return -EINVAL; | ||
31 | |||
32 | while (!ret && (evt_ent = readdir(format_dir))) { | ||
33 | char path[PATH_MAX]; | ||
34 | char *name = evt_ent->d_name; | ||
35 | FILE *file; | ||
36 | |||
37 | if (!strcmp(name, ".") || !strcmp(name, "..")) | ||
38 | continue; | ||
39 | |||
40 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | ||
41 | |||
42 | ret = -EINVAL; | ||
43 | file = fopen(path, "r"); | ||
44 | if (!file) | ||
45 | break; | ||
46 | |||
47 | perf_pmu_in = file; | ||
48 | ret = perf_pmu_parse(head, name); | ||
49 | fclose(file); | ||
50 | } | ||
51 | |||
52 | closedir(format_dir); | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Reading/parsing the default pmu format definition, which should be | ||
58 | * located at: | ||
59 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. | ||
60 | */ | ||
61 | static int pmu_format(char *name, struct list_head *format) | ||
62 | { | ||
63 | struct stat st; | ||
64 | char path[PATH_MAX]; | ||
65 | const char *sysfs; | ||
66 | |||
67 | sysfs = sysfs_find_mountpoint(); | ||
68 | if (!sysfs) | ||
69 | return -1; | ||
70 | |||
71 | snprintf(path, PATH_MAX, | ||
72 | "%s/bus/event_source/devices/%s/format", sysfs, name); | ||
73 | |||
74 | if (stat(path, &st) < 0) | ||
75 | return -1; | ||
76 | |||
77 | if (pmu_format_parse(path, format)) | ||
78 | return -1; | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Reading/parsing the default pmu type value, which should be | ||
85 | * located at: | ||
86 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. | ||
87 | */ | ||
88 | static int pmu_type(char *name, __u32 *type) | ||
89 | { | ||
90 | struct stat st; | ||
91 | char path[PATH_MAX]; | ||
92 | const char *sysfs; | ||
93 | FILE *file; | ||
94 | int ret = 0; | ||
95 | |||
96 | sysfs = sysfs_find_mountpoint(); | ||
97 | if (!sysfs) | ||
98 | return -1; | ||
99 | |||
100 | snprintf(path, PATH_MAX, | ||
101 | "%s/bus/event_source/devices/%s/type", sysfs, name); | ||
102 | |||
103 | if (stat(path, &st) < 0) | ||
104 | return -1; | ||
105 | |||
106 | file = fopen(path, "r"); | ||
107 | if (!file) | ||
108 | return -EINVAL; | ||
109 | |||
110 | if (1 != fscanf(file, "%u", type)) | ||
111 | ret = -1; | ||
112 | |||
113 | fclose(file); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static struct perf_pmu *pmu_lookup(char *name) | ||
118 | { | ||
119 | struct perf_pmu *pmu; | ||
120 | LIST_HEAD(format); | ||
121 | __u32 type; | ||
122 | |||
123 | /* | ||
124 | * The pmu data we store & need consists of the pmu | ||
125 | * type value and format definitions. Load both right | ||
126 | * now. | ||
127 | */ | ||
128 | if (pmu_format(name, &format)) | ||
129 | return NULL; | ||
130 | |||
131 | if (pmu_type(name, &type)) | ||
132 | return NULL; | ||
133 | |||
134 | pmu = zalloc(sizeof(*pmu)); | ||
135 | if (!pmu) | ||
136 | return NULL; | ||
137 | |||
138 | INIT_LIST_HEAD(&pmu->format); | ||
139 | list_splice(&format, &pmu->format); | ||
140 | pmu->name = strdup(name); | ||
141 | pmu->type = type; | ||
142 | return pmu; | ||
143 | } | ||
144 | |||
145 | static struct perf_pmu *pmu_find(char *name) | ||
146 | { | ||
147 | struct perf_pmu *pmu; | ||
148 | |||
149 | list_for_each_entry(pmu, &pmus, list) | ||
150 | if (!strcmp(pmu->name, name)) | ||
151 | return pmu; | ||
152 | |||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | struct perf_pmu *perf_pmu__find(char *name) | ||
157 | { | ||
158 | struct perf_pmu *pmu; | ||
159 | |||
160 | /* | ||
161 | * Once PMU is loaded it stays in the list, | ||
162 | * so we keep us from multiple reading/parsing | ||
163 | * the pmu format definitions. | ||
164 | */ | ||
165 | pmu = pmu_find(name); | ||
166 | if (pmu) | ||
167 | return pmu; | ||
168 | |||
169 | return pmu_lookup(name); | ||
170 | } | ||
171 | |||
172 | static struct perf_pmu__format* | ||
173 | pmu_find_format(struct list_head *formats, char *name) | ||
174 | { | ||
175 | struct perf_pmu__format *format; | ||
176 | |||
177 | list_for_each_entry(format, formats, list) | ||
178 | if (!strcmp(format->name, name)) | ||
179 | return format; | ||
180 | |||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Returns value based on the format definition (format parameter) | ||
186 | * and unformated value (value parameter). | ||
187 | * | ||
188 | * TODO maybe optimize a little ;) | ||
189 | */ | ||
190 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | ||
191 | { | ||
192 | unsigned long fbit, vbit; | ||
193 | __u64 v = 0; | ||
194 | |||
195 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | ||
196 | |||
197 | if (!test_bit(fbit, format)) | ||
198 | continue; | ||
199 | |||
200 | if (!(value & (1llu << vbit++))) | ||
201 | continue; | ||
202 | |||
203 | v |= (1llu << fbit); | ||
204 | } | ||
205 | |||
206 | return v; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Setup one of config[12] attr members based on the | ||
211 | * user input data - temr parameter. | ||
212 | */ | ||
213 | static int pmu_config_term(struct list_head *formats, | ||
214 | struct perf_event_attr *attr, | ||
215 | struct parse_events__term *term) | ||
216 | { | ||
217 | struct perf_pmu__format *format; | ||
218 | __u64 *vp; | ||
219 | |||
220 | /* | ||
221 | * Support only for hardcoded and numnerial terms. | ||
222 | * Hardcoded terms should be already in, so nothing | ||
223 | * to be done for them. | ||
224 | */ | ||
225 | if (parse_events__is_hardcoded_term(term)) | ||
226 | return 0; | ||
227 | |||
228 | if (term->type != PARSE_EVENTS__TERM_TYPE_NUM) | ||
229 | return -EINVAL; | ||
230 | |||
231 | format = pmu_find_format(formats, term->config); | ||
232 | if (!format) | ||
233 | return -EINVAL; | ||
234 | |||
235 | switch (format->value) { | ||
236 | case PERF_PMU_FORMAT_VALUE_CONFIG: | ||
237 | vp = &attr->config; | ||
238 | break; | ||
239 | case PERF_PMU_FORMAT_VALUE_CONFIG1: | ||
240 | vp = &attr->config1; | ||
241 | break; | ||
242 | case PERF_PMU_FORMAT_VALUE_CONFIG2: | ||
243 | vp = &attr->config2; | ||
244 | break; | ||
245 | default: | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | |||
249 | *vp |= pmu_format_value(format->bits, term->val.num); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, | ||
254 | struct list_head *head_terms) | ||
255 | { | ||
256 | struct parse_events__term *term, *h; | ||
257 | |||
258 | list_for_each_entry_safe(term, h, head_terms, list) | ||
259 | if (pmu_config_term(formats, attr, term)) | ||
260 | return -EINVAL; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Configures event's 'attr' parameter based on the: | ||
267 | * 1) users input - specified in terms parameter | ||
268 | * 2) pmu format definitions - specified by pmu parameter | ||
269 | */ | ||
270 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
271 | struct list_head *head_terms) | ||
272 | { | ||
273 | attr->type = pmu->type; | ||
274 | return pmu_config(&pmu->format, attr, head_terms); | ||
275 | } | ||
276 | |||
277 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
278 | int config, unsigned long *bits) | ||
279 | { | ||
280 | struct perf_pmu__format *format; | ||
281 | |||
282 | format = zalloc(sizeof(*format)); | ||
283 | if (!format) | ||
284 | return -ENOMEM; | ||
285 | |||
286 | format->name = strdup(name); | ||
287 | format->value = config; | ||
288 | memcpy(format->bits, bits, sizeof(format->bits)); | ||
289 | |||
290 | list_add_tail(&format->list, list); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | void perf_pmu__set_format(unsigned long *bits, long from, long to) | ||
295 | { | ||
296 | long b; | ||
297 | |||
298 | if (!to) | ||
299 | to = from; | ||
300 | |||
301 | memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); | ||
302 | for (b = from; b <= to; b++) | ||
303 | set_bit(b, bits); | ||
304 | } | ||
305 | |||
306 | /* Simulated format definitions. */ | ||
307 | static struct test_format { | ||
308 | const char *name; | ||
309 | const char *value; | ||
310 | } test_formats[] = { | ||
311 | { "krava01", "config:0-1,62-63\n", }, | ||
312 | { "krava02", "config:10-17\n", }, | ||
313 | { "krava03", "config:5\n", }, | ||
314 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
315 | { "krava12", "config1:63\n", }, | ||
316 | { "krava13", "config1:45-47\n", }, | ||
317 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
318 | { "krava22", "config2:8,18,48,58\n", }, | ||
319 | { "krava23", "config2:28-29,38\n", }, | ||
320 | }; | ||
321 | |||
322 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
323 | |||
324 | /* Simulated users input. */ | ||
325 | static struct parse_events__term test_terms[] = { | ||
326 | { | ||
327 | .config = (char *) "krava01", | ||
328 | .val.num = 15, | ||
329 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
330 | }, | ||
331 | { | ||
332 | .config = (char *) "krava02", | ||
333 | .val.num = 170, | ||
334 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
335 | }, | ||
336 | { | ||
337 | .config = (char *) "krava03", | ||
338 | .val.num = 1, | ||
339 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
340 | }, | ||
341 | { | ||
342 | .config = (char *) "krava11", | ||
343 | .val.num = 27, | ||
344 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
345 | }, | ||
346 | { | ||
347 | .config = (char *) "krava12", | ||
348 | .val.num = 1, | ||
349 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
350 | }, | ||
351 | { | ||
352 | .config = (char *) "krava13", | ||
353 | .val.num = 2, | ||
354 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
355 | }, | ||
356 | { | ||
357 | .config = (char *) "krava21", | ||
358 | .val.num = 119, | ||
359 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
360 | }, | ||
361 | { | ||
362 | .config = (char *) "krava22", | ||
363 | .val.num = 11, | ||
364 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
365 | }, | ||
366 | { | ||
367 | .config = (char *) "krava23", | ||
368 | .val.num = 2, | ||
369 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
370 | }, | ||
371 | }; | ||
372 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
373 | |||
374 | /* | ||
375 | * Prepare format directory data, exported by kernel | ||
376 | * at /sys/bus/event_source/devices/<dev>/format. | ||
377 | */ | ||
378 | static char *test_format_dir_get(void) | ||
379 | { | ||
380 | static char dir[PATH_MAX]; | ||
381 | unsigned int i; | ||
382 | |||
383 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
384 | if (!mkdtemp(dir)) | ||
385 | return NULL; | ||
386 | |||
387 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
388 | static char name[PATH_MAX]; | ||
389 | struct test_format *format = &test_formats[i]; | ||
390 | FILE *file; | ||
391 | |||
392 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
393 | |||
394 | file = fopen(name, "w"); | ||
395 | if (!file) | ||
396 | return NULL; | ||
397 | |||
398 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
399 | break; | ||
400 | |||
401 | fclose(file); | ||
402 | } | ||
403 | |||
404 | return dir; | ||
405 | } | ||
406 | |||
407 | /* Cleanup format directory. */ | ||
408 | static int test_format_dir_put(char *dir) | ||
409 | { | ||
410 | char buf[PATH_MAX]; | ||
411 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
412 | if (system(buf)) | ||
413 | return -1; | ||
414 | |||
415 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
416 | return system(buf); | ||
417 | } | ||
418 | |||
419 | static struct list_head *test_terms_list(void) | ||
420 | { | ||
421 | static LIST_HEAD(terms); | ||
422 | unsigned int i; | ||
423 | |||
424 | for (i = 0; i < TERMS_CNT; i++) | ||
425 | list_add_tail(&test_terms[i].list, &terms); | ||
426 | |||
427 | return &terms; | ||
428 | } | ||
429 | |||
430 | #undef TERMS_CNT | ||
431 | |||
432 | int perf_pmu__test(void) | ||
433 | { | ||
434 | char *format = test_format_dir_get(); | ||
435 | LIST_HEAD(formats); | ||
436 | struct list_head *terms = test_terms_list(); | ||
437 | int ret; | ||
438 | |||
439 | if (!format) | ||
440 | return -EINVAL; | ||
441 | |||
442 | do { | ||
443 | struct perf_event_attr attr; | ||
444 | |||
445 | memset(&attr, 0, sizeof(attr)); | ||
446 | |||
447 | ret = pmu_format_parse(format, &formats); | ||
448 | if (ret) | ||
449 | break; | ||
450 | |||
451 | ret = pmu_config(&formats, &attr, terms); | ||
452 | if (ret) | ||
453 | break; | ||
454 | |||
455 | ret = -EINVAL; | ||
456 | |||
457 | if (attr.config != 0xc00000000002a823) | ||
458 | break; | ||
459 | if (attr.config1 != 0x8000400000000145) | ||
460 | break; | ||
461 | if (attr.config2 != 0x0400000020041d07) | ||
462 | break; | ||
463 | |||
464 | ret = 0; | ||
465 | } while (0); | ||
466 | |||
467 | test_format_dir_put(format); | ||
468 | return ret; | ||
469 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h new file mode 100644 index 000000000000..68c0db965e1f --- /dev/null +++ b/tools/perf/util/pmu.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __PMU_H | ||
2 | #define __PMU_H | ||
3 | |||
4 | #include <linux/bitops.h> | ||
5 | #include "../../../include/linux/perf_event.h" | ||
6 | |||
7 | enum { | ||
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
9 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
10 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
11 | }; | ||
12 | |||
13 | #define PERF_PMU_FORMAT_BITS 64 | ||
14 | |||
15 | struct perf_pmu__format { | ||
16 | char *name; | ||
17 | int value; | ||
18 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
19 | struct list_head list; | ||
20 | }; | ||
21 | |||
22 | struct perf_pmu { | ||
23 | char *name; | ||
24 | __u32 type; | ||
25 | struct list_head format; | ||
26 | struct list_head list; | ||
27 | }; | ||
28 | |||
29 | struct perf_pmu *perf_pmu__find(char *name); | ||
30 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
31 | struct list_head *head_terms); | ||
32 | |||
33 | int perf_pmu_wrap(void); | ||
34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | ||
35 | |||
36 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
37 | int config, unsigned long *bits); | ||
38 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | ||
39 | |||
40 | int perf_pmu__test(void); | ||
41 | #endif /* __PMU_H */ | ||
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l new file mode 100644 index 000000000000..a15d9fbd7c0e --- /dev/null +++ b/tools/perf/util/pmu.l | |||
@@ -0,0 +1,43 @@ | |||
1 | %option prefix="perf_pmu_" | ||
2 | |||
3 | %{ | ||
4 | #include <stdlib.h> | ||
5 | #include <linux/bitops.h> | ||
6 | #include "pmu.h" | ||
7 | #include "pmu-bison.h" | ||
8 | |||
9 | static int value(int base) | ||
10 | { | ||
11 | long num; | ||
12 | |||
13 | errno = 0; | ||
14 | num = strtoul(perf_pmu_text, NULL, base); | ||
15 | if (errno) | ||
16 | return PP_ERROR; | ||
17 | |||
18 | perf_pmu_lval.num = num; | ||
19 | return PP_VALUE; | ||
20 | } | ||
21 | |||
22 | %} | ||
23 | |||
24 | num_dec [0-9]+ | ||
25 | |||
26 | %% | ||
27 | |||
28 | {num_dec} { return value(10); } | ||
29 | config { return PP_CONFIG; } | ||
30 | config1 { return PP_CONFIG1; } | ||
31 | config2 { return PP_CONFIG2; } | ||
32 | - { return '-'; } | ||
33 | : { return ':'; } | ||
34 | , { return ','; } | ||
35 | . { ; } | ||
36 | \n { ; } | ||
37 | |||
38 | %% | ||
39 | |||
40 | int perf_pmu_wrap(void) | ||
41 | { | ||
42 | return 1; | ||
43 | } | ||
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y new file mode 100644 index 000000000000..20ea77e93169 --- /dev/null +++ b/tools/perf/util/pmu.y | |||
@@ -0,0 +1,93 @@ | |||
1 | |||
2 | %name-prefix "perf_pmu_" | ||
3 | %parse-param {struct list_head *format} | ||
4 | %parse-param {char *name} | ||
5 | |||
6 | %{ | ||
7 | |||
8 | #include <linux/compiler.h> | ||
9 | #include <linux/list.h> | ||
10 | #include <linux/bitmap.h> | ||
11 | #include <string.h> | ||
12 | #include "pmu.h" | ||
13 | |||
14 | extern int perf_pmu_lex (void); | ||
15 | |||
16 | #define ABORT_ON(val) \ | ||
17 | do { \ | ||
18 | if (val) \ | ||
19 | YYABORT; \ | ||
20 | } while (0) | ||
21 | |||
22 | %} | ||
23 | |||
24 | %token PP_CONFIG PP_CONFIG1 PP_CONFIG2 | ||
25 | %token PP_VALUE PP_ERROR | ||
26 | %type <num> PP_VALUE | ||
27 | %type <bits> bit_term | ||
28 | %type <bits> bits | ||
29 | |||
30 | %union | ||
31 | { | ||
32 | unsigned long num; | ||
33 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
34 | } | ||
35 | |||
36 | %% | ||
37 | |||
38 | format: | ||
39 | format format_term | ||
40 | | | ||
41 | format_term | ||
42 | |||
43 | format_term: | ||
44 | PP_CONFIG ':' bits | ||
45 | { | ||
46 | ABORT_ON(perf_pmu__new_format(format, name, | ||
47 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
48 | $3)); | ||
49 | } | ||
50 | | | ||
51 | PP_CONFIG1 ':' bits | ||
52 | { | ||
53 | ABORT_ON(perf_pmu__new_format(format, name, | ||
54 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
55 | $3)); | ||
56 | } | ||
57 | | | ||
58 | PP_CONFIG2 ':' bits | ||
59 | { | ||
60 | ABORT_ON(perf_pmu__new_format(format, name, | ||
61 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
62 | $3)); | ||
63 | } | ||
64 | |||
65 | bits: | ||
66 | bits ',' bit_term | ||
67 | { | ||
68 | bitmap_or($$, $1, $3, 64); | ||
69 | } | ||
70 | | | ||
71 | bit_term | ||
72 | { | ||
73 | memcpy($$, $1, sizeof($1)); | ||
74 | } | ||
75 | |||
76 | bit_term: | ||
77 | PP_VALUE '-' PP_VALUE | ||
78 | { | ||
79 | perf_pmu__set_format($$, $1, $3); | ||
80 | } | ||
81 | | | ||
82 | PP_VALUE | ||
83 | { | ||
84 | perf_pmu__set_format($$, $1, 0); | ||
85 | } | ||
86 | |||
87 | %% | ||
88 | |||
89 | void perf_pmu_error(struct list_head *list __used, | ||
90 | char *name __used, | ||
91 | char const *msg __used) | ||
92 | { | ||
93 | } | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 2cc162d3b78c..d448984ed789 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -972,10 +972,12 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
972 | struct dwarf_callback_param *param = data; | 972 | struct dwarf_callback_param *param = data; |
973 | struct probe_finder *pf = param->data; | 973 | struct probe_finder *pf = param->data; |
974 | struct perf_probe_point *pp = &pf->pev->point; | 974 | struct perf_probe_point *pp = &pf->pev->point; |
975 | Dwarf_Attribute attr; | ||
975 | 976 | ||
976 | /* Check tag and diename */ | 977 | /* Check tag and diename */ |
977 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 978 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
978 | !die_compare_name(sp_die, pp->function)) | 979 | !die_compare_name(sp_die, pp->function) || |
980 | dwarf_attr(sp_die, DW_AT_declaration, &attr)) | ||
979 | return DWARF_CB_OK; | 981 | return DWARF_CB_OK; |
980 | 982 | ||
981 | /* Check declared file */ | 983 | /* Check declared file */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 002ebbf59f48..9412e3b05f68 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); |
143 | hists__init(&self->hists); | ||
143 | 144 | ||
144 | if (mode == O_RDONLY) { | 145 | if (mode == O_RDONLY) { |
145 | if (perf_session__open(self, force) < 0) | 146 | if (perf_session__open(self, force) < 0) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5dd83c3e2c0c..c0a028c3ebaf 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <errno.h> | 2 | #include <errno.h> |
3 | #include <libgen.h> | ||
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
5 | #include <stdio.h> | 4 | #include <stdio.h> |
6 | #include <string.h> | 5 | #include <string.h> |
@@ -51,6 +50,8 @@ struct symbol_conf symbol_conf = { | |||
51 | 50 | ||
52 | int dso__name_len(const struct dso *dso) | 51 | int dso__name_len(const struct dso *dso) |
53 | { | 52 | { |
53 | if (!dso) | ||
54 | return strlen("[unknown]"); | ||
54 | if (verbose) | 55 | if (verbose) |
55 | return dso->long_name_len; | 56 | return dso->long_name_len; |
56 | 57 | ||
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index a4088ced1e64..dfd1bd8371a4 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -722,7 +722,7 @@ static char *event_read_name(void) | |||
722 | static int event_read_id(void) | 722 | static int event_read_id(void) |
723 | { | 723 | { |
724 | char *token; | 724 | char *token; |
725 | int id; | 725 | int id = -1; |
726 | 726 | ||
727 | if (read_expected_item(EVENT_ITEM, "ID") < 0) | 727 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
728 | return -1; | 728 | return -1; |
@@ -731,15 +731,13 @@ static int event_read_id(void) | |||
731 | return -1; | 731 | return -1; |
732 | 732 | ||
733 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 733 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
734 | goto fail; | 734 | goto free; |
735 | 735 | ||
736 | id = strtoul(token, NULL, 0); | 736 | id = strtoul(token, NULL, 0); |
737 | free_token(token); | ||
738 | return id; | ||
739 | 737 | ||
740 | fail: | 738 | free: |
741 | free_token(token); | 739 | free_token(token); |
742 | return -1; | 740 | return id; |
743 | } | 741 | } |
744 | 742 | ||
745 | static int field_is_string(struct format_field *field) | 743 | static int field_is_string(struct format_field *field) |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 84d761b730c1..6ee82f60feaf 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -49,6 +49,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout, | |||
49 | const char *format, ...); | 49 | const char *format, ...); |
50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); | 50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); |
51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); | 51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); |
52 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
53 | const char *exit_msg, int delay_sec); | ||
52 | 54 | ||
53 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); | 55 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); |
54 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); | 56 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index fa530fcc764a..d7a1c4afe28b 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -879,6 +879,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
879 | char *options[16]; | 879 | char *options[16]; |
880 | int nr_options = 0; | 880 | int nr_options = 0; |
881 | int key = -1; | 881 | int key = -1; |
882 | char buf[64]; | ||
882 | 883 | ||
883 | if (browser == NULL) | 884 | if (browser == NULL) |
884 | return -1; | 885 | return -1; |
@@ -933,6 +934,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
933 | goto zoom_dso; | 934 | goto zoom_dso; |
934 | case 't': | 935 | case 't': |
935 | goto zoom_thread; | 936 | goto zoom_thread; |
937 | case 's': | ||
938 | if (ui_browser__input_window("Symbol to show", | ||
939 | "Please enter the name of symbol you want to see", | ||
940 | buf, "ENTER: OK, ESC: Cancel", | ||
941 | delay_secs * 2) == K_ENTER) { | ||
942 | self->symbol_filter_str = *buf ? buf : NULL; | ||
943 | hists__filter_by_symbol(self); | ||
944 | hist_browser__reset(browser); | ||
945 | } | ||
946 | continue; | ||
936 | case K_F1: | 947 | case K_F1: |
937 | case 'h': | 948 | case 'h': |
938 | case '?': | 949 | case '?': |
@@ -950,7 +961,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
950 | "C Collapse all callchains\n" | 961 | "C Collapse all callchains\n" |
951 | "E Expand all callchains\n" | 962 | "E Expand all callchains\n" |
952 | "d Zoom into current DSO\n" | 963 | "d Zoom into current DSO\n" |
953 | "t Zoom into current Thread"); | 964 | "t Zoom into current Thread\n" |
965 | "s Filter symbol by name"); | ||
954 | continue; | 966 | continue; |
955 | case K_ENTER: | 967 | case K_ENTER: |
956 | case K_RIGHT: | 968 | case K_RIGHT: |
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h index 3458b1985761..809eca5707fa 100644 --- a/tools/perf/util/ui/keysyms.h +++ b/tools/perf/util/ui/keysyms.h | |||
@@ -16,6 +16,8 @@ | |||
16 | #define K_TAB '\t' | 16 | #define K_TAB '\t' |
17 | #define K_UNTAB SL_KEY_UNTAB | 17 | #define K_UNTAB SL_KEY_UNTAB |
18 | #define K_UP SL_KEY_UP | 18 | #define K_UP SL_KEY_UP |
19 | #define K_BKSPC 0x7f | ||
20 | #define K_DEL SL_KEY_DELETE | ||
19 | 21 | ||
20 | /* Not really keys */ | 22 | /* Not really keys */ |
21 | #define K_TIMER -1 | 23 | #define K_TIMER -1 |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 45daa7c41dad..ad4374a16bb0 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[]) | |||
69 | return popup_menu__run(&menu); | 69 | return popup_menu__run(&menu); |
70 | } | 70 | } |
71 | 71 | ||
72 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
73 | const char *exit_msg, int delay_secs) | ||
74 | { | ||
75 | int x, y, len, key; | ||
76 | int max_len = 60, nr_lines = 0; | ||
77 | static char buf[50]; | ||
78 | const char *t; | ||
79 | |||
80 | t = text; | ||
81 | while (1) { | ||
82 | const char *sep = strchr(t, '\n'); | ||
83 | |||
84 | if (sep == NULL) | ||
85 | sep = strchr(t, '\0'); | ||
86 | len = sep - t; | ||
87 | if (max_len < len) | ||
88 | max_len = len; | ||
89 | ++nr_lines; | ||
90 | if (*sep == '\0') | ||
91 | break; | ||
92 | t = sep + 1; | ||
93 | } | ||
94 | |||
95 | max_len += 2; | ||
96 | nr_lines += 8; | ||
97 | y = SLtt_Screen_Rows / 2 - nr_lines / 2; | ||
98 | x = SLtt_Screen_Cols / 2 - max_len / 2; | ||
99 | |||
100 | SLsmg_set_color(0); | ||
101 | SLsmg_draw_box(y, x++, nr_lines, max_len); | ||
102 | if (title) { | ||
103 | SLsmg_gotorc(y, x + 1); | ||
104 | SLsmg_write_string((char *)title); | ||
105 | } | ||
106 | SLsmg_gotorc(++y, x); | ||
107 | nr_lines -= 7; | ||
108 | max_len -= 2; | ||
109 | SLsmg_write_wrapped_string((unsigned char *)text, y, x, | ||
110 | nr_lines, max_len, 1); | ||
111 | y += nr_lines; | ||
112 | len = 5; | ||
113 | while (len--) { | ||
114 | SLsmg_gotorc(y + len - 1, x); | ||
115 | SLsmg_write_nstring((char *)" ", max_len); | ||
116 | } | ||
117 | SLsmg_draw_box(y++, x + 1, 3, max_len - 2); | ||
118 | |||
119 | SLsmg_gotorc(y + 3, x); | ||
120 | SLsmg_write_nstring((char *)exit_msg, max_len); | ||
121 | SLsmg_refresh(); | ||
122 | |||
123 | x += 2; | ||
124 | len = 0; | ||
125 | key = ui__getch(delay_secs); | ||
126 | while (key != K_TIMER && key != K_ENTER && key != K_ESC) { | ||
127 | if (key == K_BKSPC) { | ||
128 | if (len == 0) | ||
129 | goto next_key; | ||
130 | SLsmg_gotorc(y, x + --len); | ||
131 | SLsmg_write_char(' '); | ||
132 | } else { | ||
133 | buf[len] = key; | ||
134 | SLsmg_gotorc(y, x + len++); | ||
135 | SLsmg_write_char(key); | ||
136 | } | ||
137 | SLsmg_refresh(); | ||
138 | |||
139 | /* XXX more graceful overflow handling needed */ | ||
140 | if (len == sizeof(buf) - 1) { | ||
141 | ui_helpline__push("maximum size of symbol name reached!"); | ||
142 | key = K_ENTER; | ||
143 | break; | ||
144 | } | ||
145 | next_key: | ||
146 | key = ui__getch(delay_secs); | ||
147 | } | ||
148 | |||
149 | buf[len] = '\0'; | ||
150 | strncpy(input, buf, len+1); | ||
151 | return key; | ||
152 | } | ||
153 | |||
72 | int ui__question_window(const char *title, const char *text, | 154 | int ui__question_window(const char *title, const char *text, |
73 | const char *exit_msg, int delay_secs) | 155 | const char *exit_msg, int delay_secs) |
74 | { | 156 | { |