diff options
104 files changed, 4417 insertions, 885 deletions
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 0bb1335313b2..53966d65591e 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
| @@ -174,6 +174,7 @@ | |||
| 174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ | 174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ |
| 175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ | 175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ |
| 176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ | 176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ |
| 177 | #define X86_FEATURE_BPEXT (6*32+26) /* data breakpoint extension */ | ||
| 177 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ | 178 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ |
| 178 | 179 | ||
| 179 | /* | 180 | /* |
| @@ -383,6 +384,7 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; | |||
| 383 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) | 384 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) |
| 384 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) | 385 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) |
| 385 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) | 386 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) |
| 387 | #define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT) | ||
| 386 | 388 | ||
| 387 | #if __GNUC__ >= 4 | 389 | #if __GNUC__ >= 4 |
| 388 | extern void warn_pre_alternatives(void); | 390 | extern void warn_pre_alternatives(void); |
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 61fd18b83b6c..12cb66f6d3a5 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h | |||
| @@ -114,5 +114,10 @@ static inline void debug_stack_usage_inc(void) { } | |||
| 114 | static inline void debug_stack_usage_dec(void) { } | 114 | static inline void debug_stack_usage_dec(void) { } |
| 115 | #endif /* X86_64 */ | 115 | #endif /* X86_64 */ |
| 116 | 116 | ||
| 117 | #ifdef CONFIG_CPU_SUP_AMD | ||
| 118 | extern void set_dr_addr_mask(unsigned long mask, int dr); | ||
| 119 | #else | ||
| 120 | static inline void set_dr_addr_mask(unsigned long mask, int dr) { } | ||
| 121 | #endif | ||
| 117 | 122 | ||
| 118 | #endif /* _ASM_X86_DEBUGREG_H */ | 123 | #endif /* _ASM_X86_DEBUGREG_H */ |
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index ef1c4d2d41ec..6c98be864a75 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | */ | 12 | */ |
| 13 | struct arch_hw_breakpoint { | 13 | struct arch_hw_breakpoint { |
| 14 | unsigned long address; | 14 | unsigned long address; |
| 15 | unsigned long mask; | ||
| 15 | u8 len; | 16 | u8 len; |
| 16 | u8 type; | 17 | u8 type; |
| 17 | }; | 18 | }; |
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 8dfc9fd094a3..dc0f6ed35b08 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h | |||
| @@ -177,6 +177,9 @@ struct x86_pmu_capability { | |||
| 177 | #define IBS_CAPS_BRNTRGT (1U<<5) | 177 | #define IBS_CAPS_BRNTRGT (1U<<5) |
| 178 | #define IBS_CAPS_OPCNTEXT (1U<<6) | 178 | #define IBS_CAPS_OPCNTEXT (1U<<6) |
| 179 | #define IBS_CAPS_RIPINVALIDCHK (1U<<7) | 179 | #define IBS_CAPS_RIPINVALIDCHK (1U<<7) |
| 180 | #define IBS_CAPS_OPBRNFUSE (1U<<8) | ||
| 181 | #define IBS_CAPS_FETCHCTLEXTD (1U<<9) | ||
| 182 | #define IBS_CAPS_OPDATA4 (1U<<10) | ||
| 180 | 183 | ||
| 181 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ | 184 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ |
| 182 | | IBS_CAPS_FETCHSAM \ | 185 | | IBS_CAPS_FETCHSAM \ |
diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index e21331ce368f..b1fb4fae03d3 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h | |||
| @@ -206,11 +206,16 @@ | |||
| 206 | #define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1) | 206 | #define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1) |
| 207 | #define MSR_AMD64_IBSCTL 0xc001103a | 207 | #define MSR_AMD64_IBSCTL 0xc001103a |
| 208 | #define MSR_AMD64_IBSBRTARGET 0xc001103b | 208 | #define MSR_AMD64_IBSBRTARGET 0xc001103b |
| 209 | #define MSR_AMD64_IBSOPDATA4 0xc001103d | ||
| 209 | #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ | 210 | #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ |
| 210 | 211 | ||
| 211 | /* Fam 16h MSRs */ | 212 | /* Fam 16h MSRs */ |
| 212 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 | 213 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 |
| 213 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 | 214 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 |
| 215 | #define MSR_F16H_DR1_ADDR_MASK 0xc0011019 | ||
| 216 | #define MSR_F16H_DR2_ADDR_MASK 0xc001101a | ||
| 217 | #define MSR_F16H_DR3_ADDR_MASK 0xc001101b | ||
| 218 | #define MSR_F16H_DR0_ADDR_MASK 0xc0011027 | ||
| 214 | 219 | ||
| 215 | /* Fam 15h MSRs */ | 220 | /* Fam 15h MSRs */ |
| 216 | #define MSR_F15H_PERF_CTL 0xc0010200 | 221 | #define MSR_F15H_PERF_CTL 0xc0010200 |
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 813d29d00a17..abe4ec760db3 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
| @@ -870,3 +870,22 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) | |||
| 870 | 870 | ||
| 871 | return false; | 871 | return false; |
| 872 | } | 872 | } |
| 873 | |||
| 874 | void set_dr_addr_mask(unsigned long mask, int dr) | ||
| 875 | { | ||
| 876 | if (!cpu_has_bpext) | ||
| 877 | return; | ||
| 878 | |||
| 879 | switch (dr) { | ||
| 880 | case 0: | ||
| 881 | wrmsr(MSR_F16H_DR0_ADDR_MASK, mask, 0); | ||
| 882 | break; | ||
| 883 | case 1: | ||
| 884 | case 2: | ||
| 885 | case 3: | ||
| 886 | wrmsr(MSR_F16H_DR1_ADDR_MASK - 1 + dr, mask, 0); | ||
| 887 | break; | ||
| 888 | default: | ||
| 889 | break; | ||
| 890 | } | ||
| 891 | } | ||
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index fc5eb390b368..4e6cdb0ddc70 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h | |||
| @@ -253,6 +253,10 @@ struct cpu_hw_events { | |||
| 253 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ | 253 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ |
| 254 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) | 254 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) |
| 255 | 255 | ||
| 256 | /* Like UEVENT_CONSTRAINT, but match flags too */ | ||
| 257 | #define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \ | ||
| 258 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS) | ||
| 259 | |||
| 256 | #define INTEL_PLD_CONSTRAINT(c, n) \ | 260 | #define INTEL_PLD_CONSTRAINT(c, n) \ |
| 257 | __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ | 261 | __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ |
| 258 | HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT) | 262 | HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT) |
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index cbb1be3ed9e4..a61f5c6911da 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c | |||
| @@ -565,6 +565,21 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) | |||
| 565 | perf_ibs->offset_max, | 565 | perf_ibs->offset_max, |
| 566 | offset + 1); | 566 | offset + 1); |
| 567 | } while (offset < offset_max); | 567 | } while (offset < offset_max); |
| 568 | if (event->attr.sample_type & PERF_SAMPLE_RAW) { | ||
| 569 | /* | ||
| 570 | * Read IbsBrTarget and IbsOpData4 separately | ||
| 571 | * depending on their availability. | ||
| 572 | * Can't add to offset_max as they are staggered | ||
| 573 | */ | ||
| 574 | if (ibs_caps & IBS_CAPS_BRNTRGT) { | ||
| 575 | rdmsrl(MSR_AMD64_IBSBRTARGET, *buf++); | ||
| 576 | size++; | ||
| 577 | } | ||
| 578 | if (ibs_caps & IBS_CAPS_OPDATA4) { | ||
| 579 | rdmsrl(MSR_AMD64_IBSOPDATA4, *buf++); | ||
| 580 | size++; | ||
| 581 | } | ||
| 582 | } | ||
| 568 | ibs_data.size = sizeof(u64) * size; | 583 | ibs_data.size = sizeof(u64) * size; |
| 569 | 584 | ||
| 570 | regs = *iregs; | 585 | regs = *iregs; |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 46211bcc813e..495ae9793628 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c | |||
| @@ -552,18 +552,18 @@ int intel_pmu_drain_bts_buffer(void) | |||
| 552 | * PEBS | 552 | * PEBS |
| 553 | */ | 553 | */ |
| 554 | struct event_constraint intel_core2_pebs_event_constraints[] = { | 554 | struct event_constraint intel_core2_pebs_event_constraints[] = { |
| 555 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ | 555 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ |
| 556 | INTEL_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ | 556 | INTEL_FLAGS_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ |
| 557 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ | 557 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ |
| 558 | INTEL_UEVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */ | 558 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */ |
| 559 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ | 559 | INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ |
| 560 | EVENT_CONSTRAINT_END | 560 | EVENT_CONSTRAINT_END |
| 561 | }; | 561 | }; |
| 562 | 562 | ||
| 563 | struct event_constraint intel_atom_pebs_event_constraints[] = { | 563 | struct event_constraint intel_atom_pebs_event_constraints[] = { |
| 564 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ | 564 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ |
| 565 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */ | 565 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */ |
| 566 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ | 566 | INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ |
| 567 | EVENT_CONSTRAINT_END | 567 | EVENT_CONSTRAINT_END |
| 568 | }; | 568 | }; |
| 569 | 569 | ||
| @@ -577,36 +577,36 @@ struct event_constraint intel_slm_pebs_event_constraints[] = { | |||
| 577 | 577 | ||
| 578 | struct event_constraint intel_nehalem_pebs_event_constraints[] = { | 578 | struct event_constraint intel_nehalem_pebs_event_constraints[] = { |
| 579 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ | 579 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ |
| 580 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 580 | INTEL_FLAGS_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
| 581 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 581 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
| 582 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */ | 582 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */ |
| 583 | INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ | 583 | INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ |
| 584 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ | 584 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ |
| 585 | INTEL_UEVENT_CONSTRAINT(0x02c5, 0xf), /* BR_MISP_RETIRED.NEAR_CALL */ | 585 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x02c5, 0xf), /* BR_MISP_RETIRED.NEAR_CALL */ |
| 586 | INTEL_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ | 586 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ |
| 587 | INTEL_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ | 587 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ |
| 588 | INTEL_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ | 588 | INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ |
| 589 | INTEL_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ | 589 | INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ |
| 590 | EVENT_CONSTRAINT_END | 590 | EVENT_CONSTRAINT_END |
| 591 | }; | 591 | }; |
| 592 | 592 | ||
| 593 | struct event_constraint intel_westmere_pebs_event_constraints[] = { | 593 | struct event_constraint intel_westmere_pebs_event_constraints[] = { |
| 594 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ | 594 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ |
| 595 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 595 | INTEL_FLAGS_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
| 596 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 596 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
| 597 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */ | 597 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */ |
| 598 | INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ | 598 | INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ |
| 599 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ | 599 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ |
| 600 | INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ | 600 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ |
| 601 | INTEL_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ | 601 | INTEL_FLAGS_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ |
| 602 | INTEL_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ | 602 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ |
| 603 | INTEL_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ | 603 | INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ |
| 604 | INTEL_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ | 604 | INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ |
| 605 | EVENT_CONSTRAINT_END | 605 | EVENT_CONSTRAINT_END |
| 606 | }; | 606 | }; |
| 607 | 607 | ||
| 608 | struct event_constraint intel_snb_pebs_event_constraints[] = { | 608 | struct event_constraint intel_snb_pebs_event_constraints[] = { |
| 609 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ | 609 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ |
| 610 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ | 610 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ |
| 611 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ | 611 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ |
| 612 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ | 612 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ |
| @@ -617,7 +617,7 @@ struct event_constraint intel_snb_pebs_event_constraints[] = { | |||
| 617 | }; | 617 | }; |
| 618 | 618 | ||
| 619 | struct event_constraint intel_ivb_pebs_event_constraints[] = { | 619 | struct event_constraint intel_ivb_pebs_event_constraints[] = { |
| 620 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ | 620 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ |
| 621 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ | 621 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ |
| 622 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ | 622 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ |
| 623 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ | 623 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ |
| @@ -628,7 +628,7 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = { | |||
| 628 | }; | 628 | }; |
| 629 | 629 | ||
| 630 | struct event_constraint intel_hsw_pebs_event_constraints[] = { | 630 | struct event_constraint intel_hsw_pebs_event_constraints[] = { |
| 631 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ | 631 | INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ |
| 632 | INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */ | 632 | INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */ |
| 633 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ | 633 | /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ |
| 634 | INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), | 634 | INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), |
| @@ -886,6 +886,29 @@ static void __intel_pmu_pebs_event(struct perf_event *event, | |||
| 886 | regs.bp = pebs->bp; | 886 | regs.bp = pebs->bp; |
| 887 | regs.sp = pebs->sp; | 887 | regs.sp = pebs->sp; |
| 888 | 888 | ||
| 889 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
| 890 | regs.ax = pebs->ax; | ||
| 891 | regs.bx = pebs->bx; | ||
| 892 | regs.cx = pebs->cx; | ||
| 893 | regs.dx = pebs->dx; | ||
| 894 | regs.si = pebs->si; | ||
| 895 | regs.di = pebs->di; | ||
| 896 | regs.bp = pebs->bp; | ||
| 897 | regs.sp = pebs->sp; | ||
| 898 | |||
| 899 | regs.flags = pebs->flags; | ||
| 900 | #ifndef CONFIG_X86_32 | ||
| 901 | regs.r8 = pebs->r8; | ||
| 902 | regs.r9 = pebs->r9; | ||
| 903 | regs.r10 = pebs->r10; | ||
| 904 | regs.r11 = pebs->r11; | ||
| 905 | regs.r12 = pebs->r12; | ||
| 906 | regs.r13 = pebs->r13; | ||
| 907 | regs.r14 = pebs->r14; | ||
| 908 | regs.r15 = pebs->r15; | ||
| 909 | #endif | ||
| 910 | } | ||
| 911 | |||
| 889 | if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) { | 912 | if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) { |
| 890 | regs.ip = pebs->real_ip; | 913 | regs.ip = pebs->real_ip; |
| 891 | regs.flags |= PERF_EFLAGS_EXACT; | 914 | regs.flags |= PERF_EFLAGS_EXACT; |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index f9ed429d6e4f..745b158e9a65 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c | |||
| @@ -449,7 +449,11 @@ static struct attribute *snbep_uncore_qpi_formats_attr[] = { | |||
| 449 | static struct uncore_event_desc snbep_uncore_imc_events[] = { | 449 | static struct uncore_event_desc snbep_uncore_imc_events[] = { |
| 450 | INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"), | 450 | INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"), |
| 451 | INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), | 451 | INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), |
| 452 | INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"), | ||
| 453 | INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"), | ||
| 452 | INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), | 454 | INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), |
| 455 | INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"), | ||
| 456 | INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"), | ||
| 453 | { /* end: all zeroes */ }, | 457 | { /* end: all zeroes */ }, |
| 454 | }; | 458 | }; |
| 455 | 459 | ||
| @@ -2036,7 +2040,11 @@ static struct intel_uncore_type hswep_uncore_ha = { | |||
| 2036 | static struct uncore_event_desc hswep_uncore_imc_events[] = { | 2040 | static struct uncore_event_desc hswep_uncore_imc_events[] = { |
| 2037 | INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"), | 2041 | INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"), |
| 2038 | INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), | 2042 | INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), |
| 2043 | INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"), | ||
| 2044 | INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"), | ||
| 2039 | INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), | 2045 | INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), |
| 2046 | INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"), | ||
| 2047 | INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"), | ||
| 2040 | { /* end: all zeroes */ }, | 2048 | { /* end: all zeroes */ }, |
| 2041 | }; | 2049 | }; |
| 2042 | 2050 | ||
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 3d5fb509bdeb..7114ba220fd4 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c | |||
| @@ -126,6 +126,8 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
| 126 | *dr7 |= encode_dr7(i, info->len, info->type); | 126 | *dr7 |= encode_dr7(i, info->len, info->type); |
| 127 | 127 | ||
| 128 | set_debugreg(*dr7, 7); | 128 | set_debugreg(*dr7, 7); |
| 129 | if (info->mask) | ||
| 130 | set_dr_addr_mask(info->mask, i); | ||
| 129 | 131 | ||
| 130 | return 0; | 132 | return 0; |
| 131 | } | 133 | } |
| @@ -161,29 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | |||
| 161 | *dr7 &= ~__encode_dr7(i, info->len, info->type); | 163 | *dr7 &= ~__encode_dr7(i, info->len, info->type); |
| 162 | 164 | ||
| 163 | set_debugreg(*dr7, 7); | 165 | set_debugreg(*dr7, 7); |
| 164 | } | 166 | if (info->mask) |
| 165 | 167 | set_dr_addr_mask(0, i); | |
| 166 | static int get_hbp_len(u8 hbp_len) | ||
| 167 | { | ||
| 168 | unsigned int len_in_bytes = 0; | ||
| 169 | |||
| 170 | switch (hbp_len) { | ||
| 171 | case X86_BREAKPOINT_LEN_1: | ||
| 172 | len_in_bytes = 1; | ||
| 173 | break; | ||
| 174 | case X86_BREAKPOINT_LEN_2: | ||
| 175 | len_in_bytes = 2; | ||
| 176 | break; | ||
| 177 | case X86_BREAKPOINT_LEN_4: | ||
| 178 | len_in_bytes = 4; | ||
| 179 | break; | ||
| 180 | #ifdef CONFIG_X86_64 | ||
| 181 | case X86_BREAKPOINT_LEN_8: | ||
| 182 | len_in_bytes = 8; | ||
| 183 | break; | ||
| 184 | #endif | ||
| 185 | } | ||
| 186 | return len_in_bytes; | ||
| 187 | } | 168 | } |
| 188 | 169 | ||
| 189 | /* | 170 | /* |
| @@ -196,7 +177,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) | |||
| 196 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 177 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
| 197 | 178 | ||
| 198 | va = info->address; | 179 | va = info->address; |
| 199 | len = get_hbp_len(info->len); | 180 | len = bp->attr.bp_len; |
| 200 | 181 | ||
| 201 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); | 182 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); |
| 202 | } | 183 | } |
| @@ -277,6 +258,8 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
| 277 | } | 258 | } |
| 278 | 259 | ||
| 279 | /* Len */ | 260 | /* Len */ |
| 261 | info->mask = 0; | ||
| 262 | |||
| 280 | switch (bp->attr.bp_len) { | 263 | switch (bp->attr.bp_len) { |
| 281 | case HW_BREAKPOINT_LEN_1: | 264 | case HW_BREAKPOINT_LEN_1: |
| 282 | info->len = X86_BREAKPOINT_LEN_1; | 265 | info->len = X86_BREAKPOINT_LEN_1; |
| @@ -293,11 +276,17 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
| 293 | break; | 276 | break; |
| 294 | #endif | 277 | #endif |
| 295 | default: | 278 | default: |
| 296 | return -EINVAL; | 279 | if (!is_power_of_2(bp->attr.bp_len)) |
| 280 | return -EINVAL; | ||
| 281 | if (!cpu_has_bpext) | ||
| 282 | return -EOPNOTSUPP; | ||
| 283 | info->mask = bp->attr.bp_len - 1; | ||
| 284 | info->len = X86_BREAKPOINT_LEN_1; | ||
| 297 | } | 285 | } |
| 298 | 286 | ||
| 299 | return 0; | 287 | return 0; |
| 300 | } | 288 | } |
| 289 | |||
| 301 | /* | 290 | /* |
| 302 | * Validate the arch-specific HW Breakpoint register settings | 291 | * Validate the arch-specific HW Breakpoint register settings |
| 303 | */ | 292 | */ |
| @@ -312,11 +301,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
| 312 | if (ret) | 301 | if (ret) |
| 313 | return ret; | 302 | return ret; |
| 314 | 303 | ||
| 315 | ret = -EINVAL; | ||
| 316 | |||
| 317 | switch (info->len) { | 304 | switch (info->len) { |
| 318 | case X86_BREAKPOINT_LEN_1: | 305 | case X86_BREAKPOINT_LEN_1: |
| 319 | align = 0; | 306 | align = 0; |
| 307 | if (info->mask) | ||
| 308 | align = info->mask; | ||
| 320 | break; | 309 | break; |
| 321 | case X86_BREAKPOINT_LEN_2: | 310 | case X86_BREAKPOINT_LEN_2: |
| 322 | align = 1; | 311 | align = 1; |
| @@ -330,7 +319,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
| 330 | break; | 319 | break; |
| 331 | #endif | 320 | #endif |
| 332 | default: | 321 | default: |
| 333 | return ret; | 322 | WARN_ON_ONCE(1); |
| 334 | } | 323 | } |
| 335 | 324 | ||
| 336 | /* | 325 | /* |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 893a0d07986f..486e84ccb1f9 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -79,7 +79,7 @@ struct perf_branch_stack { | |||
| 79 | struct perf_branch_entry entries[0]; | 79 | struct perf_branch_entry entries[0]; |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | struct perf_regs_user { | 82 | struct perf_regs { |
| 83 | __u64 abi; | 83 | __u64 abi; |
| 84 | struct pt_regs *regs; | 84 | struct pt_regs *regs; |
| 85 | }; | 85 | }; |
| @@ -580,34 +580,40 @@ extern u64 perf_event_read_value(struct perf_event *event, | |||
| 580 | 580 | ||
| 581 | 581 | ||
| 582 | struct perf_sample_data { | 582 | struct perf_sample_data { |
| 583 | u64 type; | 583 | /* |
| 584 | * Fields set by perf_sample_data_init(), group so as to | ||
| 585 | * minimize the cachelines touched. | ||
| 586 | */ | ||
| 587 | u64 addr; | ||
| 588 | struct perf_raw_record *raw; | ||
| 589 | struct perf_branch_stack *br_stack; | ||
| 590 | u64 period; | ||
| 591 | u64 weight; | ||
| 592 | u64 txn; | ||
| 593 | union perf_mem_data_src data_src; | ||
| 584 | 594 | ||
| 595 | /* | ||
| 596 | * The other fields, optionally {set,used} by | ||
| 597 | * perf_{prepare,output}_sample(). | ||
| 598 | */ | ||
| 599 | u64 type; | ||
| 585 | u64 ip; | 600 | u64 ip; |
| 586 | struct { | 601 | struct { |
| 587 | u32 pid; | 602 | u32 pid; |
| 588 | u32 tid; | 603 | u32 tid; |
| 589 | } tid_entry; | 604 | } tid_entry; |
| 590 | u64 time; | 605 | u64 time; |
| 591 | u64 addr; | ||
| 592 | u64 id; | 606 | u64 id; |
| 593 | u64 stream_id; | 607 | u64 stream_id; |
| 594 | struct { | 608 | struct { |
| 595 | u32 cpu; | 609 | u32 cpu; |
| 596 | u32 reserved; | 610 | u32 reserved; |
| 597 | } cpu_entry; | 611 | } cpu_entry; |
| 598 | u64 period; | ||
| 599 | union perf_mem_data_src data_src; | ||
| 600 | struct perf_callchain_entry *callchain; | 612 | struct perf_callchain_entry *callchain; |
| 601 | struct perf_raw_record *raw; | 613 | struct perf_regs regs_user; |
| 602 | struct perf_branch_stack *br_stack; | 614 | struct perf_regs regs_intr; |
| 603 | struct perf_regs_user regs_user; | ||
| 604 | u64 stack_user_size; | 615 | u64 stack_user_size; |
| 605 | u64 weight; | 616 | } ____cacheline_aligned; |
| 606 | /* | ||
| 607 | * Transaction flags for abort events: | ||
| 608 | */ | ||
| 609 | u64 txn; | ||
| 610 | }; | ||
| 611 | 617 | ||
| 612 | /* default value for data source */ | 618 | /* default value for data source */ |
| 613 | #define PERF_MEM_NA (PERF_MEM_S(OP, NA) |\ | 619 | #define PERF_MEM_NA (PERF_MEM_S(OP, NA) |\ |
| @@ -624,9 +630,6 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, | |||
| 624 | data->raw = NULL; | 630 | data->raw = NULL; |
| 625 | data->br_stack = NULL; | 631 | data->br_stack = NULL; |
| 626 | data->period = period; | 632 | data->period = period; |
| 627 | data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
| 628 | data->regs_user.regs = NULL; | ||
| 629 | data->stack_user_size = 0; | ||
| 630 | data->weight = 0; | 633 | data->weight = 0; |
| 631 | data->data_src.val = PERF_MEM_NA; | 634 | data->data_src.val = PERF_MEM_NA; |
| 632 | data->txn = 0; | 635 | data->txn = 0; |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 9d845404d875..9b79abbd1ab8 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
| @@ -137,8 +137,9 @@ enum perf_event_sample_format { | |||
| 137 | PERF_SAMPLE_DATA_SRC = 1U << 15, | 137 | PERF_SAMPLE_DATA_SRC = 1U << 15, |
| 138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, | 138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, |
| 139 | PERF_SAMPLE_TRANSACTION = 1U << 17, | 139 | PERF_SAMPLE_TRANSACTION = 1U << 17, |
| 140 | PERF_SAMPLE_REGS_INTR = 1U << 18, | ||
| 140 | 141 | ||
| 141 | PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */ | 142 | PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */ |
| 142 | }; | 143 | }; |
| 143 | 144 | ||
| 144 | /* | 145 | /* |
| @@ -238,6 +239,7 @@ enum perf_event_read_format { | |||
| 238 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | 239 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ |
| 239 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ | 240 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ |
| 240 | /* add: sample_stack_user */ | 241 | /* add: sample_stack_user */ |
| 242 | #define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */ | ||
| 241 | 243 | ||
| 242 | /* | 244 | /* |
| 243 | * Hardware event_id to monitor via a performance monitoring event: | 245 | * Hardware event_id to monitor via a performance monitoring event: |
| @@ -334,6 +336,15 @@ struct perf_event_attr { | |||
| 334 | 336 | ||
| 335 | /* Align to u64. */ | 337 | /* Align to u64. */ |
| 336 | __u32 __reserved_2; | 338 | __u32 __reserved_2; |
| 339 | /* | ||
| 340 | * Defines set of regs to dump for each sample | ||
| 341 | * state captured on: | ||
| 342 | * - precise = 0: PMU interrupt | ||
| 343 | * - precise > 0: sampled instruction | ||
| 344 | * | ||
| 345 | * See asm/perf_regs.h for details. | ||
| 346 | */ | ||
| 347 | __u64 sample_regs_intr; | ||
| 337 | }; | 348 | }; |
| 338 | 349 | ||
| 339 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) | 350 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) |
| @@ -686,6 +697,8 @@ enum perf_event_type { | |||
| 686 | * { u64 weight; } && PERF_SAMPLE_WEIGHT | 697 | * { u64 weight; } && PERF_SAMPLE_WEIGHT |
| 687 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC | 698 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC |
| 688 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION | 699 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION |
| 700 | * { u64 abi; # enum perf_sample_regs_abi | ||
| 701 | * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR | ||
| 689 | * }; | 702 | * }; |
| 690 | */ | 703 | */ |
| 691 | PERF_RECORD_SAMPLE = 9, | 704 | PERF_RECORD_SAMPLE = 9, |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 1cd5eef1fcdd..3e19d3ebc29c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -4460,7 +4460,7 @@ perf_output_sample_regs(struct perf_output_handle *handle, | |||
| 4460 | } | 4460 | } |
| 4461 | } | 4461 | } |
| 4462 | 4462 | ||
| 4463 | static void perf_sample_regs_user(struct perf_regs_user *regs_user, | 4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, |
| 4464 | struct pt_regs *regs) | 4464 | struct pt_regs *regs) |
| 4465 | { | 4465 | { |
| 4466 | if (!user_mode(regs)) { | 4466 | if (!user_mode(regs)) { |
| @@ -4471,11 +4471,22 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user, | |||
| 4471 | } | 4471 | } |
| 4472 | 4472 | ||
| 4473 | if (regs) { | 4473 | if (regs) { |
| 4474 | regs_user->regs = regs; | ||
| 4475 | regs_user->abi = perf_reg_abi(current); | 4474 | regs_user->abi = perf_reg_abi(current); |
| 4475 | regs_user->regs = regs; | ||
| 4476 | } else { | ||
| 4477 | regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
| 4478 | regs_user->regs = NULL; | ||
| 4476 | } | 4479 | } |
| 4477 | } | 4480 | } |
| 4478 | 4481 | ||
| 4482 | static void perf_sample_regs_intr(struct perf_regs *regs_intr, | ||
| 4483 | struct pt_regs *regs) | ||
| 4484 | { | ||
| 4485 | regs_intr->regs = regs; | ||
| 4486 | regs_intr->abi = perf_reg_abi(current); | ||
| 4487 | } | ||
| 4488 | |||
| 4489 | |||
| 4479 | /* | 4490 | /* |
| 4480 | * Get remaining task size from user stack pointer. | 4491 | * Get remaining task size from user stack pointer. |
| 4481 | * | 4492 | * |
| @@ -4857,6 +4868,23 @@ void perf_output_sample(struct perf_output_handle *handle, | |||
| 4857 | if (sample_type & PERF_SAMPLE_TRANSACTION) | 4868 | if (sample_type & PERF_SAMPLE_TRANSACTION) |
| 4858 | perf_output_put(handle, data->txn); | 4869 | perf_output_put(handle, data->txn); |
| 4859 | 4870 | ||
| 4871 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
| 4872 | u64 abi = data->regs_intr.abi; | ||
| 4873 | /* | ||
| 4874 | * If there are no regs to dump, notice it through | ||
| 4875 | * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). | ||
| 4876 | */ | ||
| 4877 | perf_output_put(handle, abi); | ||
| 4878 | |||
| 4879 | if (abi) { | ||
| 4880 | u64 mask = event->attr.sample_regs_intr; | ||
| 4881 | |||
| 4882 | perf_output_sample_regs(handle, | ||
| 4883 | data->regs_intr.regs, | ||
| 4884 | mask); | ||
| 4885 | } | ||
| 4886 | } | ||
| 4887 | |||
| 4860 | if (!event->attr.watermark) { | 4888 | if (!event->attr.watermark) { |
| 4861 | int wakeup_events = event->attr.wakeup_events; | 4889 | int wakeup_events = event->attr.wakeup_events; |
| 4862 | 4890 | ||
| @@ -4922,12 +4950,13 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
| 4922 | header->size += size; | 4950 | header->size += size; |
| 4923 | } | 4951 | } |
| 4924 | 4952 | ||
| 4953 | if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER)) | ||
| 4954 | perf_sample_regs_user(&data->regs_user, regs); | ||
| 4955 | |||
| 4925 | if (sample_type & PERF_SAMPLE_REGS_USER) { | 4956 | if (sample_type & PERF_SAMPLE_REGS_USER) { |
| 4926 | /* regs dump ABI info */ | 4957 | /* regs dump ABI info */ |
| 4927 | int size = sizeof(u64); | 4958 | int size = sizeof(u64); |
| 4928 | 4959 | ||
| 4929 | perf_sample_regs_user(&data->regs_user, regs); | ||
| 4930 | |||
| 4931 | if (data->regs_user.regs) { | 4960 | if (data->regs_user.regs) { |
| 4932 | u64 mask = event->attr.sample_regs_user; | 4961 | u64 mask = event->attr.sample_regs_user; |
| 4933 | size += hweight64(mask) * sizeof(u64); | 4962 | size += hweight64(mask) * sizeof(u64); |
| @@ -4943,15 +4972,11 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
| 4943 | * in case new sample type is added, because we could eat | 4972 | * in case new sample type is added, because we could eat |
| 4944 | * up the rest of the sample size. | 4973 | * up the rest of the sample size. |
| 4945 | */ | 4974 | */ |
| 4946 | struct perf_regs_user *uregs = &data->regs_user; | ||
| 4947 | u16 stack_size = event->attr.sample_stack_user; | 4975 | u16 stack_size = event->attr.sample_stack_user; |
| 4948 | u16 size = sizeof(u64); | 4976 | u16 size = sizeof(u64); |
| 4949 | 4977 | ||
| 4950 | if (!uregs->abi) | ||
| 4951 | perf_sample_regs_user(uregs, regs); | ||
| 4952 | |||
| 4953 | stack_size = perf_sample_ustack_size(stack_size, header->size, | 4978 | stack_size = perf_sample_ustack_size(stack_size, header->size, |
| 4954 | uregs->regs); | 4979 | data->regs_user.regs); |
| 4955 | 4980 | ||
| 4956 | /* | 4981 | /* |
| 4957 | * If there is something to dump, add space for the dump | 4982 | * If there is something to dump, add space for the dump |
| @@ -4964,6 +4989,21 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
| 4964 | data->stack_user_size = stack_size; | 4989 | data->stack_user_size = stack_size; |
| 4965 | header->size += size; | 4990 | header->size += size; |
| 4966 | } | 4991 | } |
| 4992 | |||
| 4993 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
| 4994 | /* regs dump ABI info */ | ||
| 4995 | int size = sizeof(u64); | ||
| 4996 | |||
| 4997 | perf_sample_regs_intr(&data->regs_intr, regs); | ||
| 4998 | |||
| 4999 | if (data->regs_intr.regs) { | ||
| 5000 | u64 mask = event->attr.sample_regs_intr; | ||
| 5001 | |||
| 5002 | size += hweight64(mask) * sizeof(u64); | ||
| 5003 | } | ||
| 5004 | |||
| 5005 | header->size += size; | ||
| 5006 | } | ||
| 4967 | } | 5007 | } |
| 4968 | 5008 | ||
| 4969 | static void perf_event_output(struct perf_event *event, | 5009 | static void perf_event_output(struct perf_event *event, |
| @@ -7151,6 +7191,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, | |||
| 7151 | ret = -EINVAL; | 7191 | ret = -EINVAL; |
| 7152 | } | 7192 | } |
| 7153 | 7193 | ||
| 7194 | if (attr->sample_type & PERF_SAMPLE_REGS_INTR) | ||
| 7195 | ret = perf_reg_validate(attr->sample_regs_intr); | ||
| 7154 | out: | 7196 | out: |
| 7155 | return ret; | 7197 | return ret; |
| 7156 | 7198 | ||
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 717221e98450..40399c3d97d6 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
| @@ -2,6 +2,8 @@ PERF-CFLAGS | |||
| 2 | PERF-GUI-VARS | 2 | PERF-GUI-VARS |
| 3 | PERF-VERSION-FILE | 3 | PERF-VERSION-FILE |
| 4 | perf | 4 | perf |
| 5 | perf-read-vdso32 | ||
| 6 | perf-read-vdsox32 | ||
| 5 | perf-help | 7 | perf-help |
| 6 | perf-record | 8 | perf-record |
| 7 | perf-report | 9 | perf-report |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 398f8d53bd6d..81a20f21a3e6 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
| @@ -33,12 +33,15 @@ OPTIONS | |||
| 33 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a | 33 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a |
| 34 | hexadecimal event descriptor. | 34 | hexadecimal event descriptor. |
| 35 | 35 | ||
| 36 | - a hardware breakpoint event in the form of '\mem:addr[:access]' | 36 | - a hardware breakpoint event in the form of '\mem:addr[/len][:access]' |
| 37 | where addr is the address in memory you want to break in. | 37 | where addr is the address in memory you want to break in. |
| 38 | Access is the memory access type (read, write, execute) it can | 38 | Access is the memory access type (read, write, execute) it can |
| 39 | be passed as follows: '\mem:addr[:[r][w][x]]'. | 39 | be passed as follows: '\mem:addr[:[r][w][x]]'. len is the range, |
| 40 | number of bytes from specified addr, which the breakpoint will cover. | ||
| 40 | If you want to profile read-write accesses in 0x1000, just set | 41 | If you want to profile read-write accesses in 0x1000, just set |
| 41 | 'mem:0x1000:rw'. | 42 | 'mem:0x1000:rw'. |
| 43 | If you want to profile write accesses in [0x1000~1008), just set | ||
| 44 | 'mem:0x1000/8:w'. | ||
| 42 | 45 | ||
| 43 | --filter=<filter>:: | 46 | --filter=<filter>:: |
| 44 | Event filter. | 47 | Event filter. |
| @@ -214,6 +217,12 @@ if combined with -a or -C options. | |||
| 214 | After starting the program, wait msecs before measuring. This is useful to | 217 | After starting the program, wait msecs before measuring. This is useful to |
| 215 | filter out the startup phase of the program, which is often very different. | 218 | filter out the startup phase of the program, which is often very different. |
| 216 | 219 | ||
| 220 | -I:: | ||
| 221 | --intr-regs:: | ||
| 222 | Capture machine state (registers) at interrupt, i.e., on counter overflows for | ||
| 223 | each sample. List of captured registers depends on the architecture. This option | ||
| 224 | is off by default. | ||
| 225 | |||
| 217 | SEE ALSO | 226 | SEE ALSO |
| 218 | -------- | 227 | -------- |
| 219 | linkperf:perf-stat[1], linkperf:perf-list[1] | 228 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 262916f4a377..478efa9b2364 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -60,6 +60,15 @@ include config/utilities.mak | |||
| 60 | # | 60 | # |
| 61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support | 61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support |
| 62 | # for dwarf backtrace post unwind. | 62 | # for dwarf backtrace post unwind. |
| 63 | # | ||
| 64 | # Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32 | ||
| 65 | # for reading the 32-bit compatibility VDSO in 64-bit mode | ||
| 66 | # | ||
| 67 | # Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 | ||
| 68 | # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode | ||
| 69 | # | ||
| 70 | # Define NO_ZLIB if you do not want to support compressed kernel modules | ||
| 71 | |||
| 63 | 72 | ||
| 64 | ifeq ($(srctree),) | 73 | ifeq ($(srctree),) |
| 65 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | 74 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) |
| @@ -171,11 +180,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | |||
| 171 | 180 | ||
| 172 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) | 181 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) |
| 173 | 182 | ||
| 174 | # | ||
| 175 | # Single 'perf' binary right now: | ||
| 176 | # | ||
| 177 | PROGRAMS += $(OUTPUT)perf | 183 | PROGRAMS += $(OUTPUT)perf |
| 178 | 184 | ||
| 185 | ifndef NO_PERF_READ_VDSO32 | ||
| 186 | PROGRAMS += $(OUTPUT)perf-read-vdso32 | ||
| 187 | endif | ||
| 188 | |||
| 189 | ifndef NO_PERF_READ_VDSOX32 | ||
| 190 | PROGRAMS += $(OUTPUT)perf-read-vdsox32 | ||
| 191 | endif | ||
| 192 | |||
| 179 | # what 'all' will build and 'install' will install, in perfexecdir | 193 | # what 'all' will build and 'install' will install, in perfexecdir |
| 180 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 194 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
| 181 | 195 | ||
| @@ -247,12 +261,14 @@ LIB_H += util/annotate.h | |||
| 247 | LIB_H += util/cache.h | 261 | LIB_H += util/cache.h |
| 248 | LIB_H += util/callchain.h | 262 | LIB_H += util/callchain.h |
| 249 | LIB_H += util/build-id.h | 263 | LIB_H += util/build-id.h |
| 264 | LIB_H += util/db-export.h | ||
| 250 | LIB_H += util/debug.h | 265 | LIB_H += util/debug.h |
| 251 | LIB_H += util/pmu.h | 266 | LIB_H += util/pmu.h |
| 252 | LIB_H += util/event.h | 267 | LIB_H += util/event.h |
| 253 | LIB_H += util/evsel.h | 268 | LIB_H += util/evsel.h |
| 254 | LIB_H += util/evlist.h | 269 | LIB_H += util/evlist.h |
| 255 | LIB_H += util/exec_cmd.h | 270 | LIB_H += util/exec_cmd.h |
| 271 | LIB_H += util/find-vdso-map.c | ||
| 256 | LIB_H += util/levenshtein.h | 272 | LIB_H += util/levenshtein.h |
| 257 | LIB_H += util/machine.h | 273 | LIB_H += util/machine.h |
| 258 | LIB_H += util/map.h | 274 | LIB_H += util/map.h |
| @@ -304,6 +320,7 @@ LIB_H += ui/util.h | |||
| 304 | LIB_H += ui/ui.h | 320 | LIB_H += ui/ui.h |
| 305 | LIB_H += util/data.h | 321 | LIB_H += util/data.h |
| 306 | LIB_H += util/kvm-stat.h | 322 | LIB_H += util/kvm-stat.h |
| 323 | LIB_H += util/thread-stack.h | ||
| 307 | 324 | ||
| 308 | LIB_OBJS += $(OUTPUT)util/abspath.o | 325 | LIB_OBJS += $(OUTPUT)util/abspath.o |
| 309 | LIB_OBJS += $(OUTPUT)util/alias.o | 326 | LIB_OBJS += $(OUTPUT)util/alias.o |
| @@ -311,6 +328,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o | |||
| 311 | LIB_OBJS += $(OUTPUT)util/build-id.o | 328 | LIB_OBJS += $(OUTPUT)util/build-id.o |
| 312 | LIB_OBJS += $(OUTPUT)util/config.o | 329 | LIB_OBJS += $(OUTPUT)util/config.o |
| 313 | LIB_OBJS += $(OUTPUT)util/ctype.o | 330 | LIB_OBJS += $(OUTPUT)util/ctype.o |
| 331 | LIB_OBJS += $(OUTPUT)util/db-export.o | ||
| 314 | LIB_OBJS += $(OUTPUT)util/pmu.o | 332 | LIB_OBJS += $(OUTPUT)util/pmu.o |
| 315 | LIB_OBJS += $(OUTPUT)util/environment.o | 333 | LIB_OBJS += $(OUTPUT)util/environment.o |
| 316 | LIB_OBJS += $(OUTPUT)util/event.o | 334 | LIB_OBJS += $(OUTPUT)util/event.o |
| @@ -380,6 +398,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o | |||
| 380 | LIB_OBJS += $(OUTPUT)util/data.o | 398 | LIB_OBJS += $(OUTPUT)util/data.o |
| 381 | LIB_OBJS += $(OUTPUT)util/tsc.o | 399 | LIB_OBJS += $(OUTPUT)util/tsc.o |
| 382 | LIB_OBJS += $(OUTPUT)util/cloexec.o | 400 | LIB_OBJS += $(OUTPUT)util/cloexec.o |
| 401 | LIB_OBJS += $(OUTPUT)util/thread-stack.o | ||
| 383 | 402 | ||
| 384 | LIB_OBJS += $(OUTPUT)ui/setup.o | 403 | LIB_OBJS += $(OUTPUT)ui/setup.o |
| 385 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 404 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
| @@ -478,8 +497,6 @@ ifneq ($(OUTPUT),) | |||
| 478 | endif | 497 | endif |
| 479 | 498 | ||
| 480 | ifdef NO_LIBELF | 499 | ifdef NO_LIBELF |
| 481 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) | ||
| 482 | |||
| 483 | # Remove ELF/DWARF dependent codes | 500 | # Remove ELF/DWARF dependent codes |
| 484 | LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) | 501 | LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) |
| 485 | LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) | 502 | LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) |
| @@ -568,6 +585,10 @@ ifndef NO_LIBNUMA | |||
| 568 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o | 585 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o |
| 569 | endif | 586 | endif |
| 570 | 587 | ||
| 588 | ifndef NO_ZLIB | ||
| 589 | LIB_OBJS += $(OUTPUT)util/zlib.o | ||
| 590 | endif | ||
| 591 | |||
| 571 | ifdef ASCIIDOC8 | 592 | ifdef ASCIIDOC8 |
| 572 | export ASCIIDOC8 | 593 | export ASCIIDOC8 |
| 573 | endif | 594 | endif |
| @@ -732,6 +753,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti | |||
| 732 | $(OUTPUT)perf-%: %.o $(PERFLIBS) | 753 | $(OUTPUT)perf-%: %.o $(PERFLIBS) |
| 733 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) | 754 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) |
| 734 | 755 | ||
| 756 | ifndef NO_PERF_READ_VDSO32 | ||
| 757 | $(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c | ||
| 758 | $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c | ||
| 759 | endif | ||
| 760 | |||
| 761 | ifndef NO_PERF_READ_VDSOX32 | ||
| 762 | $(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c | ||
| 763 | $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c | ||
| 764 | endif | ||
| 765 | |||
| 735 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) | 766 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) |
| 736 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) | 767 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) |
| 737 | 768 | ||
| @@ -876,6 +907,14 @@ install-bin: all install-gtk | |||
| 876 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ | 907 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ |
| 877 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ | 908 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ |
| 878 | $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' | 909 | $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' |
| 910 | ifndef NO_PERF_READ_VDSO32 | ||
| 911 | $(call QUIET_INSTALL, perf-read-vdso32) \ | ||
| 912 | $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)'; | ||
| 913 | endif | ||
| 914 | ifndef NO_PERF_READ_VDSOX32 | ||
| 915 | $(call QUIET_INSTALL, perf-read-vdsox32) \ | ||
| 916 | $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)'; | ||
| 917 | endif | ||
| 879 | $(call QUIET_INSTALL, libexec) \ | 918 | $(call QUIET_INSTALL, libexec) \ |
| 880 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 919 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
| 881 | $(call QUIET_INSTALL, perf-archive) \ | 920 | $(call QUIET_INSTALL, perf-archive) \ |
| @@ -928,7 +967,7 @@ config-clean: | |||
| 928 | 967 | ||
| 929 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean | 968 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean |
| 930 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) | 969 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) |
| 931 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf | 970 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 |
| 932 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* | 971 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* |
| 933 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 972 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
| 934 | $(python-clean) | 973 | $(python-clean) |
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c index d73ef8bb08c7..3bb50eac5542 100644 --- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c +++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c | |||
| @@ -145,7 +145,7 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc) | |||
| 145 | * yet used) | 145 | * yet used) |
| 146 | * -1 in case of errors | 146 | * -1 in case of errors |
| 147 | */ | 147 | */ |
| 148 | static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | 148 | static int check_return_addr(struct dso *dso, Dwarf_Addr pc) |
| 149 | { | 149 | { |
| 150 | int rc = -1; | 150 | int rc = -1; |
| 151 | Dwfl *dwfl; | 151 | Dwfl *dwfl; |
| @@ -156,15 +156,27 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | |||
| 156 | Dwarf_Addr end = pc; | 156 | Dwarf_Addr end = pc; |
| 157 | bool signalp; | 157 | bool signalp; |
| 158 | 158 | ||
| 159 | dwfl = dwfl_begin(&offline_callbacks); | 159 | dwfl = dso->dwfl; |
| 160 | if (!dwfl) { | ||
| 161 | pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); | ||
| 162 | return -1; | ||
| 163 | } | ||
| 164 | 160 | ||
| 165 | if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) { | 161 | if (!dwfl) { |
| 166 | pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1)); | 162 | dwfl = dwfl_begin(&offline_callbacks); |
| 167 | goto out; | 163 | if (!dwfl) { |
| 164 | pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); | ||
| 165 | return -1; | ||
| 166 | } | ||
| 167 | |||
| 168 | if (dwfl_report_offline(dwfl, "", dso->long_name, -1) == NULL) { | ||
| 169 | pr_debug("dwfl_report_offline() failed %s\n", | ||
| 170 | dwarf_errmsg(-1)); | ||
| 171 | /* | ||
| 172 | * We normally cache the DWARF debug info and never | ||
| 173 | * call dwfl_end(). But to prevent fd leak, free in | ||
| 174 | * case of error. | ||
| 175 | */ | ||
| 176 | dwfl_end(dwfl); | ||
| 177 | goto out; | ||
| 178 | } | ||
| 179 | dso->dwfl = dwfl; | ||
| 168 | } | 180 | } |
| 169 | 181 | ||
| 170 | mod = dwfl_addrmodule(dwfl, pc); | 182 | mod = dwfl_addrmodule(dwfl, pc); |
| @@ -194,7 +206,6 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | |||
| 194 | rc = check_return_reg(ra_regno, frame); | 206 | rc = check_return_reg(ra_regno, frame); |
| 195 | 207 | ||
| 196 | out: | 208 | out: |
| 197 | dwfl_end(dwfl); | ||
| 198 | return rc; | 209 | return rc; |
| 199 | } | 210 | } |
| 200 | 211 | ||
| @@ -221,8 +232,7 @@ out: | |||
| 221 | * index: of callchain entry that needs to be ignored (if any) | 232 | * index: of callchain entry that needs to be ignored (if any) |
| 222 | * -1 if no entry needs to be ignored or in case of errors | 233 | * -1 if no entry needs to be ignored or in case of errors |
| 223 | */ | 234 | */ |
| 224 | int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | 235 | int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain) |
| 225 | struct ip_callchain *chain) | ||
| 226 | { | 236 | { |
| 227 | struct addr_location al; | 237 | struct addr_location al; |
| 228 | struct dso *dso = NULL; | 238 | struct dso *dso = NULL; |
| @@ -235,7 +245,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | |||
| 235 | 245 | ||
| 236 | ip = chain->ips[2]; | 246 | ip = chain->ips[2]; |
| 237 | 247 | ||
| 238 | thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER, | 248 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, |
| 239 | MAP__FUNCTION, ip, &al); | 249 | MAP__FUNCTION, ip, &al); |
| 240 | 250 | ||
| 241 | if (al.map) | 251 | if (al.map) |
| @@ -246,7 +256,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | |||
| 246 | return skip_slot; | 256 | return skip_slot; |
| 247 | } | 257 | } |
| 248 | 258 | ||
| 249 | rc = check_return_addr(dso->long_name, ip); | 259 | rc = check_return_addr(dso, ip); |
| 250 | 260 | ||
| 251 | pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", | 261 | pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", |
| 252 | dso->long_name, chain->nr, ip, rc); | 262 | dso->long_name, chain->nr, ip, rc); |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 25114c9a6801..1ce425d101a9 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -357,6 +357,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 357 | static struct perf_tool tool = { | 357 | static struct perf_tool tool = { |
| 358 | .sample = diff__process_sample_event, | 358 | .sample = diff__process_sample_event, |
| 359 | .mmap = perf_event__process_mmap, | 359 | .mmap = perf_event__process_mmap, |
| 360 | .mmap2 = perf_event__process_mmap2, | ||
| 360 | .comm = perf_event__process_comm, | 361 | .comm = perf_event__process_comm, |
| 361 | .exit = perf_event__process_exit, | 362 | .exit = perf_event__process_exit, |
| 362 | .fork = perf_event__process_fork, | 363 | .fork = perf_event__process_fork, |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index de99ca1bb942..84df2deed988 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -217,8 +217,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
| 217 | goto repipe; | 217 | goto repipe; |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 220 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); |
| 221 | sample->ip, &al); | ||
| 222 | 221 | ||
| 223 | if (al.map != NULL) { | 222 | if (al.map != NULL) { |
| 224 | if (!al.map->dso->hit) { | 223 | if (!al.map->dso->hit) { |
| @@ -410,6 +409,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 410 | .tracing_data = perf_event__repipe_op2_synth, | 409 | .tracing_data = perf_event__repipe_op2_synth, |
| 411 | .finished_round = perf_event__repipe_op2_synth, | 410 | .finished_round = perf_event__repipe_op2_synth, |
| 412 | .build_id = perf_event__repipe_op2_synth, | 411 | .build_id = perf_event__repipe_op2_synth, |
| 412 | .id_index = perf_event__repipe_op2_synth, | ||
| 413 | }, | 413 | }, |
| 414 | .input_name = "-", | 414 | .input_name = "-", |
| 415 | .samples = LIST_HEAD_INIT(inject.samples), | 415 | .samples = LIST_HEAD_INIT(inject.samples), |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index b65eb0507b38..3c0f3d4fb021 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
| @@ -1132,6 +1132,10 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1132 | "-m", "1024", | 1132 | "-m", "1024", |
| 1133 | "-c", "1", | 1133 | "-c", "1", |
| 1134 | }; | 1134 | }; |
| 1135 | const char * const kvm_stat_record_usage[] = { | ||
| 1136 | "perf kvm stat record [<options>]", | ||
| 1137 | NULL | ||
| 1138 | }; | ||
| 1135 | const char * const *events_tp; | 1139 | const char * const *events_tp; |
| 1136 | events_tp_size = 0; | 1140 | events_tp_size = 0; |
| 1137 | 1141 | ||
| @@ -1159,6 +1163,27 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1159 | for (j = 1; j < (unsigned int)argc; j++, i++) | 1163 | for (j = 1; j < (unsigned int)argc; j++, i++) |
| 1160 | rec_argv[i] = argv[j]; | 1164 | rec_argv[i] = argv[j]; |
| 1161 | 1165 | ||
| 1166 | set_option_flag(record_options, 'e', "event", PARSE_OPT_HIDDEN); | ||
| 1167 | set_option_flag(record_options, 0, "filter", PARSE_OPT_HIDDEN); | ||
| 1168 | set_option_flag(record_options, 'R', "raw-samples", PARSE_OPT_HIDDEN); | ||
| 1169 | |||
| 1170 | set_option_flag(record_options, 'F', "freq", PARSE_OPT_DISABLED); | ||
| 1171 | set_option_flag(record_options, 0, "group", PARSE_OPT_DISABLED); | ||
| 1172 | set_option_flag(record_options, 'g', NULL, PARSE_OPT_DISABLED); | ||
| 1173 | set_option_flag(record_options, 0, "call-graph", PARSE_OPT_DISABLED); | ||
| 1174 | set_option_flag(record_options, 'd', "data", PARSE_OPT_DISABLED); | ||
| 1175 | set_option_flag(record_options, 'T', "timestamp", PARSE_OPT_DISABLED); | ||
| 1176 | set_option_flag(record_options, 'P', "period", PARSE_OPT_DISABLED); | ||
| 1177 | set_option_flag(record_options, 'n', "no-samples", PARSE_OPT_DISABLED); | ||
| 1178 | set_option_flag(record_options, 'N', "no-buildid-cache", PARSE_OPT_DISABLED); | ||
| 1179 | set_option_flag(record_options, 'B', "no-buildid", PARSE_OPT_DISABLED); | ||
| 1180 | set_option_flag(record_options, 'G', "cgroup", PARSE_OPT_DISABLED); | ||
| 1181 | set_option_flag(record_options, 'b', "branch-any", PARSE_OPT_DISABLED); | ||
| 1182 | set_option_flag(record_options, 'j', "branch-filter", PARSE_OPT_DISABLED); | ||
| 1183 | set_option_flag(record_options, 'W', "weight", PARSE_OPT_DISABLED); | ||
| 1184 | set_option_flag(record_options, 0, "transaction", PARSE_OPT_DISABLED); | ||
| 1185 | |||
| 1186 | record_usage = kvm_stat_record_usage; | ||
| 1162 | return cmd_record(i, rec_argv, NULL); | 1187 | return cmd_record(i, rec_argv, NULL); |
| 1163 | } | 1188 | } |
| 1164 | 1189 | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 7af26acf06d9..921bb6942503 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -55,6 +55,7 @@ static struct { | |||
| 55 | bool show_funcs; | 55 | bool show_funcs; |
| 56 | bool mod_events; | 56 | bool mod_events; |
| 57 | bool uprobes; | 57 | bool uprobes; |
| 58 | bool quiet; | ||
| 58 | int nevents; | 59 | int nevents; |
| 59 | struct perf_probe_event events[MAX_PROBES]; | 60 | struct perf_probe_event events[MAX_PROBES]; |
| 60 | struct strlist *dellist; | 61 | struct strlist *dellist; |
| @@ -312,9 +313,11 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 312 | #endif | 313 | #endif |
| 313 | NULL | 314 | NULL |
| 314 | }; | 315 | }; |
| 315 | const struct option options[] = { | 316 | struct option options[] = { |
| 316 | OPT_INCR('v', "verbose", &verbose, | 317 | OPT_INCR('v', "verbose", &verbose, |
| 317 | "be more verbose (show parsed arguments, etc)"), | 318 | "be more verbose (show parsed arguments, etc)"), |
| 319 | OPT_BOOLEAN('q', "quiet", ¶ms.quiet, | ||
| 320 | "be quiet (do not show any mesages)"), | ||
| 318 | OPT_BOOLEAN('l', "list", ¶ms.list_events, | 321 | OPT_BOOLEAN('l', "list", ¶ms.list_events, |
| 319 | "list up current probe events"), | 322 | "list up current probe events"), |
| 320 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", | 323 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", |
| @@ -382,6 +385,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 382 | }; | 385 | }; |
| 383 | int ret; | 386 | int ret; |
| 384 | 387 | ||
| 388 | set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE); | ||
| 389 | set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE); | ||
| 390 | set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE); | ||
| 391 | #ifdef HAVE_DWARF_SUPPORT | ||
| 392 | set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); | ||
| 393 | set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); | ||
| 394 | #endif | ||
| 395 | |||
| 385 | argc = parse_options(argc, argv, options, probe_usage, | 396 | argc = parse_options(argc, argv, options, probe_usage, |
| 386 | PARSE_OPT_STOP_AT_NON_OPTION); | 397 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 387 | if (argc > 0) { | 398 | if (argc > 0) { |
| @@ -396,6 +407,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 396 | } | 407 | } |
| 397 | } | 408 | } |
| 398 | 409 | ||
| 410 | if (params.quiet) { | ||
| 411 | if (verbose != 0) { | ||
| 412 | pr_err(" Error: -v and -q are exclusive.\n"); | ||
| 413 | return -EINVAL; | ||
| 414 | } | ||
| 415 | verbose = -1; | ||
| 416 | } | ||
| 417 | |||
| 399 | if (params.max_probe_points == 0) | 418 | if (params.max_probe_points == 0) |
| 400 | params.max_probe_points = MAX_PROBES; | 419 | params.max_probe_points = MAX_PROBES; |
| 401 | 420 | ||
| @@ -409,22 +428,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 409 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 428 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
| 410 | 429 | ||
| 411 | if (params.list_events) { | 430 | if (params.list_events) { |
| 412 | if (params.mod_events) { | ||
| 413 | pr_err(" Error: Don't use --list with --add/--del.\n"); | ||
| 414 | usage_with_options(probe_usage, options); | ||
| 415 | } | ||
| 416 | if (params.show_lines) { | ||
| 417 | pr_err(" Error: Don't use --list with --line.\n"); | ||
| 418 | usage_with_options(probe_usage, options); | ||
| 419 | } | ||
| 420 | if (params.show_vars) { | ||
| 421 | pr_err(" Error: Don't use --list with --vars.\n"); | ||
| 422 | usage_with_options(probe_usage, options); | ||
| 423 | } | ||
| 424 | if (params.show_funcs) { | ||
| 425 | pr_err(" Error: Don't use --list with --funcs.\n"); | ||
| 426 | usage_with_options(probe_usage, options); | ||
| 427 | } | ||
| 428 | if (params.uprobes) { | 431 | if (params.uprobes) { |
| 429 | pr_warning(" Error: Don't use --list with --exec.\n"); | 432 | pr_warning(" Error: Don't use --list with --exec.\n"); |
| 430 | usage_with_options(probe_usage, options); | 433 | usage_with_options(probe_usage, options); |
| @@ -435,19 +438,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 435 | return ret; | 438 | return ret; |
| 436 | } | 439 | } |
| 437 | if (params.show_funcs) { | 440 | if (params.show_funcs) { |
| 438 | if (params.nevents != 0 || params.dellist) { | ||
| 439 | pr_err(" Error: Don't use --funcs with" | ||
| 440 | " --add/--del.\n"); | ||
| 441 | usage_with_options(probe_usage, options); | ||
| 442 | } | ||
| 443 | if (params.show_lines) { | ||
| 444 | pr_err(" Error: Don't use --funcs with --line.\n"); | ||
| 445 | usage_with_options(probe_usage, options); | ||
| 446 | } | ||
| 447 | if (params.show_vars) { | ||
| 448 | pr_err(" Error: Don't use --funcs with --vars.\n"); | ||
| 449 | usage_with_options(probe_usage, options); | ||
| 450 | } | ||
| 451 | if (!params.filter) | 441 | if (!params.filter) |
| 452 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, | 442 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, |
| 453 | NULL); | 443 | NULL); |
| @@ -462,16 +452,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 462 | 452 | ||
| 463 | #ifdef HAVE_DWARF_SUPPORT | 453 | #ifdef HAVE_DWARF_SUPPORT |
| 464 | if (params.show_lines) { | 454 | if (params.show_lines) { |
| 465 | if (params.mod_events) { | ||
| 466 | pr_err(" Error: Don't use --line with" | ||
| 467 | " --add/--del.\n"); | ||
| 468 | usage_with_options(probe_usage, options); | ||
| 469 | } | ||
| 470 | if (params.show_vars) { | ||
| 471 | pr_err(" Error: Don't use --line with --vars.\n"); | ||
| 472 | usage_with_options(probe_usage, options); | ||
| 473 | } | ||
| 474 | |||
| 475 | ret = show_line_range(¶ms.line_range, params.target, | 455 | ret = show_line_range(¶ms.line_range, params.target, |
| 476 | params.uprobes); | 456 | params.uprobes); |
| 477 | if (ret < 0) | 457 | if (ret < 0) |
| @@ -479,11 +459,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 479 | return ret; | 459 | return ret; |
| 480 | } | 460 | } |
| 481 | if (params.show_vars) { | 461 | if (params.show_vars) { |
| 482 | if (params.mod_events) { | ||
| 483 | pr_err(" Error: Don't use --vars with" | ||
| 484 | " --add/--del.\n"); | ||
| 485 | usage_with_options(probe_usage, options); | ||
| 486 | } | ||
| 487 | if (!params.filter) | 462 | if (!params.filter) |
| 488 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, | 463 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, |
| 489 | NULL); | 464 | NULL); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2583a9b04317..8648c6d3003d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -200,6 +200,17 @@ static int process_buildids(struct record *rec) | |||
| 200 | if (size == 0) | 200 | if (size == 0) |
| 201 | return 0; | 201 | return 0; |
| 202 | 202 | ||
| 203 | /* | ||
| 204 | * During this process, it'll load kernel map and replace the | ||
| 205 | * dso->long_name to a real pathname it found. In this case | ||
| 206 | * we prefer the vmlinux path like | ||
| 207 | * /lib/modules/3.16.4/build/vmlinux | ||
| 208 | * | ||
| 209 | * rather than build-id path (in debug directory). | ||
| 210 | * $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551 | ||
| 211 | */ | ||
| 212 | symbol_conf.ignore_vmlinux_buildid = true; | ||
| 213 | |||
| 203 | return __perf_session__process_events(session, start, | 214 | return __perf_session__process_events(session, start, |
| 204 | size - start, | 215 | size - start, |
| 205 | size, &build_id__mark_dso_hit_ops); | 216 | size, &build_id__mark_dso_hit_ops); |
| @@ -680,11 +691,12 @@ static int perf_record_config(const char *var, const char *value, void *cb) | |||
| 680 | return perf_default_config(var, value, cb); | 691 | return perf_default_config(var, value, cb); |
| 681 | } | 692 | } |
| 682 | 693 | ||
| 683 | static const char * const record_usage[] = { | 694 | static const char * const __record_usage[] = { |
| 684 | "perf record [<options>] [<command>]", | 695 | "perf record [<options>] [<command>]", |
| 685 | "perf record [<options>] -- <command> [<options>]", | 696 | "perf record [<options>] -- <command> [<options>]", |
| 686 | NULL | 697 | NULL |
| 687 | }; | 698 | }; |
| 699 | const char * const *record_usage = __record_usage; | ||
| 688 | 700 | ||
| 689 | /* | 701 | /* |
| 690 | * XXX Ideally would be local to cmd_record() and passed to a record__new | 702 | * XXX Ideally would be local to cmd_record() and passed to a record__new |
| @@ -725,7 +737,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp"; | |||
| 725 | * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', | 737 | * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', |
| 726 | * using pipes, etc. | 738 | * using pipes, etc. |
| 727 | */ | 739 | */ |
| 728 | const struct option record_options[] = { | 740 | struct option __record_options[] = { |
| 729 | OPT_CALLBACK('e', "event", &record.evlist, "event", | 741 | OPT_CALLBACK('e', "event", &record.evlist, "event", |
| 730 | "event selector. use 'perf list' to list available events", | 742 | "event selector. use 'perf list' to list available events", |
| 731 | parse_events_option), | 743 | parse_events_option), |
| @@ -799,9 +811,13 @@ const struct option record_options[] = { | |||
| 799 | "sample transaction flags (special events only)"), | 811 | "sample transaction flags (special events only)"), |
| 800 | OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, | 812 | OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, |
| 801 | "use per-thread mmaps"), | 813 | "use per-thread mmaps"), |
| 814 | OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs, | ||
| 815 | "Sample machine registers on interrupt"), | ||
| 802 | OPT_END() | 816 | OPT_END() |
| 803 | }; | 817 | }; |
| 804 | 818 | ||
| 819 | struct option *record_options = __record_options; | ||
| 820 | |||
| 805 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | 821 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) |
| 806 | { | 822 | { |
| 807 | int err = -ENOMEM; | 823 | int err = -ENOMEM; |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 9708a1290571..ce304dfd962a 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -23,7 +23,6 @@ static char const *generate_script_lang; | |||
| 23 | static bool debug_mode; | 23 | static bool debug_mode; |
| 24 | static u64 last_timestamp; | 24 | static u64 last_timestamp; |
| 25 | static u64 nr_unordered; | 25 | static u64 nr_unordered; |
| 26 | extern const struct option record_options[]; | ||
| 27 | static bool no_callchain; | 26 | static bool no_callchain; |
| 28 | static bool latency_format; | 27 | static bool latency_format; |
| 29 | static bool system_wide; | 28 | static bool system_wide; |
| @@ -379,7 +378,6 @@ static void print_sample_start(struct perf_sample *sample, | |||
| 379 | 378 | ||
| 380 | static void print_sample_addr(union perf_event *event, | 379 | static void print_sample_addr(union perf_event *event, |
| 381 | struct perf_sample *sample, | 380 | struct perf_sample *sample, |
| 382 | struct machine *machine, | ||
| 383 | struct thread *thread, | 381 | struct thread *thread, |
| 384 | struct perf_event_attr *attr) | 382 | struct perf_event_attr *attr) |
| 385 | { | 383 | { |
| @@ -390,7 +388,7 @@ static void print_sample_addr(union perf_event *event, | |||
| 390 | if (!sample_addr_correlates_sym(attr)) | 388 | if (!sample_addr_correlates_sym(attr)) |
| 391 | return; | 389 | return; |
| 392 | 390 | ||
| 393 | perf_event__preprocess_sample_addr(event, sample, machine, thread, &al); | 391 | perf_event__preprocess_sample_addr(event, sample, thread, &al); |
| 394 | 392 | ||
| 395 | if (PRINT_FIELD(SYM)) { | 393 | if (PRINT_FIELD(SYM)) { |
| 396 | printf(" "); | 394 | printf(" "); |
| @@ -438,7 +436,7 @@ static void print_sample_bts(union perf_event *event, | |||
| 438 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | 436 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && |
| 439 | !output[attr->type].user_set)) { | 437 | !output[attr->type].user_set)) { |
| 440 | printf(" => "); | 438 | printf(" => "); |
| 441 | print_sample_addr(event, sample, al->machine, thread, attr); | 439 | print_sample_addr(event, sample, thread, attr); |
| 442 | } | 440 | } |
| 443 | 441 | ||
| 444 | if (print_srcline_last) | 442 | if (print_srcline_last) |
| @@ -475,7 +473,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
| 475 | event_format__print(evsel->tp_format, sample->cpu, | 473 | event_format__print(evsel->tp_format, sample->cpu, |
| 476 | sample->raw_data, sample->raw_size); | 474 | sample->raw_data, sample->raw_size); |
| 477 | if (PRINT_FIELD(ADDR)) | 475 | if (PRINT_FIELD(ADDR)) |
| 478 | print_sample_addr(event, sample, al->machine, thread, attr); | 476 | print_sample_addr(event, sample, thread, attr); |
| 479 | 477 | ||
| 480 | if (PRINT_FIELD(IP)) { | 478 | if (PRINT_FIELD(IP)) { |
| 481 | if (!symbol_conf.use_callchain) | 479 | if (!symbol_conf.use_callchain) |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 35b425b6293f..f3bb1a4bf060 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
| @@ -528,7 +528,7 @@ static const char *cat_backtrace(union perf_event *event, | |||
| 528 | } | 528 | } |
| 529 | 529 | ||
| 530 | tal.filtered = 0; | 530 | tal.filtered = 0; |
| 531 | thread__find_addr_location(al.thread, machine, cpumode, | 531 | thread__find_addr_location(al.thread, cpumode, |
| 532 | MAP__FUNCTION, ip, &tal); | 532 | MAP__FUNCTION, ip, &tal); |
| 533 | 533 | ||
| 534 | if (tal.sym) | 534 | if (tal.sym) |
| @@ -1963,7 +1963,7 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1963 | NULL | 1963 | NULL |
| 1964 | }; | 1964 | }; |
| 1965 | 1965 | ||
| 1966 | const struct option record_options[] = { | 1966 | const struct option timechart_record_options[] = { |
| 1967 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), | 1967 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), |
| 1968 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, | 1968 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, |
| 1969 | "output processes data only"), | 1969 | "output processes data only"), |
| @@ -1972,7 +1972,7 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1972 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), | 1972 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), |
| 1973 | OPT_END() | 1973 | OPT_END() |
| 1974 | }; | 1974 | }; |
| 1975 | const char * const record_usage[] = { | 1975 | const char * const timechart_record_usage[] = { |
| 1976 | "perf timechart record [<options>]", | 1976 | "perf timechart record [<options>]", |
| 1977 | NULL | 1977 | NULL |
| 1978 | }; | 1978 | }; |
| @@ -1985,7 +1985,8 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1985 | } | 1985 | } |
| 1986 | 1986 | ||
| 1987 | if (argc && !strncmp(argv[0], "rec", 3)) { | 1987 | if (argc && !strncmp(argv[0], "rec", 3)) { |
| 1988 | argc = parse_options(argc, argv, record_options, record_usage, | 1988 | argc = parse_options(argc, argv, timechart_record_options, |
| 1989 | timechart_record_usage, | ||
| 1989 | PARSE_OPT_STOP_AT_NON_OPTION); | 1990 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1990 | 1991 | ||
| 1991 | if (tchart.power_only && tchart.tasks_only) { | 1992 | if (tchart.power_only && tchart.tasks_only) { |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index fb126459b134..83a4835c8118 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -1846,7 +1846,7 @@ static int trace__pgfault(struct trace *trace, | |||
| 1846 | if (trace->summary_only) | 1846 | if (trace->summary_only) |
| 1847 | return 0; | 1847 | return 0; |
| 1848 | 1848 | ||
| 1849 | thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION, | 1849 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, |
| 1850 | sample->ip, &al); | 1850 | sample->ip, &al); |
| 1851 | 1851 | ||
| 1852 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); | 1852 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); |
| @@ -1859,11 +1859,11 @@ static int trace__pgfault(struct trace *trace, | |||
| 1859 | 1859 | ||
| 1860 | fprintf(trace->output, "] => "); | 1860 | fprintf(trace->output, "] => "); |
| 1861 | 1861 | ||
| 1862 | thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE, | 1862 | thread__find_addr_location(thread, cpumode, MAP__VARIABLE, |
| 1863 | sample->addr, &al); | 1863 | sample->addr, &al); |
| 1864 | 1864 | ||
| 1865 | if (!al.map) { | 1865 | if (!al.map) { |
| 1866 | thread__find_addr_location(thread, trace->host, cpumode, | 1866 | thread__find_addr_location(thread, cpumode, |
| 1867 | MAP__FUNCTION, sample->addr, &al); | 1867 | MAP__FUNCTION, sample->addr, &al); |
| 1868 | 1868 | ||
| 1869 | if (al.map) | 1869 | if (al.map) |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 58f609198c6d..5d4b039fe1ed 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
| @@ -150,7 +150,7 @@ CFLAGS += -std=gnu99 | |||
| 150 | # adding assembler files missing the .GNU-stack linker note. | 150 | # adding assembler files missing the .GNU-stack linker note. |
| 151 | LDFLAGS += -Wl,-z,noexecstack | 151 | LDFLAGS += -Wl,-z,noexecstack |
| 152 | 152 | ||
| 153 | EXTLIBS = -lelf -lpthread -lrt -lm -ldl | 153 | EXTLIBS = -lpthread -lrt -lm -ldl |
| 154 | 154 | ||
| 155 | ifneq ($(OUTPUT),) | 155 | ifneq ($(OUTPUT),) |
| 156 | OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ | 156 | OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ |
| @@ -200,7 +200,8 @@ CORE_FEATURE_TESTS = \ | |||
| 200 | libunwind \ | 200 | libunwind \ |
| 201 | stackprotector-all \ | 201 | stackprotector-all \ |
| 202 | timerfd \ | 202 | timerfd \ |
| 203 | libdw-dwarf-unwind | 203 | libdw-dwarf-unwind \ |
| 204 | zlib | ||
| 204 | 205 | ||
| 205 | LIB_FEATURE_TESTS = \ | 206 | LIB_FEATURE_TESTS = \ |
| 206 | dwarf \ | 207 | dwarf \ |
| @@ -214,7 +215,8 @@ LIB_FEATURE_TESTS = \ | |||
| 214 | libpython \ | 215 | libpython \ |
| 215 | libslang \ | 216 | libslang \ |
| 216 | libunwind \ | 217 | libunwind \ |
| 217 | libdw-dwarf-unwind | 218 | libdw-dwarf-unwind \ |
| 219 | zlib | ||
| 218 | 220 | ||
| 219 | VF_FEATURE_TESTS = \ | 221 | VF_FEATURE_TESTS = \ |
| 220 | backtrace \ | 222 | backtrace \ |
| @@ -230,7 +232,9 @@ VF_FEATURE_TESTS = \ | |||
| 230 | bionic \ | 232 | bionic \ |
| 231 | liberty \ | 233 | liberty \ |
| 232 | liberty-z \ | 234 | liberty-z \ |
| 233 | cplus-demangle | 235 | cplus-demangle \ |
| 236 | compile-32 \ | ||
| 237 | compile-x32 | ||
| 234 | 238 | ||
| 235 | # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. | 239 | # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. |
| 236 | # If in the future we need per-feature checks/flags for features not | 240 | # If in the future we need per-feature checks/flags for features not |
| @@ -350,6 +354,7 @@ endif # NO_LIBELF | |||
| 350 | 354 | ||
| 351 | ifndef NO_LIBELF | 355 | ifndef NO_LIBELF |
| 352 | CFLAGS += -DHAVE_LIBELF_SUPPORT | 356 | CFLAGS += -DHAVE_LIBELF_SUPPORT |
| 357 | EXTLIBS += -lelf | ||
| 353 | 358 | ||
| 354 | ifeq ($(feature-libelf-mmap), 1) | 359 | ifeq ($(feature-libelf-mmap), 1) |
| 355 | CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT | 360 | CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT |
| @@ -369,7 +374,7 @@ ifndef NO_LIBELF | |||
| 369 | else | 374 | else |
| 370 | CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) | 375 | CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) |
| 371 | LDFLAGS += $(LIBDW_LDFLAGS) | 376 | LDFLAGS += $(LIBDW_LDFLAGS) |
| 372 | EXTLIBS += -lelf -ldw | 377 | EXTLIBS += -ldw |
| 373 | endif # PERF_HAVE_DWARF_REGS | 378 | endif # PERF_HAVE_DWARF_REGS |
| 374 | endif # NO_DWARF | 379 | endif # NO_DWARF |
| 375 | endif # NO_LIBELF | 380 | endif # NO_LIBELF |
| @@ -602,6 +607,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),) | |||
| 602 | CFLAGS += -DHAVE_LIBBFD_SUPPORT | 607 | CFLAGS += -DHAVE_LIBBFD_SUPPORT |
| 603 | endif | 608 | endif |
| 604 | 609 | ||
| 610 | ifndef NO_ZLIB | ||
| 611 | ifeq ($(feature-zlib), 1) | ||
| 612 | CFLAGS += -DHAVE_ZLIB_SUPPORT | ||
| 613 | EXTLIBS += -lz | ||
| 614 | else | ||
| 615 | NO_ZLIB := 1 | ||
| 616 | endif | ||
| 617 | endif | ||
| 618 | |||
| 605 | ifndef NO_BACKTRACE | 619 | ifndef NO_BACKTRACE |
| 606 | ifeq ($(feature-backtrace), 1) | 620 | ifeq ($(feature-backtrace), 1) |
| 607 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT | 621 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT |
| @@ -622,6 +636,31 @@ ifdef HAVE_KVM_STAT_SUPPORT | |||
| 622 | CFLAGS += -DHAVE_KVM_STAT_SUPPORT | 636 | CFLAGS += -DHAVE_KVM_STAT_SUPPORT |
| 623 | endif | 637 | endif |
| 624 | 638 | ||
| 639 | ifeq (${IS_64_BIT}, 1) | ||
| 640 | ifndef NO_PERF_READ_VDSO32 | ||
| 641 | $(call feature_check,compile-32) | ||
| 642 | ifeq ($(feature-compile-32), 1) | ||
| 643 | CFLAGS += -DHAVE_PERF_READ_VDSO32 | ||
| 644 | else | ||
| 645 | NO_PERF_READ_VDSO32 := 1 | ||
| 646 | endif | ||
| 647 | endif | ||
| 648 | ifneq (${IS_X86_64}, 1) | ||
| 649 | NO_PERF_READ_VDSOX32 := 1 | ||
| 650 | endif | ||
| 651 | ifndef NO_PERF_READ_VDSOX32 | ||
| 652 | $(call feature_check,compile-x32) | ||
| 653 | ifeq ($(feature-compile-x32), 1) | ||
| 654 | CFLAGS += -DHAVE_PERF_READ_VDSOX32 | ||
| 655 | else | ||
| 656 | NO_PERF_READ_VDSOX32 := 1 | ||
| 657 | endif | ||
| 658 | endif | ||
| 659 | else | ||
| 660 | NO_PERF_READ_VDSO32 := 1 | ||
| 661 | NO_PERF_READ_VDSOX32 := 1 | ||
| 662 | endif | ||
| 663 | |||
| 625 | # Among the variables below, these: | 664 | # Among the variables below, these: |
| 626 | # perfexecdir | 665 | # perfexecdir |
| 627 | # template_dir | 666 | # template_dir |
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch index 4b06719ee984..851cd0172a76 100644 --- a/tools/perf/config/Makefile.arch +++ b/tools/perf/config/Makefile.arch | |||
| @@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64) | |||
| 21 | RAW_ARCH := x86_64 | 21 | RAW_ARCH := x86_64 |
| 22 | endif | 22 | endif |
| 23 | endif | 23 | endif |
| 24 | |||
| 25 | ifeq (${IS_X86_64}, 1) | ||
| 26 | IS_64_BIT := 1 | ||
| 27 | else ifeq ($(ARCH),x86) | ||
| 28 | IS_64_BIT := 0 | ||
| 29 | else | ||
| 30 | IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) | ||
| 31 | endif | ||
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 72ab2984718e..53f19b5dbc37 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
| @@ -27,7 +27,10 @@ FILES= \ | |||
| 27 | test-libunwind-debug-frame.bin \ | 27 | test-libunwind-debug-frame.bin \ |
| 28 | test-stackprotector-all.bin \ | 28 | test-stackprotector-all.bin \ |
| 29 | test-timerfd.bin \ | 29 | test-timerfd.bin \ |
| 30 | test-libdw-dwarf-unwind.bin | 30 | test-libdw-dwarf-unwind.bin \ |
| 31 | test-compile-32.bin \ | ||
| 32 | test-compile-x32.bin \ | ||
| 33 | test-zlib.bin | ||
| 31 | 34 | ||
| 32 | CC := $(CROSS_COMPILE)gcc -MD | 35 | CC := $(CROSS_COMPILE)gcc -MD |
| 33 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 36 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
| @@ -39,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) | |||
| 39 | ############################### | 42 | ############################### |
| 40 | 43 | ||
| 41 | test-all.bin: | 44 | test-all.bin: |
| 42 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl | 45 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz |
| 43 | 46 | ||
| 44 | test-hello.bin: | 47 | test-hello.bin: |
| 45 | $(BUILD) | 48 | $(BUILD) |
| @@ -131,6 +134,15 @@ test-libdw-dwarf-unwind.bin: | |||
| 131 | test-sync-compare-and-swap.bin: | 134 | test-sync-compare-and-swap.bin: |
| 132 | $(BUILD) -Werror | 135 | $(BUILD) -Werror |
| 133 | 136 | ||
| 137 | test-compile-32.bin: | ||
| 138 | $(CC) -m32 -o $(OUTPUT)$@ test-compile.c | ||
| 139 | |||
| 140 | test-compile-x32.bin: | ||
| 141 | $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c | ||
| 142 | |||
| 143 | test-zlib.bin: | ||
| 144 | $(BUILD) -lz | ||
| 145 | |||
| 134 | -include *.d | 146 | -include *.d |
| 135 | 147 | ||
| 136 | ############################### | 148 | ############################### |
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index a7d022e161c0..652e0098eba6 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
| @@ -93,6 +93,10 @@ | |||
| 93 | # include "test-sync-compare-and-swap.c" | 93 | # include "test-sync-compare-and-swap.c" |
| 94 | #undef main | 94 | #undef main |
| 95 | 95 | ||
| 96 | #define main main_test_zlib | ||
| 97 | # include "test-zlib.c" | ||
| 98 | #undef main | ||
| 99 | |||
| 96 | int main(int argc, char *argv[]) | 100 | int main(int argc, char *argv[]) |
| 97 | { | 101 | { |
| 98 | main_test_libpython(); | 102 | main_test_libpython(); |
| @@ -116,6 +120,7 @@ int main(int argc, char *argv[]) | |||
| 116 | main_test_stackprotector_all(); | 120 | main_test_stackprotector_all(); |
| 117 | main_test_libdw_dwarf_unwind(); | 121 | main_test_libdw_dwarf_unwind(); |
| 118 | main_test_sync_compare_and_swap(argc, argv); | 122 | main_test_sync_compare_and_swap(argc, argv); |
| 123 | main_test_zlib(); | ||
| 119 | 124 | ||
| 120 | return 0; | 125 | return 0; |
| 121 | } | 126 | } |
diff --git a/tools/perf/config/feature-checks/test-compile.c b/tools/perf/config/feature-checks/test-compile.c new file mode 100644 index 000000000000..31dbf45bf99c --- /dev/null +++ b/tools/perf/config/feature-checks/test-compile.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | int main(void) | ||
| 2 | { | ||
| 3 | return 0; | ||
| 4 | } | ||
diff --git a/tools/perf/config/feature-checks/test-zlib.c b/tools/perf/config/feature-checks/test-zlib.c new file mode 100644 index 000000000000..e111fff6240e --- /dev/null +++ b/tools/perf/config/feature-checks/test-zlib.c | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #include <zlib.h> | ||
| 2 | |||
| 3 | int main(void) | ||
| 4 | { | ||
| 5 | z_stream zs; | ||
| 6 | |||
| 7 | inflateInit(&zs); | ||
| 8 | return 0; | ||
| 9 | } | ||
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c new file mode 100644 index 000000000000..764e2547c25a --- /dev/null +++ b/tools/perf/perf-read-vdso.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <string.h> | ||
| 3 | |||
| 4 | #define VDSO__MAP_NAME "[vdso]" | ||
| 5 | |||
| 6 | /* | ||
| 7 | * Include definition of find_vdso_map() also used in util/vdso.c for | ||
| 8 | * building perf. | ||
| 9 | */ | ||
| 10 | #include "util/find-vdso-map.c" | ||
| 11 | |||
| 12 | int main(void) | ||
| 13 | { | ||
| 14 | void *start, *end; | ||
| 15 | size_t size, written; | ||
| 16 | |||
| 17 | if (find_vdso_map(&start, &end)) | ||
| 18 | return 1; | ||
| 19 | |||
| 20 | size = end - start; | ||
| 21 | |||
| 22 | while (size) { | ||
| 23 | written = fwrite(start, 1, size, stdout); | ||
| 24 | if (!written) | ||
| 25 | return 1; | ||
| 26 | start += written; | ||
| 27 | size -= written; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (fflush(stdout)) | ||
| 31 | return 1; | ||
| 32 | |||
| 33 | return 0; | ||
| 34 | } | ||
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 220d44e44c1b..1dabb8553499 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -52,6 +52,7 @@ struct record_opts { | |||
| 52 | bool sample_weight; | 52 | bool sample_weight; |
| 53 | bool sample_time; | 53 | bool sample_time; |
| 54 | bool period; | 54 | bool period; |
| 55 | bool sample_intr_regs; | ||
| 55 | unsigned int freq; | 56 | unsigned int freq; |
| 56 | unsigned int mmap_pages; | 57 | unsigned int mmap_pages; |
| 57 | unsigned int user_freq; | 58 | unsigned int user_freq; |
| @@ -62,4 +63,7 @@ struct record_opts { | |||
| 62 | unsigned initial_delay; | 63 | unsigned initial_delay; |
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 66 | struct option; | ||
| 67 | extern const char * const *record_usage; | ||
| 68 | extern struct option *record_options; | ||
| 65 | #endif | 69 | #endif |
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record new file mode 100644 index 000000000000..221d66e05713 --- /dev/null +++ b/tools/perf/scripts/python/bin/export-to-postgresql-record | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | # | ||
| 4 | # export perf data to a postgresql database. Can cover | ||
| 5 | # perf ip samples (excluding the tracepoints). No special | ||
| 6 | # record requirements, just record what you want to export. | ||
| 7 | # | ||
| 8 | perf record $@ | ||
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report new file mode 100644 index 000000000000..cd335b6e2a01 --- /dev/null +++ b/tools/perf/scripts/python/bin/export-to-postgresql-report | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # description: export perf data to a postgresql database | ||
| 3 | # args: [database name] [columns] [calls] | ||
| 4 | n_args=0 | ||
| 5 | for i in "$@" | ||
| 6 | do | ||
| 7 | if expr match "$i" "-" > /dev/null ; then | ||
| 8 | break | ||
| 9 | fi | ||
| 10 | n_args=$(( $n_args + 1 )) | ||
| 11 | done | ||
| 12 | if [ "$n_args" -gt 3 ] ; then | ||
| 13 | echo "usage: export-to-postgresql-report [database name] [columns] [calls]" | ||
| 14 | exit | ||
| 15 | fi | ||
| 16 | if [ "$n_args" -gt 2 ] ; then | ||
| 17 | dbname=$1 | ||
| 18 | columns=$2 | ||
| 19 | calls=$3 | ||
| 20 | shift 3 | ||
| 21 | elif [ "$n_args" -gt 1 ] ; then | ||
| 22 | dbname=$1 | ||
| 23 | columns=$2 | ||
| 24 | shift 2 | ||
| 25 | elif [ "$n_args" -gt 0 ] ; then | ||
| 26 | dbname=$1 | ||
| 27 | shift | ||
| 28 | fi | ||
| 29 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls | ||
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py new file mode 100644 index 000000000000..4cdafd880074 --- /dev/null +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
| @@ -0,0 +1,444 @@ | |||
| 1 | # export-to-postgresql.py: export perf data to a postgresql database | ||
| 2 | # Copyright (c) 2014, Intel Corporation. | ||
| 3 | # | ||
| 4 | # This program is free software; you can redistribute it and/or modify it | ||
| 5 | # under the terms and conditions of the GNU General Public License, | ||
| 6 | # version 2, as published by the Free Software Foundation. | ||
| 7 | # | ||
| 8 | # This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | # more details. | ||
| 12 | |||
| 13 | import os | ||
| 14 | import sys | ||
| 15 | import struct | ||
| 16 | import datetime | ||
| 17 | |||
| 18 | from PySide.QtSql import * | ||
| 19 | |||
| 20 | # Need to access PostgreSQL C library directly to use COPY FROM STDIN | ||
| 21 | from ctypes import * | ||
| 22 | libpq = CDLL("libpq.so.5") | ||
| 23 | PQconnectdb = libpq.PQconnectdb | ||
| 24 | PQconnectdb.restype = c_void_p | ||
| 25 | PQfinish = libpq.PQfinish | ||
| 26 | PQstatus = libpq.PQstatus | ||
| 27 | PQexec = libpq.PQexec | ||
| 28 | PQexec.restype = c_void_p | ||
| 29 | PQresultStatus = libpq.PQresultStatus | ||
| 30 | PQputCopyData = libpq.PQputCopyData | ||
| 31 | PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ] | ||
| 32 | PQputCopyEnd = libpq.PQputCopyEnd | ||
| 33 | PQputCopyEnd.argtypes = [ c_void_p, c_void_p ] | ||
| 34 | |||
| 35 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
| 36 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
| 37 | |||
| 38 | # These perf imports are not used at present | ||
| 39 | #from perf_trace_context import * | ||
| 40 | #from Core import * | ||
| 41 | |||
| 42 | perf_db_export_mode = True | ||
| 43 | perf_db_export_calls = False | ||
| 44 | |||
| 45 | def usage(): | ||
| 46 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]" | ||
| 47 | print >> sys.stderr, "where: columns 'all' or 'branches'" | ||
| 48 | print >> sys.stderr, " calls 'calls' => create calls table" | ||
| 49 | raise Exception("Too few arguments") | ||
| 50 | |||
| 51 | if (len(sys.argv) < 2): | ||
| 52 | usage() | ||
| 53 | |||
| 54 | dbname = sys.argv[1] | ||
| 55 | |||
| 56 | if (len(sys.argv) >= 3): | ||
| 57 | columns = sys.argv[2] | ||
| 58 | else: | ||
| 59 | columns = "all" | ||
| 60 | |||
| 61 | if columns not in ("all", "branches"): | ||
| 62 | usage() | ||
| 63 | |||
| 64 | branches = (columns == "branches") | ||
| 65 | |||
| 66 | if (len(sys.argv) >= 4): | ||
| 67 | if (sys.argv[3] == "calls"): | ||
| 68 | perf_db_export_calls = True | ||
| 69 | else: | ||
| 70 | usage() | ||
| 71 | |||
| 72 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" | ||
| 73 | os.mkdir(output_dir_name) | ||
| 74 | |||
| 75 | def do_query(q, s): | ||
| 76 | if (q.exec_(s)): | ||
| 77 | return | ||
| 78 | raise Exception("Query failed: " + q.lastError().text()) | ||
| 79 | |||
| 80 | print datetime.datetime.today(), "Creating database..." | ||
| 81 | |||
| 82 | db = QSqlDatabase.addDatabase('QPSQL') | ||
| 83 | query = QSqlQuery(db) | ||
| 84 | db.setDatabaseName('postgres') | ||
| 85 | db.open() | ||
| 86 | try: | ||
| 87 | do_query(query, 'CREATE DATABASE ' + dbname) | ||
| 88 | except: | ||
| 89 | os.rmdir(output_dir_name) | ||
| 90 | raise | ||
| 91 | query.finish() | ||
| 92 | query.clear() | ||
| 93 | db.close() | ||
| 94 | |||
| 95 | db.setDatabaseName(dbname) | ||
| 96 | db.open() | ||
| 97 | |||
| 98 | query = QSqlQuery(db) | ||
| 99 | do_query(query, 'SET client_min_messages TO WARNING') | ||
| 100 | |||
| 101 | do_query(query, 'CREATE TABLE selected_events (' | ||
| 102 | 'id bigint NOT NULL,' | ||
| 103 | 'name varchar(80))') | ||
| 104 | do_query(query, 'CREATE TABLE machines (' | ||
| 105 | 'id bigint NOT NULL,' | ||
| 106 | 'pid integer,' | ||
| 107 | 'root_dir varchar(4096))') | ||
| 108 | do_query(query, 'CREATE TABLE threads (' | ||
| 109 | 'id bigint NOT NULL,' | ||
| 110 | 'machine_id bigint,' | ||
| 111 | 'process_id bigint,' | ||
| 112 | 'pid integer,' | ||
| 113 | 'tid integer)') | ||
| 114 | do_query(query, 'CREATE TABLE comms (' | ||
| 115 | 'id bigint NOT NULL,' | ||
| 116 | 'comm varchar(16))') | ||
| 117 | do_query(query, 'CREATE TABLE comm_threads (' | ||
| 118 | 'id bigint NOT NULL,' | ||
| 119 | 'comm_id bigint,' | ||
| 120 | 'thread_id bigint)') | ||
| 121 | do_query(query, 'CREATE TABLE dsos (' | ||
| 122 | 'id bigint NOT NULL,' | ||
| 123 | 'machine_id bigint,' | ||
| 124 | 'short_name varchar(256),' | ||
| 125 | 'long_name varchar(4096),' | ||
| 126 | 'build_id varchar(64))') | ||
| 127 | do_query(query, 'CREATE TABLE symbols (' | ||
| 128 | 'id bigint NOT NULL,' | ||
| 129 | 'dso_id bigint,' | ||
| 130 | 'sym_start bigint,' | ||
| 131 | 'sym_end bigint,' | ||
| 132 | 'binding integer,' | ||
| 133 | 'name varchar(2048))') | ||
| 134 | do_query(query, 'CREATE TABLE branch_types (' | ||
| 135 | 'id integer NOT NULL,' | ||
| 136 | 'name varchar(80))') | ||
| 137 | |||
| 138 | if branches: | ||
| 139 | do_query(query, 'CREATE TABLE samples (' | ||
| 140 | 'id bigint NOT NULL,' | ||
| 141 | 'evsel_id bigint,' | ||
| 142 | 'machine_id bigint,' | ||
| 143 | 'thread_id bigint,' | ||
| 144 | 'comm_id bigint,' | ||
| 145 | 'dso_id bigint,' | ||
| 146 | 'symbol_id bigint,' | ||
| 147 | 'sym_offset bigint,' | ||
| 148 | 'ip bigint,' | ||
| 149 | 'time bigint,' | ||
| 150 | 'cpu integer,' | ||
| 151 | 'to_dso_id bigint,' | ||
| 152 | 'to_symbol_id bigint,' | ||
| 153 | 'to_sym_offset bigint,' | ||
| 154 | 'to_ip bigint,' | ||
| 155 | 'branch_type integer,' | ||
| 156 | 'in_tx boolean)') | ||
| 157 | else: | ||
| 158 | do_query(query, 'CREATE TABLE samples (' | ||
| 159 | 'id bigint NOT NULL,' | ||
| 160 | 'evsel_id bigint,' | ||
| 161 | 'machine_id bigint,' | ||
| 162 | 'thread_id bigint,' | ||
| 163 | 'comm_id bigint,' | ||
| 164 | 'dso_id bigint,' | ||
| 165 | 'symbol_id bigint,' | ||
| 166 | 'sym_offset bigint,' | ||
| 167 | 'ip bigint,' | ||
| 168 | 'time bigint,' | ||
| 169 | 'cpu integer,' | ||
| 170 | 'to_dso_id bigint,' | ||
| 171 | 'to_symbol_id bigint,' | ||
| 172 | 'to_sym_offset bigint,' | ||
| 173 | 'to_ip bigint,' | ||
| 174 | 'period bigint,' | ||
| 175 | 'weight bigint,' | ||
| 176 | 'transaction bigint,' | ||
| 177 | 'data_src bigint,' | ||
| 178 | 'branch_type integer,' | ||
| 179 | 'in_tx boolean)') | ||
| 180 | |||
| 181 | if perf_db_export_calls: | ||
| 182 | do_query(query, 'CREATE TABLE call_paths (' | ||
| 183 | 'id bigint NOT NULL,' | ||
| 184 | 'parent_id bigint,' | ||
| 185 | 'symbol_id bigint,' | ||
| 186 | 'ip bigint)') | ||
| 187 | do_query(query, 'CREATE TABLE calls (' | ||
| 188 | 'id bigint NOT NULL,' | ||
| 189 | 'thread_id bigint,' | ||
| 190 | 'comm_id bigint,' | ||
| 191 | 'call_path_id bigint,' | ||
| 192 | 'call_time bigint,' | ||
| 193 | 'return_time bigint,' | ||
| 194 | 'branch_count bigint,' | ||
| 195 | 'call_id bigint,' | ||
| 196 | 'return_id bigint,' | ||
| 197 | 'parent_call_path_id bigint,' | ||
| 198 | 'flags integer)') | ||
| 199 | |||
| 200 | do_query(query, 'CREATE VIEW samples_view AS ' | ||
| 201 | 'SELECT ' | ||
| 202 | 'id,' | ||
| 203 | 'time,' | ||
| 204 | 'cpu,' | ||
| 205 | '(SELECT pid FROM threads WHERE id = thread_id) AS pid,' | ||
| 206 | '(SELECT tid FROM threads WHERE id = thread_id) AS tid,' | ||
| 207 | '(SELECT comm FROM comms WHERE id = comm_id) AS command,' | ||
| 208 | '(SELECT name FROM selected_events WHERE id = evsel_id) AS event,' | ||
| 209 | 'to_hex(ip) AS ip_hex,' | ||
| 210 | '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,' | ||
| 211 | 'sym_offset,' | ||
| 212 | '(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,' | ||
| 213 | 'to_hex(to_ip) AS to_ip_hex,' | ||
| 214 | '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' | ||
| 215 | 'to_sym_offset,' | ||
| 216 | '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' | ||
| 217 | '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' | ||
| 218 | 'in_tx' | ||
| 219 | ' FROM samples') | ||
| 220 | |||
| 221 | |||
| 222 | file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0) | ||
| 223 | file_trailer = "\377\377" | ||
| 224 | |||
| 225 | def open_output_file(file_name): | ||
| 226 | path_name = output_dir_name + "/" + file_name | ||
| 227 | file = open(path_name, "w+") | ||
| 228 | file.write(file_header) | ||
| 229 | return file | ||
| 230 | |||
| 231 | def close_output_file(file): | ||
| 232 | file.write(file_trailer) | ||
| 233 | file.close() | ||
| 234 | |||
| 235 | def copy_output_file_direct(file, table_name): | ||
| 236 | close_output_file(file) | ||
| 237 | sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')" | ||
| 238 | do_query(query, sql) | ||
| 239 | |||
| 240 | # Use COPY FROM STDIN because security may prevent postgres from accessing the files directly | ||
| 241 | def copy_output_file(file, table_name): | ||
| 242 | conn = PQconnectdb("dbname = " + dbname) | ||
| 243 | if (PQstatus(conn)): | ||
| 244 | raise Exception("COPY FROM STDIN PQconnectdb failed") | ||
| 245 | file.write(file_trailer) | ||
| 246 | file.seek(0) | ||
| 247 | sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" | ||
| 248 | res = PQexec(conn, sql) | ||
| 249 | if (PQresultStatus(res) != 4): | ||
| 250 | raise Exception("COPY FROM STDIN PQexec failed") | ||
| 251 | data = file.read(65536) | ||
| 252 | while (len(data)): | ||
| 253 | ret = PQputCopyData(conn, data, len(data)) | ||
| 254 | if (ret != 1): | ||
| 255 | raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret)) | ||
| 256 | data = file.read(65536) | ||
| 257 | ret = PQputCopyEnd(conn, None) | ||
| 258 | if (ret != 1): | ||
| 259 | raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret)) | ||
| 260 | PQfinish(conn) | ||
| 261 | |||
| 262 | def remove_output_file(file): | ||
| 263 | name = file.name | ||
| 264 | file.close() | ||
| 265 | os.unlink(name) | ||
| 266 | |||
| 267 | evsel_file = open_output_file("evsel_table.bin") | ||
| 268 | machine_file = open_output_file("machine_table.bin") | ||
| 269 | thread_file = open_output_file("thread_table.bin") | ||
| 270 | comm_file = open_output_file("comm_table.bin") | ||
| 271 | comm_thread_file = open_output_file("comm_thread_table.bin") | ||
| 272 | dso_file = open_output_file("dso_table.bin") | ||
| 273 | symbol_file = open_output_file("symbol_table.bin") | ||
| 274 | branch_type_file = open_output_file("branch_type_table.bin") | ||
| 275 | sample_file = open_output_file("sample_table.bin") | ||
| 276 | if perf_db_export_calls: | ||
| 277 | call_path_file = open_output_file("call_path_table.bin") | ||
| 278 | call_file = open_output_file("call_table.bin") | ||
| 279 | |||
| 280 | def trace_begin(): | ||
| 281 | print datetime.datetime.today(), "Writing to intermediate files..." | ||
| 282 | # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs | ||
| 283 | evsel_table(0, "unknown") | ||
| 284 | machine_table(0, 0, "unknown") | ||
| 285 | thread_table(0, 0, 0, -1, -1) | ||
| 286 | comm_table(0, "unknown") | ||
| 287 | dso_table(0, 0, "unknown", "unknown", "") | ||
| 288 | symbol_table(0, 0, 0, 0, 0, "unknown") | ||
| 289 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
| 290 | if perf_db_export_calls: | ||
| 291 | call_path_table(0, 0, 0, 0) | ||
| 292 | |||
| 293 | unhandled_count = 0 | ||
| 294 | |||
| 295 | def trace_end(): | ||
| 296 | print datetime.datetime.today(), "Copying to database..." | ||
| 297 | copy_output_file(evsel_file, "selected_events") | ||
| 298 | copy_output_file(machine_file, "machines") | ||
| 299 | copy_output_file(thread_file, "threads") | ||
| 300 | copy_output_file(comm_file, "comms") | ||
| 301 | copy_output_file(comm_thread_file, "comm_threads") | ||
| 302 | copy_output_file(dso_file, "dsos") | ||
| 303 | copy_output_file(symbol_file, "symbols") | ||
| 304 | copy_output_file(branch_type_file, "branch_types") | ||
| 305 | copy_output_file(sample_file, "samples") | ||
| 306 | if perf_db_export_calls: | ||
| 307 | copy_output_file(call_path_file, "call_paths") | ||
| 308 | copy_output_file(call_file, "calls") | ||
| 309 | |||
| 310 | print datetime.datetime.today(), "Removing intermediate files..." | ||
| 311 | remove_output_file(evsel_file) | ||
| 312 | remove_output_file(machine_file) | ||
| 313 | remove_output_file(thread_file) | ||
| 314 | remove_output_file(comm_file) | ||
| 315 | remove_output_file(comm_thread_file) | ||
| 316 | remove_output_file(dso_file) | ||
| 317 | remove_output_file(symbol_file) | ||
| 318 | remove_output_file(branch_type_file) | ||
| 319 | remove_output_file(sample_file) | ||
| 320 | if perf_db_export_calls: | ||
| 321 | remove_output_file(call_path_file) | ||
| 322 | remove_output_file(call_file) | ||
| 323 | os.rmdir(output_dir_name) | ||
| 324 | print datetime.datetime.today(), "Adding primary keys" | ||
| 325 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') | ||
| 326 | do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') | ||
| 327 | do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') | ||
| 328 | do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)') | ||
| 329 | do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') | ||
| 330 | do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') | ||
| 331 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') | ||
| 332 | do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') | ||
| 333 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') | ||
| 334 | if perf_db_export_calls: | ||
| 335 | do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') | ||
| 336 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') | ||
| 337 | |||
| 338 | print datetime.datetime.today(), "Adding foreign keys" | ||
| 339 | do_query(query, 'ALTER TABLE threads ' | ||
| 340 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' | ||
| 341 | 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') | ||
| 342 | do_query(query, 'ALTER TABLE comm_threads ' | ||
| 343 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
| 344 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)') | ||
| 345 | do_query(query, 'ALTER TABLE dsos ' | ||
| 346 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id)') | ||
| 347 | do_query(query, 'ALTER TABLE symbols ' | ||
| 348 | 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id)') | ||
| 349 | do_query(query, 'ALTER TABLE samples ' | ||
| 350 | 'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES selected_events (id),' | ||
| 351 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' | ||
| 352 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
| 353 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
| 354 | 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id),' | ||
| 355 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' | ||
| 356 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' | ||
| 357 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') | ||
| 358 | if perf_db_export_calls: | ||
| 359 | do_query(query, 'ALTER TABLE call_paths ' | ||
| 360 | 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' | ||
| 361 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') | ||
| 362 | do_query(query, 'ALTER TABLE calls ' | ||
| 363 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
| 364 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
| 365 | 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' | ||
| 366 | 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' | ||
| 367 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' | ||
| 368 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') | ||
| 369 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') | ||
| 370 | |||
| 371 | if (unhandled_count): | ||
| 372 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" | ||
| 373 | print datetime.datetime.today(), "Done" | ||
| 374 | |||
| 375 | def trace_unhandled(event_name, context, event_fields_dict): | ||
| 376 | global unhandled_count | ||
| 377 | unhandled_count += 1 | ||
| 378 | |||
| 379 | def sched__sched_switch(*x): | ||
| 380 | pass | ||
| 381 | |||
| 382 | def evsel_table(evsel_id, evsel_name, *x): | ||
| 383 | n = len(evsel_name) | ||
| 384 | fmt = "!hiqi" + str(n) + "s" | ||
| 385 | value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) | ||
| 386 | evsel_file.write(value) | ||
| 387 | |||
| 388 | def machine_table(machine_id, pid, root_dir, *x): | ||
| 389 | n = len(root_dir) | ||
| 390 | fmt = "!hiqiii" + str(n) + "s" | ||
| 391 | value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) | ||
| 392 | machine_file.write(value) | ||
| 393 | |||
| 394 | def thread_table(thread_id, machine_id, process_id, pid, tid, *x): | ||
| 395 | value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid) | ||
| 396 | thread_file.write(value) | ||
| 397 | |||
| 398 | def comm_table(comm_id, comm_str, *x): | ||
| 399 | n = len(comm_str) | ||
| 400 | fmt = "!hiqi" + str(n) + "s" | ||
| 401 | value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) | ||
| 402 | comm_file.write(value) | ||
| 403 | |||
| 404 | def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): | ||
| 405 | fmt = "!hiqiqiq" | ||
| 406 | value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id) | ||
| 407 | comm_thread_file.write(value) | ||
| 408 | |||
| 409 | def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): | ||
| 410 | n1 = len(short_name) | ||
| 411 | n2 = len(long_name) | ||
| 412 | n3 = len(build_id) | ||
| 413 | fmt = "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s" | ||
| 414 | value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id) | ||
| 415 | dso_file.write(value) | ||
| 416 | |||
| 417 | def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x): | ||
| 418 | n = len(symbol_name) | ||
| 419 | fmt = "!hiqiqiqiqiii" + str(n) + "s" | ||
| 420 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) | ||
| 421 | symbol_file.write(value) | ||
| 422 | |||
| 423 | def branch_type_table(branch_type, name, *x): | ||
| 424 | n = len(name) | ||
| 425 | fmt = "!hiii" + str(n) + "s" | ||
| 426 | value = struct.pack(fmt, 2, 4, branch_type, n, name) | ||
| 427 | branch_type_file.write(value) | ||
| 428 | |||
| 429 | def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x): | ||
| 430 | if branches: | ||
| 431 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx) | ||
| 432 | else: | ||
| 433 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx) | ||
| 434 | sample_file.write(value) | ||
| 435 | |||
| 436 | def call_path_table(cp_id, parent_id, symbol_id, ip, *x): | ||
| 437 | fmt = "!hiqiqiqiq" | ||
| 438 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) | ||
| 439 | call_path_file.write(value) | ||
| 440 | |||
| 441 | def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x): | ||
| 442 | fmt = "!hiqiqiqiqiqiqiqiqiqiqii" | ||
| 443 | value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags) | ||
| 444 | call_file.write(value) | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 162c978f1491..4b7d9ab0f049 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -85,7 +85,7 @@ static struct test { | |||
| 85 | .func = test__hists_link, | 85 | .func = test__hists_link, |
| 86 | }, | 86 | }, |
| 87 | { | 87 | { |
| 88 | .desc = "Try 'use perf' in python, checking link problems", | 88 | .desc = "Try 'import perf' in python, checking link problems", |
| 89 | .func = test__python_use, | 89 | .func = test__python_use, |
| 90 | }, | 90 | }, |
| 91 | { | 91 | { |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 67f2d6323558..f671ec37a7c4 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
| @@ -133,8 +133,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf, | |||
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | static int read_object_code(u64 addr, size_t len, u8 cpumode, | 135 | static int read_object_code(u64 addr, size_t len, u8 cpumode, |
| 136 | struct thread *thread, struct machine *machine, | 136 | struct thread *thread, struct state *state) |
| 137 | struct state *state) | ||
| 138 | { | 137 | { |
| 139 | struct addr_location al; | 138 | struct addr_location al; |
| 140 | unsigned char buf1[BUFSZ]; | 139 | unsigned char buf1[BUFSZ]; |
| @@ -145,8 +144,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, | |||
| 145 | 144 | ||
| 146 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); | 145 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); |
| 147 | 146 | ||
| 148 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr, | 147 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, addr, &al); |
| 149 | &al); | ||
| 150 | if (!al.map || !al.map->dso) { | 148 | if (!al.map || !al.map->dso) { |
| 151 | pr_debug("thread__find_addr_map failed\n"); | 149 | pr_debug("thread__find_addr_map failed\n"); |
| 152 | return -1; | 150 | return -1; |
| @@ -170,8 +168,8 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, | |||
| 170 | len = al.map->end - addr; | 168 | len = al.map->end - addr; |
| 171 | 169 | ||
| 172 | /* Read the object code using perf */ | 170 | /* Read the object code using perf */ |
| 173 | ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1, | 171 | ret_len = dso__data_read_offset(al.map->dso, thread->mg->machine, |
| 174 | len); | 172 | al.addr, buf1, len); |
| 175 | if (ret_len != len) { | 173 | if (ret_len != len) { |
| 176 | pr_debug("dso__data_read_offset failed\n"); | 174 | pr_debug("dso__data_read_offset failed\n"); |
| 177 | return -1; | 175 | return -1; |
| @@ -264,8 +262,7 @@ static int process_sample_event(struct machine *machine, | |||
| 264 | 262 | ||
| 265 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 263 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
| 266 | 264 | ||
| 267 | return read_object_code(sample.ip, READLEN, cpumode, thread, machine, | 265 | return read_object_code(sample.ip, READLEN, cpumode, thread, state); |
| 268 | state); | ||
| 269 | } | 266 | } |
| 270 | 267 | ||
| 271 | static int process_event(struct machine *machine, struct perf_evlist *evlist, | 268 | static int process_event(struct machine *machine, struct perf_evlist *evlist, |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index fc25e57f4a5d..ab28cca2cb97 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
| @@ -59,7 +59,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | __attribute__ ((noinline)) | 61 | __attribute__ ((noinline)) |
| 62 | static int unwind_thread(struct thread *thread, struct machine *machine) | 62 | static int unwind_thread(struct thread *thread) |
| 63 | { | 63 | { |
| 64 | struct perf_sample sample; | 64 | struct perf_sample sample; |
| 65 | unsigned long cnt = 0; | 65 | unsigned long cnt = 0; |
| @@ -72,7 +72,7 @@ static int unwind_thread(struct thread *thread, struct machine *machine) | |||
| 72 | goto out; | 72 | goto out; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | err = unwind__get_entries(unwind_entry, &cnt, machine, thread, | 75 | err = unwind__get_entries(unwind_entry, &cnt, thread, |
| 76 | &sample, MAX_STACK); | 76 | &sample, MAX_STACK); |
| 77 | if (err) | 77 | if (err) |
| 78 | pr_debug("unwind failed\n"); | 78 | pr_debug("unwind failed\n"); |
| @@ -89,21 +89,21 @@ static int unwind_thread(struct thread *thread, struct machine *machine) | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | __attribute__ ((noinline)) | 91 | __attribute__ ((noinline)) |
| 92 | static int krava_3(struct thread *thread, struct machine *machine) | 92 | static int krava_3(struct thread *thread) |
| 93 | { | 93 | { |
| 94 | return unwind_thread(thread, machine); | 94 | return unwind_thread(thread); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | __attribute__ ((noinline)) | 97 | __attribute__ ((noinline)) |
| 98 | static int krava_2(struct thread *thread, struct machine *machine) | 98 | static int krava_2(struct thread *thread) |
| 99 | { | 99 | { |
| 100 | return krava_3(thread, machine); | 100 | return krava_3(thread); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | __attribute__ ((noinline)) | 103 | __attribute__ ((noinline)) |
| 104 | static int krava_1(struct thread *thread, struct machine *machine) | 104 | static int krava_1(struct thread *thread) |
| 105 | { | 105 | { |
| 106 | return krava_2(thread, machine); | 106 | return krava_2(thread); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | int test__dwarf_unwind(void) | 109 | int test__dwarf_unwind(void) |
| @@ -137,7 +137,7 @@ int test__dwarf_unwind(void) | |||
| 137 | goto out; | 137 | goto out; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | err = krava_1(thread, machine); | 140 | err = krava_1(thread); |
| 141 | 141 | ||
| 142 | out: | 142 | out: |
| 143 | machine__delete_threads(machine); | 143 | machine__delete_threads(machine); |
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 5a31787cc6b9..74f257a81265 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
| @@ -43,7 +43,7 @@ static struct sample fake_samples[] = { | |||
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | static int add_hist_entries(struct perf_evlist *evlist, | 45 | static int add_hist_entries(struct perf_evlist *evlist, |
| 46 | struct machine *machine __maybe_unused) | 46 | struct machine *machine) |
| 47 | { | 47 | { |
| 48 | struct perf_evsel *evsel; | 48 | struct perf_evsel *evsel; |
| 49 | struct addr_location al; | 49 | struct addr_location al; |
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 4a456fef66ca..2113f1c8611f 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c | |||
| @@ -187,7 +187,7 @@ static int mmap_events(synth_cb synth) | |||
| 187 | 187 | ||
| 188 | pr_debug("looking for map %p\n", td->map); | 188 | pr_debug("looking for map %p\n", td->map); |
| 189 | 189 | ||
| 190 | thread__find_addr_map(thread, machine, | 190 | thread__find_addr_map(thread, |
| 191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, | 191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, |
| 192 | (unsigned long) (td->map + 1), &al); | 192 | (unsigned long) (td->map + 1), &al); |
| 193 | 193 | ||
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 7f2f51f93619..4169f460efa1 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
| @@ -1145,6 +1145,49 @@ static int test__pinned_group(struct perf_evlist *evlist) | |||
| 1145 | return 0; | 1145 | return 0; |
| 1146 | } | 1146 | } |
| 1147 | 1147 | ||
| 1148 | static int test__checkevent_breakpoint_len(struct perf_evlist *evlist) | ||
| 1149 | { | ||
| 1150 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1151 | |||
| 1152 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 1153 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 1154 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 1155 | TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == | ||
| 1156 | evsel->attr.bp_type); | ||
| 1157 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 == | ||
| 1158 | evsel->attr.bp_len); | ||
| 1159 | |||
| 1160 | return 0; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | static int test__checkevent_breakpoint_len_w(struct perf_evlist *evlist) | ||
| 1164 | { | ||
| 1165 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1166 | |||
| 1167 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 1168 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 1169 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 1170 | TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W == | ||
| 1171 | evsel->attr.bp_type); | ||
| 1172 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 == | ||
| 1173 | evsel->attr.bp_len); | ||
| 1174 | |||
| 1175 | return 0; | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | static int | ||
| 1179 | test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist) | ||
| 1180 | { | ||
| 1181 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1182 | |||
| 1183 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 1184 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
| 1185 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 1186 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 1187 | |||
| 1188 | return test__checkevent_breakpoint_rw(evlist); | ||
| 1189 | } | ||
| 1190 | |||
| 1148 | static int count_tracepoints(void) | 1191 | static int count_tracepoints(void) |
| 1149 | { | 1192 | { |
| 1150 | char events_path[PATH_MAX]; | 1193 | char events_path[PATH_MAX]; |
| @@ -1420,6 +1463,21 @@ static struct evlist_test test__events[] = { | |||
| 1420 | .check = test__pinned_group, | 1463 | .check = test__pinned_group, |
| 1421 | .id = 41, | 1464 | .id = 41, |
| 1422 | }, | 1465 | }, |
| 1466 | { | ||
| 1467 | .name = "mem:0/1", | ||
| 1468 | .check = test__checkevent_breakpoint_len, | ||
| 1469 | .id = 42, | ||
| 1470 | }, | ||
| 1471 | { | ||
| 1472 | .name = "mem:0/2:w", | ||
| 1473 | .check = test__checkevent_breakpoint_len_w, | ||
| 1474 | .id = 43, | ||
| 1475 | }, | ||
| 1476 | { | ||
| 1477 | .name = "mem:0/4:rw:u", | ||
| 1478 | .check = test__checkevent_breakpoint_len_rw_modifier, | ||
| 1479 | .id = 44 | ||
| 1480 | }, | ||
| 1423 | #if defined(__s390x__) | 1481 | #if defined(__s390x__) |
| 1424 | { | 1482 | { |
| 1425 | .name = "kvm-s390:kvm_s390_create_vm", | 1483 | .name = "kvm-s390:kvm_s390_create_vm", |
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index ca292f9a4ae2..4908c648a597 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c | |||
| @@ -126,16 +126,28 @@ static bool samples_same(const struct perf_sample *s1, | |||
| 126 | if (type & PERF_SAMPLE_TRANSACTION) | 126 | if (type & PERF_SAMPLE_TRANSACTION) |
| 127 | COMP(transaction); | 127 | COMP(transaction); |
| 128 | 128 | ||
| 129 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
| 130 | size_t sz = hweight_long(s1->intr_regs.mask) * sizeof(u64); | ||
| 131 | |||
| 132 | COMP(intr_regs.mask); | ||
| 133 | COMP(intr_regs.abi); | ||
| 134 | if (s1->intr_regs.abi && | ||
| 135 | (!s1->intr_regs.regs || !s2->intr_regs.regs || | ||
| 136 | memcmp(s1->intr_regs.regs, s2->intr_regs.regs, sz))) { | ||
| 137 | pr_debug("Samples differ at 'intr_regs'\n"); | ||
| 138 | return false; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 129 | return true; | 142 | return true; |
| 130 | } | 143 | } |
| 131 | 144 | ||
| 132 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | 145 | static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) |
| 133 | { | 146 | { |
| 134 | struct perf_evsel evsel = { | 147 | struct perf_evsel evsel = { |
| 135 | .needs_swap = false, | 148 | .needs_swap = false, |
| 136 | .attr = { | 149 | .attr = { |
| 137 | .sample_type = sample_type, | 150 | .sample_type = sample_type, |
| 138 | .sample_regs_user = sample_regs_user, | ||
| 139 | .read_format = read_format, | 151 | .read_format = read_format, |
| 140 | }, | 152 | }, |
| 141 | }; | 153 | }; |
| @@ -154,7 +166,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
| 154 | /* 1 branch_entry */ | 166 | /* 1 branch_entry */ |
| 155 | .data = {1, 211, 212, 213}, | 167 | .data = {1, 211, 212, 213}, |
| 156 | }; | 168 | }; |
| 157 | u64 user_regs[64]; | 169 | u64 regs[64]; |
| 158 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | 170 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; |
| 159 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | 171 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; |
| 160 | struct perf_sample sample = { | 172 | struct perf_sample sample = { |
| @@ -176,8 +188,8 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
| 176 | .branch_stack = &branch_stack.branch_stack, | 188 | .branch_stack = &branch_stack.branch_stack, |
| 177 | .user_regs = { | 189 | .user_regs = { |
| 178 | .abi = PERF_SAMPLE_REGS_ABI_64, | 190 | .abi = PERF_SAMPLE_REGS_ABI_64, |
| 179 | .mask = sample_regs_user, | 191 | .mask = sample_regs, |
| 180 | .regs = user_regs, | 192 | .regs = regs, |
| 181 | }, | 193 | }, |
| 182 | .user_stack = { | 194 | .user_stack = { |
| 183 | .size = sizeof(data), | 195 | .size = sizeof(data), |
| @@ -187,14 +199,25 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
| 187 | .time_enabled = 0x030a59d664fca7deULL, | 199 | .time_enabled = 0x030a59d664fca7deULL, |
| 188 | .time_running = 0x011b6ae553eb98edULL, | 200 | .time_running = 0x011b6ae553eb98edULL, |
| 189 | }, | 201 | }, |
| 202 | .intr_regs = { | ||
| 203 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
| 204 | .mask = sample_regs, | ||
| 205 | .regs = regs, | ||
| 206 | }, | ||
| 190 | }; | 207 | }; |
| 191 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | 208 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; |
| 192 | struct perf_sample sample_out; | 209 | struct perf_sample sample_out; |
| 193 | size_t i, sz, bufsz; | 210 | size_t i, sz, bufsz; |
| 194 | int err, ret = -1; | 211 | int err, ret = -1; |
| 195 | 212 | ||
| 196 | for (i = 0; i < sizeof(user_regs); i++) | 213 | if (sample_type & PERF_SAMPLE_REGS_USER) |
| 197 | *(i + (u8 *)user_regs) = i & 0xfe; | 214 | evsel.attr.sample_regs_user = sample_regs; |
| 215 | |||
| 216 | if (sample_type & PERF_SAMPLE_REGS_INTR) | ||
| 217 | evsel.attr.sample_regs_intr = sample_regs; | ||
| 218 | |||
| 219 | for (i = 0; i < sizeof(regs); i++) | ||
| 220 | *(i + (u8 *)regs) = i & 0xfe; | ||
| 198 | 221 | ||
| 199 | if (read_format & PERF_FORMAT_GROUP) { | 222 | if (read_format & PERF_FORMAT_GROUP) { |
| 200 | sample.read.group.nr = 4; | 223 | sample.read.group.nr = 4; |
| @@ -271,7 +294,7 @@ int test__sample_parsing(void) | |||
| 271 | { | 294 | { |
| 272 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | 295 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; |
| 273 | u64 sample_type; | 296 | u64 sample_type; |
| 274 | u64 sample_regs_user; | 297 | u64 sample_regs; |
| 275 | size_t i; | 298 | size_t i; |
| 276 | int err; | 299 | int err; |
| 277 | 300 | ||
| @@ -280,7 +303,7 @@ int test__sample_parsing(void) | |||
| 280 | * were added. Please actually update the test rather than just change | 303 | * were added. Please actually update the test rather than just change |
| 281 | * the condition below. | 304 | * the condition below. |
| 282 | */ | 305 | */ |
| 283 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { | 306 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_REGS_INTR << 1) { |
| 284 | pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); | 307 | pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); |
| 285 | return -1; | 308 | return -1; |
| 286 | } | 309 | } |
| @@ -297,22 +320,24 @@ int test__sample_parsing(void) | |||
| 297 | } | 320 | } |
| 298 | continue; | 321 | continue; |
| 299 | } | 322 | } |
| 323 | sample_regs = 0; | ||
| 300 | 324 | ||
| 301 | if (sample_type == PERF_SAMPLE_REGS_USER) | 325 | if (sample_type == PERF_SAMPLE_REGS_USER) |
| 302 | sample_regs_user = 0x3fff; | 326 | sample_regs = 0x3fff; |
| 303 | else | 327 | |
| 304 | sample_regs_user = 0; | 328 | if (sample_type == PERF_SAMPLE_REGS_INTR) |
| 329 | sample_regs = 0xff0fff; | ||
| 305 | 330 | ||
| 306 | err = do_test(sample_type, sample_regs_user, 0); | 331 | err = do_test(sample_type, sample_regs, 0); |
| 307 | if (err) | 332 | if (err) |
| 308 | return err; | 333 | return err; |
| 309 | } | 334 | } |
| 310 | 335 | ||
| 311 | /* Test all sample format bits together */ | 336 | /* Test all sample format bits together */ |
| 312 | sample_type = PERF_SAMPLE_MAX - 1; | 337 | sample_type = PERF_SAMPLE_MAX - 1; |
| 313 | sample_regs_user = 0x3fff; | 338 | sample_regs = 0x3fff; /* shared yb intr and user regs */ |
| 314 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | 339 | for (i = 0; i < ARRAY_SIZE(rf); i++) { |
| 315 | err = do_test(sample_type, sample_regs_user, rf[i]); | 340 | err = do_test(sample_type, sample_regs, rf[i]); |
| 316 | if (err) | 341 | if (err) |
| 317 | return err; | 342 | return err; |
| 318 | } | 343 | } |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index f0697a3aede0..1e0a2fd80115 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
| @@ -27,6 +27,7 @@ static struct annotate_browser_opt { | |||
| 27 | bool hide_src_code, | 27 | bool hide_src_code, |
| 28 | use_offset, | 28 | use_offset, |
| 29 | jump_arrows, | 29 | jump_arrows, |
| 30 | show_linenr, | ||
| 30 | show_nr_jumps; | 31 | show_nr_jumps; |
| 31 | } annotate_browser__opts = { | 32 | } annotate_browser__opts = { |
| 32 | .use_offset = true, | 33 | .use_offset = true, |
| @@ -128,7 +129,11 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
| 128 | if (!*dl->line) | 129 | if (!*dl->line) |
| 129 | slsmg_write_nstring(" ", width - pcnt_width); | 130 | slsmg_write_nstring(" ", width - pcnt_width); |
| 130 | else if (dl->offset == -1) { | 131 | else if (dl->offset == -1) { |
| 131 | printed = scnprintf(bf, sizeof(bf), "%*s ", | 132 | if (dl->line_nr && annotate_browser__opts.show_linenr) |
| 133 | printed = scnprintf(bf, sizeof(bf), "%-*d ", | ||
| 134 | ab->addr_width + 1, dl->line_nr); | ||
| 135 | else | ||
| 136 | printed = scnprintf(bf, sizeof(bf), "%*s ", | ||
| 132 | ab->addr_width, " "); | 137 | ab->addr_width, " "); |
| 133 | slsmg_write_nstring(bf, printed); | 138 | slsmg_write_nstring(bf, printed); |
| 134 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); | 139 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); |
| @@ -733,6 +738,7 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
| 733 | "o Toggle disassembler output/simplified view\n" | 738 | "o Toggle disassembler output/simplified view\n" |
| 734 | "s Toggle source code view\n" | 739 | "s Toggle source code view\n" |
| 735 | "/ Search string\n" | 740 | "/ Search string\n" |
| 741 | "k Toggle line numbers\n" | ||
| 736 | "r Run available scripts\n" | 742 | "r Run available scripts\n" |
| 737 | "? Search string backwards\n"); | 743 | "? Search string backwards\n"); |
| 738 | continue; | 744 | continue; |
| @@ -741,6 +747,10 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
| 741 | script_browse(NULL); | 747 | script_browse(NULL); |
| 742 | continue; | 748 | continue; |
| 743 | } | 749 | } |
| 750 | case 'k': | ||
| 751 | annotate_browser__opts.show_linenr = | ||
| 752 | !annotate_browser__opts.show_linenr; | ||
| 753 | break; | ||
| 744 | case 'H': | 754 | case 'H': |
| 745 | nd = browser->curr_hot; | 755 | nd = browser->curr_hot; |
| 746 | break; | 756 | break; |
| @@ -984,6 +994,7 @@ static struct annotate_config { | |||
| 984 | } annotate__configs[] = { | 994 | } annotate__configs[] = { |
| 985 | ANNOTATE_CFG(hide_src_code), | 995 | ANNOTATE_CFG(hide_src_code), |
| 986 | ANNOTATE_CFG(jump_arrows), | 996 | ANNOTATE_CFG(jump_arrows), |
| 997 | ANNOTATE_CFG(show_linenr), | ||
| 987 | ANNOTATE_CFG(show_nr_jumps), | 998 | ANNOTATE_CFG(show_nr_jumps), |
| 988 | ANNOTATE_CFG(use_offset), | 999 | ANNOTATE_CFG(use_offset), |
| 989 | }; | 1000 | }; |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index cfb976b3de3a..12c17c5a3d68 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
| @@ -463,23 +463,6 @@ out: | |||
| 463 | return key; | 463 | return key; |
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | static char *callchain_list__sym_name(struct callchain_list *cl, | ||
| 467 | char *bf, size_t bfsize, bool show_dso) | ||
| 468 | { | ||
| 469 | int printed; | ||
| 470 | |||
| 471 | if (cl->ms.sym) | ||
| 472 | printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
| 473 | else | ||
| 474 | printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
| 475 | |||
| 476 | if (show_dso) | ||
| 477 | scnprintf(bf + printed, bfsize - printed, " %s", | ||
| 478 | cl->ms.map ? cl->ms.map->dso->short_name : "unknown"); | ||
| 479 | |||
| 480 | return bf; | ||
| 481 | } | ||
| 482 | |||
| 483 | struct callchain_print_arg { | 466 | struct callchain_print_arg { |
| 484 | /* for hists browser */ | 467 | /* for hists browser */ |
| 485 | off_t row_offset; | 468 | off_t row_offset; |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index fc654fb77ace..4b3585eed1e8 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
| @@ -89,15 +89,6 @@ void perf_gtk__init_hpp(void) | |||
| 89 | perf_gtk__hpp_color_overhead_acc; | 89 | perf_gtk__hpp_color_overhead_acc; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | static void callchain_list__sym_name(struct callchain_list *cl, | ||
| 93 | char *bf, size_t bfsize) | ||
| 94 | { | ||
| 95 | if (cl->ms.sym) | ||
| 96 | scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
| 97 | else | ||
| 98 | scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
| 99 | } | ||
| 100 | |||
| 101 | static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, | 92 | static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, |
| 102 | GtkTreeIter *parent, int col, u64 total) | 93 | GtkTreeIter *parent, int col, u64 total) |
| 103 | { | 94 | { |
| @@ -128,7 +119,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, | |||
| 128 | scnprintf(buf, sizeof(buf), "%5.2f%%", percent); | 119 | scnprintf(buf, sizeof(buf), "%5.2f%%", percent); |
| 129 | gtk_tree_store_set(store, &iter, 0, buf, -1); | 120 | gtk_tree_store_set(store, &iter, 0, buf, -1); |
| 130 | 121 | ||
| 131 | callchain_list__sym_name(chain, buf, sizeof(buf)); | 122 | callchain_list__sym_name(chain, buf, sizeof(buf), false); |
| 132 | gtk_tree_store_set(store, &iter, col, buf, -1); | 123 | gtk_tree_store_set(store, &iter, col, buf, -1); |
| 133 | 124 | ||
| 134 | if (need_new_parent) { | 125 | if (need_new_parent) { |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 15b451acbde6..dfcbc90146ef 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
| @@ -41,6 +41,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
| 41 | { | 41 | { |
| 42 | int i; | 42 | int i; |
| 43 | size_t ret = 0; | 43 | size_t ret = 0; |
| 44 | char bf[1024]; | ||
| 44 | 45 | ||
| 45 | ret += callchain__fprintf_left_margin(fp, left_margin); | 46 | ret += callchain__fprintf_left_margin(fp, left_margin); |
| 46 | for (i = 0; i < depth; i++) { | 47 | for (i = 0; i < depth; i++) { |
| @@ -56,11 +57,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
| 56 | } else | 57 | } else |
| 57 | ret += fprintf(fp, "%s", " "); | 58 | ret += fprintf(fp, "%s", " "); |
| 58 | } | 59 | } |
| 59 | if (chain->ms.sym) | 60 | fputs(callchain_list__sym_name(chain, bf, sizeof(bf), false), fp); |
| 60 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); | 61 | fputc('\n', fp); |
| 61 | else | ||
| 62 | ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip); | ||
| 63 | |||
| 64 | return ret; | 62 | return ret; |
| 65 | } | 63 | } |
| 66 | 64 | ||
| @@ -168,6 +166,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
| 168 | struct rb_node *node; | 166 | struct rb_node *node; |
| 169 | int i = 0; | 167 | int i = 0; |
| 170 | int ret = 0; | 168 | int ret = 0; |
| 169 | char bf[1024]; | ||
| 171 | 170 | ||
| 172 | /* | 171 | /* |
| 173 | * If have one single callchain root, don't bother printing | 172 | * If have one single callchain root, don't bother printing |
| @@ -196,10 +195,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
| 196 | } else | 195 | } else |
| 197 | ret += callchain__fprintf_left_margin(fp, left_margin); | 196 | ret += callchain__fprintf_left_margin(fp, left_margin); |
| 198 | 197 | ||
| 199 | if (chain->ms.sym) | 198 | ret += fprintf(fp, "%s\n", callchain_list__sym_name(chain, bf, sizeof(bf), |
| 200 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 199 | false)); |
| 201 | else | ||
| 202 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
| 203 | 200 | ||
| 204 | if (++entries_printed == callchain_param.print_limit) | 201 | if (++entries_printed == callchain_param.print_limit) |
| 205 | break; | 202 | break; |
| @@ -219,6 +216,7 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, | |||
| 219 | { | 216 | { |
| 220 | struct callchain_list *chain; | 217 | struct callchain_list *chain; |
| 221 | size_t ret = 0; | 218 | size_t ret = 0; |
| 219 | char bf[1024]; | ||
| 222 | 220 | ||
| 223 | if (!node) | 221 | if (!node) |
| 224 | return 0; | 222 | return 0; |
| @@ -229,11 +227,8 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, | |||
| 229 | list_for_each_entry(chain, &node->val, list) { | 227 | list_for_each_entry(chain, &node->val, list) { |
| 230 | if (chain->ip >= PERF_CONTEXT_MAX) | 228 | if (chain->ip >= PERF_CONTEXT_MAX) |
| 231 | continue; | 229 | continue; |
| 232 | if (chain->ms.sym) | 230 | ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain, |
| 233 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 231 | bf, sizeof(bf), false)); |
| 234 | else | ||
| 235 | ret += fprintf(fp, " %p\n", | ||
| 236 | (void *)(long)chain->ip); | ||
| 237 | } | 232 | } |
| 238 | 233 | ||
| 239 | return ret; | 234 | return ret; |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7dabde14ea54..e5670f1af737 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -17,11 +17,13 @@ | |||
| 17 | #include "debug.h" | 17 | #include "debug.h" |
| 18 | #include "annotate.h" | 18 | #include "annotate.h" |
| 19 | #include "evsel.h" | 19 | #include "evsel.h" |
| 20 | #include <regex.h> | ||
| 20 | #include <pthread.h> | 21 | #include <pthread.h> |
| 21 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
| 22 | 23 | ||
| 23 | const char *disassembler_style; | 24 | const char *disassembler_style; |
| 24 | const char *objdump_path; | 25 | const char *objdump_path; |
| 26 | static regex_t file_lineno; | ||
| 25 | 27 | ||
| 26 | static struct ins *ins__find(const char *name); | 28 | static struct ins *ins__find(const char *name); |
| 27 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 29 | static int disasm_line__parse(char *line, char **namep, char **rawp); |
| @@ -570,13 +572,15 @@ out_free_name: | |||
| 570 | return -1; | 572 | return -1; |
| 571 | } | 573 | } |
| 572 | 574 | ||
| 573 | static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) | 575 | static struct disasm_line *disasm_line__new(s64 offset, char *line, |
| 576 | size_t privsize, int line_nr) | ||
| 574 | { | 577 | { |
| 575 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 578 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
| 576 | 579 | ||
| 577 | if (dl != NULL) { | 580 | if (dl != NULL) { |
| 578 | dl->offset = offset; | 581 | dl->offset = offset; |
| 579 | dl->line = strdup(line); | 582 | dl->line = strdup(line); |
| 583 | dl->line_nr = line_nr; | ||
| 580 | if (dl->line == NULL) | 584 | if (dl->line == NULL) |
| 581 | goto out_delete; | 585 | goto out_delete; |
| 582 | 586 | ||
| @@ -788,13 +792,15 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
| 788 | * The ops.raw part will be parsed further according to type of the instruction. | 792 | * The ops.raw part will be parsed further according to type of the instruction. |
| 789 | */ | 793 | */ |
| 790 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 794 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
| 791 | FILE *file, size_t privsize) | 795 | FILE *file, size_t privsize, |
| 796 | int *line_nr) | ||
| 792 | { | 797 | { |
| 793 | struct annotation *notes = symbol__annotation(sym); | 798 | struct annotation *notes = symbol__annotation(sym); |
| 794 | struct disasm_line *dl; | 799 | struct disasm_line *dl; |
| 795 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; | 800 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; |
| 796 | size_t line_len; | 801 | size_t line_len; |
| 797 | s64 line_ip, offset = -1; | 802 | s64 line_ip, offset = -1; |
| 803 | regmatch_t match[2]; | ||
| 798 | 804 | ||
| 799 | if (getline(&line, &line_len, file) < 0) | 805 | if (getline(&line, &line_len, file) < 0) |
| 800 | return -1; | 806 | return -1; |
| @@ -812,6 +818,12 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
| 812 | line_ip = -1; | 818 | line_ip = -1; |
| 813 | parsed_line = line; | 819 | parsed_line = line; |
| 814 | 820 | ||
| 821 | /* /filename:linenr ? Save line number and ignore. */ | ||
| 822 | if (regexec(&file_lineno, line, 2, match, 0) == 0) { | ||
| 823 | *line_nr = atoi(line + match[1].rm_so); | ||
| 824 | return 0; | ||
| 825 | } | ||
| 826 | |||
| 815 | /* | 827 | /* |
| 816 | * Strip leading spaces: | 828 | * Strip leading spaces: |
| 817 | */ | 829 | */ |
| @@ -842,8 +854,9 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
| 842 | parsed_line = tmp2 + 1; | 854 | parsed_line = tmp2 + 1; |
| 843 | } | 855 | } |
| 844 | 856 | ||
| 845 | dl = disasm_line__new(offset, parsed_line, privsize); | 857 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr); |
| 846 | free(line); | 858 | free(line); |
| 859 | (*line_nr)++; | ||
| 847 | 860 | ||
| 848 | if (dl == NULL) | 861 | if (dl == NULL) |
| 849 | return -1; | 862 | return -1; |
| @@ -869,6 +882,11 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
| 869 | return 0; | 882 | return 0; |
| 870 | } | 883 | } |
| 871 | 884 | ||
| 885 | static __attribute__((constructor)) void symbol__init_regexpr(void) | ||
| 886 | { | ||
| 887 | regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED); | ||
| 888 | } | ||
| 889 | |||
| 872 | static void delete_last_nop(struct symbol *sym) | 890 | static void delete_last_nop(struct symbol *sym) |
| 873 | { | 891 | { |
| 874 | struct annotation *notes = symbol__annotation(sym); | 892 | struct annotation *notes = symbol__annotation(sym); |
| @@ -904,6 +922,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
| 904 | char symfs_filename[PATH_MAX]; | 922 | char symfs_filename[PATH_MAX]; |
| 905 | struct kcore_extract kce; | 923 | struct kcore_extract kce; |
| 906 | bool delete_extract = false; | 924 | bool delete_extract = false; |
| 925 | int lineno = 0; | ||
| 907 | 926 | ||
| 908 | if (filename) | 927 | if (filename) |
| 909 | symbol__join_symfs(symfs_filename, filename); | 928 | symbol__join_symfs(symfs_filename, filename); |
| @@ -915,6 +934,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
| 915 | return -ENOMEM; | 934 | return -ENOMEM; |
| 916 | } | 935 | } |
| 917 | goto fallback; | 936 | goto fallback; |
| 937 | } else if (dso__is_kcore(dso)) { | ||
| 938 | goto fallback; | ||
| 918 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | 939 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || |
| 919 | strstr(command, "[kernel.kallsyms]") || | 940 | strstr(command, "[kernel.kallsyms]") || |
| 920 | access(symfs_filename, R_OK)) { | 941 | access(symfs_filename, R_OK)) { |
| @@ -982,7 +1003,7 @@ fallback: | |||
| 982 | snprintf(command, sizeof(command), | 1003 | snprintf(command, sizeof(command), |
| 983 | "%s %s%s --start-address=0x%016" PRIx64 | 1004 | "%s %s%s --start-address=0x%016" PRIx64 |
| 984 | " --stop-address=0x%016" PRIx64 | 1005 | " --stop-address=0x%016" PRIx64 |
| 985 | " -d %s %s -C %s 2>/dev/null|grep -v %s|expand", | 1006 | " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand", |
| 986 | objdump_path ? objdump_path : "objdump", | 1007 | objdump_path ? objdump_path : "objdump", |
| 987 | disassembler_style ? "-M " : "", | 1008 | disassembler_style ? "-M " : "", |
| 988 | disassembler_style ? disassembler_style : "", | 1009 | disassembler_style ? disassembler_style : "", |
| @@ -999,7 +1020,8 @@ fallback: | |||
| 999 | goto out_free_filename; | 1020 | goto out_free_filename; |
| 1000 | 1021 | ||
| 1001 | while (!feof(file)) | 1022 | while (!feof(file)) |
| 1002 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) | 1023 | if (symbol__parse_objdump_line(sym, map, file, privsize, |
| 1024 | &lineno) < 0) | ||
| 1003 | break; | 1025 | break; |
| 1004 | 1026 | ||
| 1005 | /* | 1027 | /* |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 112d6e268150..0784a9420528 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
| @@ -58,6 +58,7 @@ struct disasm_line { | |||
| 58 | char *line; | 58 | char *line; |
| 59 | char *name; | 59 | char *name; |
| 60 | struct ins *ins; | 60 | struct ins *ins; |
| 61 | int line_nr; | ||
| 61 | struct ins_operands ops; | 62 | struct ins_operands ops; |
| 62 | }; | 63 | }; |
| 63 | 64 | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index a904a4cfe7d3..e8d79e5bfaf7 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
| @@ -15,6 +15,11 @@ | |||
| 15 | #include "debug.h" | 15 | #include "debug.h" |
| 16 | #include "session.h" | 16 | #include "session.h" |
| 17 | #include "tool.h" | 17 | #include "tool.h" |
| 18 | #include "header.h" | ||
| 19 | #include "vdso.h" | ||
| 20 | |||
| 21 | |||
| 22 | static bool no_buildid_cache; | ||
| 18 | 23 | ||
| 19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 24 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
| 20 | union perf_event *event, | 25 | union perf_event *event, |
| @@ -33,8 +38,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
| 33 | return -1; | 38 | return -1; |
| 34 | } | 39 | } |
| 35 | 40 | ||
| 36 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 41 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); |
| 37 | sample->ip, &al); | ||
| 38 | 42 | ||
| 39 | if (al.map != NULL) | 43 | if (al.map != NULL) |
| 40 | al.map->dso->hit = 1; | 44 | al.map->dso->hit = 1; |
| @@ -106,3 +110,343 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) | |||
| 106 | build_id_hex, build_id_hex + 2); | 110 | build_id_hex, build_id_hex + 2); |
| 107 | return bf; | 111 | return bf; |
| 108 | } | 112 | } |
| 113 | |||
| 114 | #define dsos__for_each_with_build_id(pos, head) \ | ||
| 115 | list_for_each_entry(pos, head, node) \ | ||
| 116 | if (!pos->has_build_id) \ | ||
| 117 | continue; \ | ||
| 118 | else | ||
| 119 | |||
| 120 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
| 121 | pid_t pid, u16 misc, int fd) | ||
| 122 | { | ||
| 123 | int err; | ||
| 124 | struct build_id_event b; | ||
| 125 | size_t len; | ||
| 126 | |||
| 127 | len = name_len + 1; | ||
| 128 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
| 129 | |||
| 130 | memset(&b, 0, sizeof(b)); | ||
| 131 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
| 132 | b.pid = pid; | ||
| 133 | b.header.misc = misc; | ||
| 134 | b.header.size = sizeof(b) + len; | ||
| 135 | |||
| 136 | err = writen(fd, &b, sizeof(b)); | ||
| 137 | if (err < 0) | ||
| 138 | return err; | ||
| 139 | |||
| 140 | return write_padded(fd, name, name_len + 1, len); | ||
| 141 | } | ||
| 142 | |||
| 143 | static int __dsos__write_buildid_table(struct list_head *head, | ||
| 144 | struct machine *machine, | ||
| 145 | pid_t pid, u16 misc, int fd) | ||
| 146 | { | ||
| 147 | char nm[PATH_MAX]; | ||
| 148 | struct dso *pos; | ||
| 149 | |||
| 150 | dsos__for_each_with_build_id(pos, head) { | ||
| 151 | int err; | ||
| 152 | const char *name; | ||
| 153 | size_t name_len; | ||
| 154 | |||
| 155 | if (!pos->hit) | ||
| 156 | continue; | ||
| 157 | |||
| 158 | if (dso__is_vdso(pos)) { | ||
| 159 | name = pos->short_name; | ||
| 160 | name_len = pos->short_name_len + 1; | ||
| 161 | } else if (dso__is_kcore(pos)) { | ||
| 162 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
| 163 | name = nm; | ||
| 164 | name_len = strlen(nm) + 1; | ||
| 165 | } else { | ||
| 166 | name = pos->long_name; | ||
| 167 | name_len = pos->long_name_len + 1; | ||
| 168 | } | ||
| 169 | |||
| 170 | err = write_buildid(name, name_len, pos->build_id, | ||
| 171 | pid, misc, fd); | ||
| 172 | if (err) | ||
| 173 | return err; | ||
| 174 | } | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
| 180 | { | ||
| 181 | int err; | ||
| 182 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
| 183 | umisc = PERF_RECORD_MISC_USER; | ||
| 184 | |||
| 185 | if (!machine__is_host(machine)) { | ||
| 186 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
| 187 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
| 188 | } | ||
| 189 | |||
| 190 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, | ||
| 191 | machine->pid, kmisc, fd); | ||
| 192 | if (err == 0) | ||
| 193 | err = __dsos__write_buildid_table(&machine->user_dsos.head, | ||
| 194 | machine, machine->pid, umisc, | ||
| 195 | fd); | ||
| 196 | return err; | ||
| 197 | } | ||
| 198 | |||
| 199 | int perf_session__write_buildid_table(struct perf_session *session, int fd) | ||
| 200 | { | ||
| 201 | struct rb_node *nd; | ||
| 202 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
| 203 | |||
| 204 | if (err) | ||
| 205 | return err; | ||
| 206 | |||
| 207 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 208 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 209 | err = machine__write_buildid_table(pos, fd); | ||
| 210 | if (err) | ||
| 211 | break; | ||
| 212 | } | ||
| 213 | return err; | ||
| 214 | } | ||
| 215 | |||
| 216 | static int __dsos__hit_all(struct list_head *head) | ||
| 217 | { | ||
| 218 | struct dso *pos; | ||
| 219 | |||
| 220 | list_for_each_entry(pos, head, node) | ||
| 221 | pos->hit = true; | ||
| 222 | |||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | static int machine__hit_all_dsos(struct machine *machine) | ||
| 227 | { | ||
| 228 | int err; | ||
| 229 | |||
| 230 | err = __dsos__hit_all(&machine->kernel_dsos.head); | ||
| 231 | if (err) | ||
| 232 | return err; | ||
| 233 | |||
| 234 | return __dsos__hit_all(&machine->user_dsos.head); | ||
| 235 | } | ||
| 236 | |||
| 237 | int dsos__hit_all(struct perf_session *session) | ||
| 238 | { | ||
| 239 | struct rb_node *nd; | ||
| 240 | int err; | ||
| 241 | |||
| 242 | err = machine__hit_all_dsos(&session->machines.host); | ||
| 243 | if (err) | ||
| 244 | return err; | ||
| 245 | |||
| 246 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 247 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 248 | |||
| 249 | err = machine__hit_all_dsos(pos); | ||
| 250 | if (err) | ||
| 251 | return err; | ||
| 252 | } | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | void disable_buildid_cache(void) | ||
| 258 | { | ||
| 259 | no_buildid_cache = true; | ||
| 260 | } | ||
| 261 | |||
| 262 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
| 263 | const char *name, bool is_kallsyms, bool is_vdso) | ||
| 264 | { | ||
| 265 | const size_t size = PATH_MAX; | ||
| 266 | char *realname, *filename = zalloc(size), | ||
| 267 | *linkname = zalloc(size), *targetname; | ||
| 268 | int len, err = -1; | ||
| 269 | bool slash = is_kallsyms || is_vdso; | ||
| 270 | |||
| 271 | if (is_kallsyms) { | ||
| 272 | if (symbol_conf.kptr_restrict) { | ||
| 273 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
| 274 | err = 0; | ||
| 275 | goto out_free; | ||
| 276 | } | ||
| 277 | realname = (char *) name; | ||
| 278 | } else | ||
| 279 | realname = realpath(name, NULL); | ||
| 280 | |||
| 281 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
| 282 | goto out_free; | ||
| 283 | |||
| 284 | len = scnprintf(filename, size, "%s%s%s", | ||
| 285 | debugdir, slash ? "/" : "", | ||
| 286 | is_vdso ? DSO__NAME_VDSO : realname); | ||
| 287 | if (mkdir_p(filename, 0755)) | ||
| 288 | goto out_free; | ||
| 289 | |||
| 290 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
| 291 | |||
| 292 | if (access(filename, F_OK)) { | ||
| 293 | if (is_kallsyms) { | ||
| 294 | if (copyfile("/proc/kallsyms", filename)) | ||
| 295 | goto out_free; | ||
| 296 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
| 297 | goto out_free; | ||
| 298 | } | ||
| 299 | |||
| 300 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
| 301 | debugdir, sbuild_id); | ||
| 302 | |||
| 303 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
| 304 | goto out_free; | ||
| 305 | |||
| 306 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
| 307 | targetname = filename + strlen(debugdir) - 5; | ||
| 308 | memcpy(targetname, "../..", 5); | ||
| 309 | |||
| 310 | if (symlink(targetname, linkname) == 0) | ||
| 311 | err = 0; | ||
| 312 | out_free: | ||
| 313 | if (!is_kallsyms) | ||
| 314 | free(realname); | ||
| 315 | free(filename); | ||
| 316 | free(linkname); | ||
| 317 | return err; | ||
| 318 | } | ||
| 319 | |||
| 320 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
| 321 | const char *name, const char *debugdir, | ||
| 322 | bool is_kallsyms, bool is_vdso) | ||
| 323 | { | ||
| 324 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 325 | |||
| 326 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
| 327 | |||
| 328 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
| 329 | is_kallsyms, is_vdso); | ||
| 330 | } | ||
| 331 | |||
| 332 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
| 333 | { | ||
| 334 | const size_t size = PATH_MAX; | ||
| 335 | char *filename = zalloc(size), | ||
| 336 | *linkname = zalloc(size); | ||
| 337 | int err = -1; | ||
| 338 | |||
| 339 | if (filename == NULL || linkname == NULL) | ||
| 340 | goto out_free; | ||
| 341 | |||
| 342 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
| 343 | debugdir, sbuild_id, sbuild_id + 2); | ||
| 344 | |||
| 345 | if (access(linkname, F_OK)) | ||
| 346 | goto out_free; | ||
| 347 | |||
| 348 | if (readlink(linkname, filename, size - 1) < 0) | ||
| 349 | goto out_free; | ||
| 350 | |||
| 351 | if (unlink(linkname)) | ||
| 352 | goto out_free; | ||
| 353 | |||
| 354 | /* | ||
| 355 | * Since the link is relative, we must make it absolute: | ||
| 356 | */ | ||
| 357 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
| 358 | debugdir, sbuild_id, filename); | ||
| 359 | |||
| 360 | if (unlink(linkname)) | ||
| 361 | goto out_free; | ||
| 362 | |||
| 363 | err = 0; | ||
| 364 | out_free: | ||
| 365 | free(filename); | ||
| 366 | free(linkname); | ||
| 367 | return err; | ||
| 368 | } | ||
| 369 | |||
| 370 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
| 371 | const char *debugdir) | ||
| 372 | { | ||
| 373 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
| 374 | bool is_vdso = dso__is_vdso(dso); | ||
| 375 | const char *name = dso->long_name; | ||
| 376 | char nm[PATH_MAX]; | ||
| 377 | |||
| 378 | if (dso__is_kcore(dso)) { | ||
| 379 | is_kallsyms = true; | ||
| 380 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
| 381 | name = nm; | ||
| 382 | } | ||
| 383 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
| 384 | debugdir, is_kallsyms, is_vdso); | ||
| 385 | } | ||
| 386 | |||
| 387 | static int __dsos__cache_build_ids(struct list_head *head, | ||
| 388 | struct machine *machine, const char *debugdir) | ||
| 389 | { | ||
| 390 | struct dso *pos; | ||
| 391 | int err = 0; | ||
| 392 | |||
| 393 | dsos__for_each_with_build_id(pos, head) | ||
| 394 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
| 395 | err = -1; | ||
| 396 | |||
| 397 | return err; | ||
| 398 | } | ||
| 399 | |||
| 400 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
| 401 | { | ||
| 402 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, | ||
| 403 | debugdir); | ||
| 404 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, | ||
| 405 | debugdir); | ||
| 406 | return ret; | ||
| 407 | } | ||
| 408 | |||
| 409 | int perf_session__cache_build_ids(struct perf_session *session) | ||
| 410 | { | ||
| 411 | struct rb_node *nd; | ||
| 412 | int ret; | ||
| 413 | char debugdir[PATH_MAX]; | ||
| 414 | |||
| 415 | if (no_buildid_cache) | ||
| 416 | return 0; | ||
| 417 | |||
| 418 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | ||
| 419 | |||
| 420 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
| 421 | return -1; | ||
| 422 | |||
| 423 | ret = machine__cache_build_ids(&session->machines.host, debugdir); | ||
| 424 | |||
| 425 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 426 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 427 | ret |= machine__cache_build_ids(pos, debugdir); | ||
| 428 | } | ||
| 429 | return ret ? -1 : 0; | ||
| 430 | } | ||
| 431 | |||
| 432 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
| 433 | { | ||
| 434 | bool ret; | ||
| 435 | |||
| 436 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
| 437 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
| 438 | return ret; | ||
| 439 | } | ||
| 440 | |||
| 441 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
| 442 | { | ||
| 443 | struct rb_node *nd; | ||
| 444 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
| 445 | |||
| 446 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 447 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 448 | ret |= machine__read_build_ids(pos, with_hits); | ||
| 449 | } | ||
| 450 | |||
| 451 | return ret; | ||
| 452 | } | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index ae392561470b..8236319514d5 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
| @@ -15,4 +15,16 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); | |||
| 15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | 15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, |
| 16 | struct perf_sample *sample, struct perf_evsel *evsel, | 16 | struct perf_sample *sample, struct perf_evsel *evsel, |
| 17 | struct machine *machine); | 17 | struct machine *machine); |
| 18 | |||
| 19 | int dsos__hit_all(struct perf_session *session); | ||
| 20 | |||
| 21 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); | ||
| 22 | int perf_session__write_buildid_table(struct perf_session *session, int fd); | ||
| 23 | int perf_session__cache_build_ids(struct perf_session *session); | ||
| 24 | |||
| 25 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
| 26 | const char *name, bool is_kallsyms, bool is_vdso); | ||
| 27 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
| 28 | void disable_buildid_cache(void); | ||
| 29 | |||
| 18 | #endif | 30 | #endif |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index c84d3f8dcb75..38da69c8c1ff 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -754,8 +754,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent | |||
| 754 | 754 | ||
| 755 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || | 755 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || |
| 756 | sort__has_parent) { | 756 | sort__has_parent) { |
| 757 | return machine__resolve_callchain(al->machine, evsel, al->thread, | 757 | return thread__resolve_callchain(al->thread, evsel, sample, |
| 758 | sample, parent, al, max_stack); | 758 | parent, al, max_stack); |
| 759 | } | 759 | } |
| 760 | return 0; | 760 | return 0; |
| 761 | } | 761 | } |
| @@ -808,3 +808,22 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
| 808 | out: | 808 | out: |
| 809 | return 1; | 809 | return 1; |
| 810 | } | 810 | } |
| 811 | |||
| 812 | char *callchain_list__sym_name(struct callchain_list *cl, | ||
| 813 | char *bf, size_t bfsize, bool show_dso) | ||
| 814 | { | ||
| 815 | int printed; | ||
| 816 | |||
| 817 | if (cl->ms.sym) { | ||
| 818 | printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
| 819 | } else | ||
| 820 | printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
| 821 | |||
| 822 | if (show_dso) | ||
| 823 | scnprintf(bf + printed, bfsize - printed, " %s", | ||
| 824 | cl->ms.map ? | ||
| 825 | cl->ms.map->dso->short_name : | ||
| 826 | "unknown"); | ||
| 827 | |||
| 828 | return bf; | ||
| 829 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 94cfefddf4db..3e1ed15d11f1 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -184,15 +184,16 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | |||
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | #ifdef HAVE_SKIP_CALLCHAIN_IDX | 186 | #ifdef HAVE_SKIP_CALLCHAIN_IDX |
| 187 | extern int arch_skip_callchain_idx(struct machine *machine, | 187 | extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain); |
| 188 | struct thread *thread, struct ip_callchain *chain); | ||
| 189 | #else | 188 | #else |
| 190 | static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused, | 189 | static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, |
| 191 | struct thread *thread __maybe_unused, | ||
| 192 | struct ip_callchain *chain __maybe_unused) | 190 | struct ip_callchain *chain __maybe_unused) |
| 193 | { | 191 | { |
| 194 | return -1; | 192 | return -1; |
| 195 | } | 193 | } |
| 196 | #endif | 194 | #endif |
| 197 | 195 | ||
| 196 | char *callchain_list__sym_name(struct callchain_list *cl, | ||
| 197 | char *bf, size_t bfsize, bool show_dso); | ||
| 198 | |||
| 198 | #endif /* __PERF_CALLCHAIN_H */ | 199 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index 51c10ab257f8..71c9c39340d4 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h | |||
| @@ -12,6 +12,10 @@ struct comm { | |||
| 12 | u64 start; | 12 | u64 start; |
| 13 | struct list_head list; | 13 | struct list_head list; |
| 14 | bool exec; | 14 | bool exec; |
| 15 | union { /* Tool specific area */ | ||
| 16 | void *priv; | ||
| 17 | u64 db_id; | ||
| 18 | }; | ||
| 15 | }; | 19 | }; |
| 16 | 20 | ||
| 17 | void comm__free(struct comm *comm); | 21 | void comm__free(struct comm *comm); |
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c new file mode 100644 index 000000000000..c81dae399763 --- /dev/null +++ b/tools/perf/util/db-export.c | |||
| @@ -0,0 +1,428 @@ | |||
| 1 | /* | ||
| 2 | * db-export.c: Support for exporting data suitable for import to a database | ||
| 3 | * Copyright (c) 2014, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <errno.h> | ||
| 17 | |||
| 18 | #include "evsel.h" | ||
| 19 | #include "machine.h" | ||
| 20 | #include "thread.h" | ||
| 21 | #include "comm.h" | ||
| 22 | #include "symbol.h" | ||
| 23 | #include "event.h" | ||
| 24 | #include "util.h" | ||
| 25 | #include "thread-stack.h" | ||
| 26 | #include "db-export.h" | ||
| 27 | |||
| 28 | struct deferred_export { | ||
| 29 | struct list_head node; | ||
| 30 | struct comm *comm; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static int db_export__deferred(struct db_export *dbe) | ||
| 34 | { | ||
| 35 | struct deferred_export *de; | ||
| 36 | int err; | ||
| 37 | |||
| 38 | while (!list_empty(&dbe->deferred)) { | ||
| 39 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
| 40 | node); | ||
| 41 | err = dbe->export_comm(dbe, de->comm); | ||
| 42 | list_del(&de->node); | ||
| 43 | free(de); | ||
| 44 | if (err) | ||
| 45 | return err; | ||
| 46 | } | ||
| 47 | |||
| 48 | return 0; | ||
| 49 | } | ||
| 50 | |||
| 51 | static void db_export__free_deferred(struct db_export *dbe) | ||
| 52 | { | ||
| 53 | struct deferred_export *de; | ||
| 54 | |||
| 55 | while (!list_empty(&dbe->deferred)) { | ||
| 56 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
| 57 | node); | ||
| 58 | list_del(&de->node); | ||
| 59 | free(de); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | static int db_export__defer_comm(struct db_export *dbe, struct comm *comm) | ||
| 64 | { | ||
| 65 | struct deferred_export *de; | ||
| 66 | |||
| 67 | de = zalloc(sizeof(struct deferred_export)); | ||
| 68 | if (!de) | ||
| 69 | return -ENOMEM; | ||
| 70 | |||
| 71 | de->comm = comm; | ||
| 72 | list_add_tail(&de->node, &dbe->deferred); | ||
| 73 | |||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | int db_export__init(struct db_export *dbe) | ||
| 78 | { | ||
| 79 | memset(dbe, 0, sizeof(struct db_export)); | ||
| 80 | INIT_LIST_HEAD(&dbe->deferred); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | int db_export__flush(struct db_export *dbe) | ||
| 85 | { | ||
| 86 | return db_export__deferred(dbe); | ||
| 87 | } | ||
| 88 | |||
| 89 | void db_export__exit(struct db_export *dbe) | ||
| 90 | { | ||
| 91 | db_export__free_deferred(dbe); | ||
| 92 | call_return_processor__free(dbe->crp); | ||
| 93 | dbe->crp = NULL; | ||
| 94 | } | ||
| 95 | |||
| 96 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel) | ||
| 97 | { | ||
| 98 | if (evsel->db_id) | ||
| 99 | return 0; | ||
| 100 | |||
| 101 | evsel->db_id = ++dbe->evsel_last_db_id; | ||
| 102 | |||
| 103 | if (dbe->export_evsel) | ||
| 104 | return dbe->export_evsel(dbe, evsel); | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | int db_export__machine(struct db_export *dbe, struct machine *machine) | ||
| 110 | { | ||
| 111 | if (machine->db_id) | ||
| 112 | return 0; | ||
| 113 | |||
| 114 | machine->db_id = ++dbe->machine_last_db_id; | ||
| 115 | |||
| 116 | if (dbe->export_machine) | ||
| 117 | return dbe->export_machine(dbe, machine); | ||
| 118 | |||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | int db_export__thread(struct db_export *dbe, struct thread *thread, | ||
| 123 | struct machine *machine, struct comm *comm) | ||
| 124 | { | ||
| 125 | u64 main_thread_db_id = 0; | ||
| 126 | int err; | ||
| 127 | |||
| 128 | if (thread->db_id) | ||
| 129 | return 0; | ||
| 130 | |||
| 131 | thread->db_id = ++dbe->thread_last_db_id; | ||
| 132 | |||
| 133 | if (thread->pid_ != -1) { | ||
| 134 | struct thread *main_thread; | ||
| 135 | |||
| 136 | if (thread->pid_ == thread->tid) { | ||
| 137 | main_thread = thread; | ||
| 138 | } else { | ||
| 139 | main_thread = machine__findnew_thread(machine, | ||
| 140 | thread->pid_, | ||
| 141 | thread->pid_); | ||
| 142 | if (!main_thread) | ||
| 143 | return -ENOMEM; | ||
| 144 | err = db_export__thread(dbe, main_thread, machine, | ||
| 145 | comm); | ||
| 146 | if (err) | ||
| 147 | return err; | ||
| 148 | if (comm) { | ||
| 149 | err = db_export__comm_thread(dbe, comm, thread); | ||
| 150 | if (err) | ||
| 151 | return err; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | main_thread_db_id = main_thread->db_id; | ||
| 155 | } | ||
| 156 | |||
| 157 | if (dbe->export_thread) | ||
| 158 | return dbe->export_thread(dbe, thread, main_thread_db_id, | ||
| 159 | machine); | ||
| 160 | |||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | int db_export__comm(struct db_export *dbe, struct comm *comm, | ||
| 165 | struct thread *main_thread) | ||
| 166 | { | ||
| 167 | int err; | ||
| 168 | |||
| 169 | if (comm->db_id) | ||
| 170 | return 0; | ||
| 171 | |||
| 172 | comm->db_id = ++dbe->comm_last_db_id; | ||
| 173 | |||
| 174 | if (dbe->export_comm) { | ||
| 175 | if (main_thread->comm_set) | ||
| 176 | err = dbe->export_comm(dbe, comm); | ||
| 177 | else | ||
| 178 | err = db_export__defer_comm(dbe, comm); | ||
| 179 | if (err) | ||
| 180 | return err; | ||
| 181 | } | ||
| 182 | |||
| 183 | return db_export__comm_thread(dbe, comm, main_thread); | ||
| 184 | } | ||
| 185 | |||
| 186 | int db_export__comm_thread(struct db_export *dbe, struct comm *comm, | ||
| 187 | struct thread *thread) | ||
| 188 | { | ||
| 189 | u64 db_id; | ||
| 190 | |||
| 191 | db_id = ++dbe->comm_thread_last_db_id; | ||
| 192 | |||
| 193 | if (dbe->export_comm_thread) | ||
| 194 | return dbe->export_comm_thread(dbe, db_id, comm, thread); | ||
| 195 | |||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | int db_export__dso(struct db_export *dbe, struct dso *dso, | ||
| 200 | struct machine *machine) | ||
| 201 | { | ||
| 202 | if (dso->db_id) | ||
| 203 | return 0; | ||
| 204 | |||
| 205 | dso->db_id = ++dbe->dso_last_db_id; | ||
| 206 | |||
| 207 | if (dbe->export_dso) | ||
| 208 | return dbe->export_dso(dbe, dso, machine); | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, | ||
| 214 | struct dso *dso) | ||
| 215 | { | ||
| 216 | u64 *sym_db_id = symbol__priv(sym); | ||
| 217 | |||
| 218 | if (*sym_db_id) | ||
| 219 | return 0; | ||
| 220 | |||
| 221 | *sym_db_id = ++dbe->symbol_last_db_id; | ||
| 222 | |||
| 223 | if (dbe->export_symbol) | ||
| 224 | return dbe->export_symbol(dbe, sym, dso); | ||
| 225 | |||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | static struct thread *get_main_thread(struct machine *machine, struct thread *thread) | ||
| 230 | { | ||
| 231 | if (thread->pid_ == thread->tid) | ||
| 232 | return thread; | ||
| 233 | |||
| 234 | if (thread->pid_ == -1) | ||
| 235 | return NULL; | ||
| 236 | |||
| 237 | return machine__find_thread(machine, thread->pid_, thread->pid_); | ||
| 238 | } | ||
| 239 | |||
| 240 | static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, | ||
| 241 | u64 *dso_db_id, u64 *sym_db_id, u64 *offset) | ||
| 242 | { | ||
| 243 | int err; | ||
| 244 | |||
| 245 | if (al->map) { | ||
| 246 | struct dso *dso = al->map->dso; | ||
| 247 | |||
| 248 | err = db_export__dso(dbe, dso, al->machine); | ||
| 249 | if (err) | ||
| 250 | return err; | ||
| 251 | *dso_db_id = dso->db_id; | ||
| 252 | |||
| 253 | if (!al->sym) { | ||
| 254 | al->sym = symbol__new(al->addr, 0, 0, "unknown"); | ||
| 255 | if (al->sym) | ||
| 256 | symbols__insert(&dso->symbols[al->map->type], | ||
| 257 | al->sym); | ||
| 258 | } | ||
| 259 | |||
| 260 | if (al->sym) { | ||
| 261 | u64 *db_id = symbol__priv(al->sym); | ||
| 262 | |||
| 263 | err = db_export__symbol(dbe, al->sym, dso); | ||
| 264 | if (err) | ||
| 265 | return err; | ||
| 266 | *sym_db_id = *db_id; | ||
| 267 | *offset = al->addr - al->sym->start; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
| 275 | const char *name) | ||
| 276 | { | ||
| 277 | if (dbe->export_branch_type) | ||
| 278 | return dbe->export_branch_type(dbe, branch_type, name); | ||
| 279 | |||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 283 | int db_export__sample(struct db_export *dbe, union perf_event *event, | ||
| 284 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
| 285 | struct thread *thread, struct addr_location *al) | ||
| 286 | { | ||
| 287 | struct export_sample es = { | ||
| 288 | .event = event, | ||
| 289 | .sample = sample, | ||
| 290 | .evsel = evsel, | ||
| 291 | .thread = thread, | ||
| 292 | .al = al, | ||
| 293 | }; | ||
| 294 | struct thread *main_thread; | ||
| 295 | struct comm *comm = NULL; | ||
| 296 | int err; | ||
| 297 | |||
| 298 | err = db_export__evsel(dbe, evsel); | ||
| 299 | if (err) | ||
| 300 | return err; | ||
| 301 | |||
| 302 | err = db_export__machine(dbe, al->machine); | ||
| 303 | if (err) | ||
| 304 | return err; | ||
| 305 | |||
| 306 | main_thread = get_main_thread(al->machine, thread); | ||
| 307 | if (main_thread) | ||
| 308 | comm = machine__thread_exec_comm(al->machine, main_thread); | ||
| 309 | |||
| 310 | err = db_export__thread(dbe, thread, al->machine, comm); | ||
| 311 | if (err) | ||
| 312 | return err; | ||
| 313 | |||
| 314 | if (comm) { | ||
| 315 | err = db_export__comm(dbe, comm, main_thread); | ||
| 316 | if (err) | ||
| 317 | return err; | ||
| 318 | es.comm_db_id = comm->db_id; | ||
| 319 | } | ||
| 320 | |||
| 321 | es.db_id = ++dbe->sample_last_db_id; | ||
| 322 | |||
| 323 | err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset); | ||
| 324 | if (err) | ||
| 325 | return err; | ||
| 326 | |||
| 327 | if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | ||
| 328 | sample_addr_correlates_sym(&evsel->attr)) { | ||
| 329 | struct addr_location addr_al; | ||
| 330 | |||
| 331 | perf_event__preprocess_sample_addr(event, sample, thread, &addr_al); | ||
| 332 | err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id, | ||
| 333 | &es.addr_sym_db_id, &es.addr_offset); | ||
| 334 | if (err) | ||
| 335 | return err; | ||
| 336 | if (dbe->crp) { | ||
| 337 | err = thread_stack__process(thread, comm, sample, al, | ||
| 338 | &addr_al, es.db_id, | ||
| 339 | dbe->crp); | ||
| 340 | if (err) | ||
| 341 | return err; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | if (dbe->export_sample) | ||
| 346 | return dbe->export_sample(dbe, &es); | ||
| 347 | |||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | static struct { | ||
| 352 | u32 branch_type; | ||
| 353 | const char *name; | ||
| 354 | } branch_types[] = { | ||
| 355 | {0, "no branch"}, | ||
| 356 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"}, | ||
| 357 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"}, | ||
| 358 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"}, | ||
| 359 | {PERF_IP_FLAG_BRANCH, "unconditional jump"}, | ||
| 360 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, | ||
| 361 | "software interrupt"}, | ||
| 362 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, | ||
| 363 | "return from interrupt"}, | ||
| 364 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, | ||
| 365 | "system call"}, | ||
| 366 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, | ||
| 367 | "return from system call"}, | ||
| 368 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"}, | ||
| 369 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | | ||
| 370 | PERF_IP_FLAG_INTERRUPT, "hardware interrupt"}, | ||
| 371 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"}, | ||
| 372 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"}, | ||
| 373 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"}, | ||
| 374 | {0, NULL} | ||
| 375 | }; | ||
| 376 | |||
| 377 | int db_export__branch_types(struct db_export *dbe) | ||
| 378 | { | ||
| 379 | int i, err = 0; | ||
| 380 | |||
| 381 | for (i = 0; branch_types[i].name ; i++) { | ||
| 382 | err = db_export__branch_type(dbe, branch_types[i].branch_type, | ||
| 383 | branch_types[i].name); | ||
| 384 | if (err) | ||
| 385 | break; | ||
| 386 | } | ||
| 387 | return err; | ||
| 388 | } | ||
| 389 | |||
| 390 | int db_export__call_path(struct db_export *dbe, struct call_path *cp) | ||
| 391 | { | ||
| 392 | int err; | ||
| 393 | |||
| 394 | if (cp->db_id) | ||
| 395 | return 0; | ||
| 396 | |||
| 397 | if (cp->parent) { | ||
| 398 | err = db_export__call_path(dbe, cp->parent); | ||
| 399 | if (err) | ||
| 400 | return err; | ||
| 401 | } | ||
| 402 | |||
| 403 | cp->db_id = ++dbe->call_path_last_db_id; | ||
| 404 | |||
| 405 | if (dbe->export_call_path) | ||
| 406 | return dbe->export_call_path(dbe, cp); | ||
| 407 | |||
| 408 | return 0; | ||
| 409 | } | ||
| 410 | |||
| 411 | int db_export__call_return(struct db_export *dbe, struct call_return *cr) | ||
| 412 | { | ||
| 413 | int err; | ||
| 414 | |||
| 415 | if (cr->db_id) | ||
| 416 | return 0; | ||
| 417 | |||
| 418 | err = db_export__call_path(dbe, cr->cp); | ||
| 419 | if (err) | ||
| 420 | return err; | ||
| 421 | |||
| 422 | cr->db_id = ++dbe->call_return_last_db_id; | ||
| 423 | |||
| 424 | if (dbe->export_call_return) | ||
| 425 | return dbe->export_call_return(dbe, cr); | ||
| 426 | |||
| 427 | return 0; | ||
| 428 | } | ||
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h new file mode 100644 index 000000000000..adbd22d66798 --- /dev/null +++ b/tools/perf/util/db-export.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | /* | ||
| 2 | * db-export.h: Support for exporting data suitable for import to a database | ||
| 3 | * Copyright (c) 2014, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef __PERF_DB_EXPORT_H | ||
| 17 | #define __PERF_DB_EXPORT_H | ||
| 18 | |||
| 19 | #include <linux/types.h> | ||
| 20 | #include <linux/list.h> | ||
| 21 | |||
| 22 | struct perf_evsel; | ||
| 23 | struct machine; | ||
| 24 | struct thread; | ||
| 25 | struct comm; | ||
| 26 | struct dso; | ||
| 27 | struct perf_sample; | ||
| 28 | struct addr_location; | ||
| 29 | struct call_return_processor; | ||
| 30 | struct call_path; | ||
| 31 | struct call_return; | ||
| 32 | |||
| 33 | struct export_sample { | ||
| 34 | union perf_event *event; | ||
| 35 | struct perf_sample *sample; | ||
| 36 | struct perf_evsel *evsel; | ||
| 37 | struct thread *thread; | ||
| 38 | struct addr_location *al; | ||
| 39 | u64 db_id; | ||
| 40 | u64 comm_db_id; | ||
| 41 | u64 dso_db_id; | ||
| 42 | u64 sym_db_id; | ||
| 43 | u64 offset; /* ip offset from symbol start */ | ||
| 44 | u64 addr_dso_db_id; | ||
| 45 | u64 addr_sym_db_id; | ||
| 46 | u64 addr_offset; /* addr offset from symbol start */ | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct db_export { | ||
| 50 | int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel); | ||
| 51 | int (*export_machine)(struct db_export *dbe, struct machine *machine); | ||
| 52 | int (*export_thread)(struct db_export *dbe, struct thread *thread, | ||
| 53 | u64 main_thread_db_id, struct machine *machine); | ||
| 54 | int (*export_comm)(struct db_export *dbe, struct comm *comm); | ||
| 55 | int (*export_comm_thread)(struct db_export *dbe, u64 db_id, | ||
| 56 | struct comm *comm, struct thread *thread); | ||
| 57 | int (*export_dso)(struct db_export *dbe, struct dso *dso, | ||
| 58 | struct machine *machine); | ||
| 59 | int (*export_symbol)(struct db_export *dbe, struct symbol *sym, | ||
| 60 | struct dso *dso); | ||
| 61 | int (*export_branch_type)(struct db_export *dbe, u32 branch_type, | ||
| 62 | const char *name); | ||
| 63 | int (*export_sample)(struct db_export *dbe, struct export_sample *es); | ||
| 64 | int (*export_call_path)(struct db_export *dbe, struct call_path *cp); | ||
| 65 | int (*export_call_return)(struct db_export *dbe, | ||
| 66 | struct call_return *cr); | ||
| 67 | struct call_return_processor *crp; | ||
| 68 | u64 evsel_last_db_id; | ||
| 69 | u64 machine_last_db_id; | ||
| 70 | u64 thread_last_db_id; | ||
| 71 | u64 comm_last_db_id; | ||
| 72 | u64 comm_thread_last_db_id; | ||
| 73 | u64 dso_last_db_id; | ||
| 74 | u64 symbol_last_db_id; | ||
| 75 | u64 sample_last_db_id; | ||
| 76 | u64 call_path_last_db_id; | ||
| 77 | u64 call_return_last_db_id; | ||
| 78 | struct list_head deferred; | ||
| 79 | }; | ||
| 80 | |||
| 81 | int db_export__init(struct db_export *dbe); | ||
| 82 | int db_export__flush(struct db_export *dbe); | ||
| 83 | void db_export__exit(struct db_export *dbe); | ||
| 84 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel); | ||
| 85 | int db_export__machine(struct db_export *dbe, struct machine *machine); | ||
| 86 | int db_export__thread(struct db_export *dbe, struct thread *thread, | ||
| 87 | struct machine *machine, struct comm *comm); | ||
| 88 | int db_export__comm(struct db_export *dbe, struct comm *comm, | ||
| 89 | struct thread *main_thread); | ||
| 90 | int db_export__comm_thread(struct db_export *dbe, struct comm *comm, | ||
| 91 | struct thread *thread); | ||
| 92 | int db_export__dso(struct db_export *dbe, struct dso *dso, | ||
| 93 | struct machine *machine); | ||
| 94 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, | ||
| 95 | struct dso *dso); | ||
| 96 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
| 97 | const char *name); | ||
| 98 | int db_export__sample(struct db_export *dbe, union perf_event *event, | ||
| 99 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
| 100 | struct thread *thread, struct addr_location *al); | ||
| 101 | |||
| 102 | int db_export__branch_types(struct db_export *dbe); | ||
| 103 | |||
| 104 | int db_export__call_path(struct db_export *dbe, struct call_path *cp); | ||
| 105 | int db_export__call_return(struct db_export *dbe, struct call_return *cr); | ||
| 106 | |||
| 107 | #endif | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 0247acfdfaca..45be944d450a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
| @@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso) | |||
| 21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | 21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', |
| 22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | 22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', |
| 23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | 23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', |
| 24 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm', | ||
| 24 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | 25 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', |
| 25 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | 26 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', |
| 27 | [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M', | ||
| 26 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | 28 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', |
| 27 | }; | 29 | }; |
| 28 | 30 | ||
| @@ -112,11 +114,13 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 112 | break; | 114 | break; |
| 113 | 115 | ||
| 114 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 116 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
| 117 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | ||
| 115 | path__join3(filename, size, symbol_conf.symfs, | 118 | path__join3(filename, size, symbol_conf.symfs, |
| 116 | root_dir, dso->long_name); | 119 | root_dir, dso->long_name); |
| 117 | break; | 120 | break; |
| 118 | 121 | ||
| 119 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 122 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
| 123 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | ||
| 120 | __symbol__join_symfs(filename, size, dso->long_name); | 124 | __symbol__join_symfs(filename, size, dso->long_name); |
| 121 | break; | 125 | break; |
| 122 | 126 | ||
| @@ -137,6 +141,73 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 137 | return ret; | 141 | return ret; |
| 138 | } | 142 | } |
| 139 | 143 | ||
| 144 | static const struct { | ||
| 145 | const char *fmt; | ||
| 146 | int (*decompress)(const char *input, int output); | ||
| 147 | } compressions[] = { | ||
| 148 | #ifdef HAVE_ZLIB_SUPPORT | ||
| 149 | { "gz", gzip_decompress_to_file }, | ||
| 150 | #endif | ||
| 151 | { NULL, NULL }, | ||
| 152 | }; | ||
| 153 | |||
| 154 | bool is_supported_compression(const char *ext) | ||
| 155 | { | ||
| 156 | unsigned i; | ||
| 157 | |||
| 158 | for (i = 0; compressions[i].fmt; i++) { | ||
| 159 | if (!strcmp(ext, compressions[i].fmt)) | ||
| 160 | return true; | ||
| 161 | } | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | |||
| 165 | bool is_kmodule_extension(const char *ext) | ||
| 166 | { | ||
| 167 | if (strncmp(ext, "ko", 2)) | ||
| 168 | return false; | ||
| 169 | |||
| 170 | if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3))) | ||
| 171 | return true; | ||
| 172 | |||
| 173 | return false; | ||
| 174 | } | ||
| 175 | |||
| 176 | bool is_kernel_module(const char *pathname, bool *compressed) | ||
| 177 | { | ||
| 178 | const char *ext = strrchr(pathname, '.'); | ||
| 179 | |||
| 180 | if (ext == NULL) | ||
| 181 | return false; | ||
| 182 | |||
| 183 | if (is_supported_compression(ext + 1)) { | ||
| 184 | if (compressed) | ||
| 185 | *compressed = true; | ||
| 186 | ext -= 3; | ||
| 187 | } else if (compressed) | ||
| 188 | *compressed = false; | ||
| 189 | |||
| 190 | return is_kmodule_extension(ext + 1); | ||
| 191 | } | ||
| 192 | |||
| 193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd) | ||
| 194 | { | ||
| 195 | unsigned i; | ||
| 196 | |||
| 197 | for (i = 0; compressions[i].fmt; i++) { | ||
| 198 | if (!strcmp(ext, compressions[i].fmt)) | ||
| 199 | return !compressions[i].decompress(filename, | ||
| 200 | output_fd); | ||
| 201 | } | ||
| 202 | return false; | ||
| 203 | } | ||
| 204 | |||
| 205 | bool dso__needs_decompress(struct dso *dso) | ||
| 206 | { | ||
| 207 | return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || | ||
| 208 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
| 209 | } | ||
| 210 | |||
| 140 | /* | 211 | /* |
| 141 | * Global list of open DSOs and the counter. | 212 | * Global list of open DSOs and the counter. |
| 142 | */ | 213 | */ |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index acb651acc7fd..3782c82c6e44 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
| @@ -22,7 +22,9 @@ enum dso_binary_type { | |||
| 22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
| 23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
| 24 | DSO_BINARY_TYPE__GUEST_KMODULE, | 24 | DSO_BINARY_TYPE__GUEST_KMODULE, |
| 25 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
| 25 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 26 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
| 27 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
| 26 | DSO_BINARY_TYPE__KCORE, | 28 | DSO_BINARY_TYPE__KCORE, |
| 27 | DSO_BINARY_TYPE__GUEST_KCORE, | 29 | DSO_BINARY_TYPE__GUEST_KCORE, |
| 28 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 30 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
| @@ -127,6 +129,7 @@ struct dso { | |||
| 127 | const char *long_name; | 129 | const char *long_name; |
| 128 | u16 long_name_len; | 130 | u16 long_name_len; |
| 129 | u16 short_name_len; | 131 | u16 short_name_len; |
| 132 | void *dwfl; /* DWARF debug info */ | ||
| 130 | 133 | ||
| 131 | /* dso data file */ | 134 | /* dso data file */ |
| 132 | struct { | 135 | struct { |
| @@ -138,6 +141,11 @@ struct dso { | |||
| 138 | struct list_head open_entry; | 141 | struct list_head open_entry; |
| 139 | } data; | 142 | } data; |
| 140 | 143 | ||
| 144 | union { /* Tool specific area */ | ||
| 145 | void *priv; | ||
| 146 | u64 db_id; | ||
| 147 | }; | ||
| 148 | |||
| 141 | char name[0]; | 149 | char name[0]; |
| 142 | }; | 150 | }; |
| 143 | 151 | ||
| @@ -179,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); | |||
| 179 | char dso__symtab_origin(const struct dso *dso); | 187 | char dso__symtab_origin(const struct dso *dso); |
| 180 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 188 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
| 181 | char *root_dir, char *filename, size_t size); | 189 | char *root_dir, char *filename, size_t size); |
| 190 | bool is_supported_compression(const char *ext); | ||
| 191 | bool is_kmodule_extension(const char *ext); | ||
| 192 | bool is_kernel_module(const char *pathname, bool *compressed); | ||
| 193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); | ||
| 194 | bool dso__needs_decompress(struct dso *dso); | ||
| 182 | 195 | ||
| 183 | /* | 196 | /* |
| 184 | * The dso__data_* external interface provides following functions: | 197 | * The dso__data_* external interface provides following functions: |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 4af6b279e34a..6c6d044e959a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -28,6 +28,7 @@ static const char *perf_event__names[] = { | |||
| 28 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 28 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
| 29 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | 29 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", |
| 30 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 30 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
| 31 | [PERF_RECORD_ID_INDEX] = "ID_INDEX", | ||
| 31 | }; | 32 | }; |
| 32 | 33 | ||
| 33 | const char *perf_event__name(unsigned int id) | 34 | const char *perf_event__name(unsigned int id) |
| @@ -730,12 +731,12 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, | |||
| 730 | return machine__process_event(machine, event, sample); | 731 | return machine__process_event(machine, event, sample); |
| 731 | } | 732 | } |
| 732 | 733 | ||
| 733 | void thread__find_addr_map(struct thread *thread, | 734 | void thread__find_addr_map(struct thread *thread, u8 cpumode, |
| 734 | struct machine *machine, u8 cpumode, | ||
| 735 | enum map_type type, u64 addr, | 735 | enum map_type type, u64 addr, |
| 736 | struct addr_location *al) | 736 | struct addr_location *al) |
| 737 | { | 737 | { |
| 738 | struct map_groups *mg = thread->mg; | 738 | struct map_groups *mg = thread->mg; |
| 739 | struct machine *machine = mg->machine; | ||
| 739 | bool load_map = false; | 740 | bool load_map = false; |
| 740 | 741 | ||
| 741 | al->machine = machine; | 742 | al->machine = machine; |
| @@ -806,14 +807,14 @@ try_again: | |||
| 806 | } | 807 | } |
| 807 | } | 808 | } |
| 808 | 809 | ||
| 809 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 810 | void thread__find_addr_location(struct thread *thread, |
| 810 | u8 cpumode, enum map_type type, u64 addr, | 811 | u8 cpumode, enum map_type type, u64 addr, |
| 811 | struct addr_location *al) | 812 | struct addr_location *al) |
| 812 | { | 813 | { |
| 813 | thread__find_addr_map(thread, machine, cpumode, type, addr, al); | 814 | thread__find_addr_map(thread, cpumode, type, addr, al); |
| 814 | if (al->map != NULL) | 815 | if (al->map != NULL) |
| 815 | al->sym = map__find_symbol(al->map, al->addr, | 816 | al->sym = map__find_symbol(al->map, al->addr, |
| 816 | machine->symbol_filter); | 817 | thread->mg->machine->symbol_filter); |
| 817 | else | 818 | else |
| 818 | al->sym = NULL; | 819 | al->sym = NULL; |
| 819 | } | 820 | } |
| @@ -842,8 +843,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
| 842 | machine->vmlinux_maps[MAP__FUNCTION] == NULL) | 843 | machine->vmlinux_maps[MAP__FUNCTION] == NULL) |
| 843 | machine__create_kernel_maps(machine); | 844 | machine__create_kernel_maps(machine); |
| 844 | 845 | ||
| 845 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 846 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al); |
| 846 | sample->ip, al); | ||
| 847 | dump_printf(" ...... dso: %s\n", | 847 | dump_printf(" ...... dso: %s\n", |
| 848 | al->map ? al->map->dso->long_name : | 848 | al->map ? al->map->dso->long_name : |
| 849 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 849 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
| @@ -902,16 +902,14 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr) | |||
| 902 | 902 | ||
| 903 | void perf_event__preprocess_sample_addr(union perf_event *event, | 903 | void perf_event__preprocess_sample_addr(union perf_event *event, |
| 904 | struct perf_sample *sample, | 904 | struct perf_sample *sample, |
| 905 | struct machine *machine, | ||
| 906 | struct thread *thread, | 905 | struct thread *thread, |
| 907 | struct addr_location *al) | 906 | struct addr_location *al) |
| 908 | { | 907 | { |
| 909 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 908 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
| 910 | 909 | ||
| 911 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 910 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al); |
| 912 | sample->addr, al); | ||
| 913 | if (!al->map) | 911 | if (!al->map) |
| 914 | thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE, | 912 | thread__find_addr_map(thread, cpumode, MAP__VARIABLE, |
| 915 | sample->addr, al); | 913 | sample->addr, al); |
| 916 | 914 | ||
| 917 | al->cpu = sample->cpu; | 915 | al->cpu = sample->cpu; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 5699e7e2a790..09b9e8d3fcf7 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -143,6 +143,32 @@ struct branch_stack { | |||
| 143 | struct branch_entry entries[0]; | 143 | struct branch_entry entries[0]; |
| 144 | }; | 144 | }; |
| 145 | 145 | ||
| 146 | enum { | ||
| 147 | PERF_IP_FLAG_BRANCH = 1ULL << 0, | ||
| 148 | PERF_IP_FLAG_CALL = 1ULL << 1, | ||
| 149 | PERF_IP_FLAG_RETURN = 1ULL << 2, | ||
| 150 | PERF_IP_FLAG_CONDITIONAL = 1ULL << 3, | ||
| 151 | PERF_IP_FLAG_SYSCALLRET = 1ULL << 4, | ||
| 152 | PERF_IP_FLAG_ASYNC = 1ULL << 5, | ||
| 153 | PERF_IP_FLAG_INTERRUPT = 1ULL << 6, | ||
| 154 | PERF_IP_FLAG_TX_ABORT = 1ULL << 7, | ||
| 155 | PERF_IP_FLAG_TRACE_BEGIN = 1ULL << 8, | ||
| 156 | PERF_IP_FLAG_TRACE_END = 1ULL << 9, | ||
| 157 | PERF_IP_FLAG_IN_TX = 1ULL << 10, | ||
| 158 | }; | ||
| 159 | |||
| 160 | #define PERF_BRANCH_MASK (\ | ||
| 161 | PERF_IP_FLAG_BRANCH |\ | ||
| 162 | PERF_IP_FLAG_CALL |\ | ||
| 163 | PERF_IP_FLAG_RETURN |\ | ||
| 164 | PERF_IP_FLAG_CONDITIONAL |\ | ||
| 165 | PERF_IP_FLAG_SYSCALLRET |\ | ||
| 166 | PERF_IP_FLAG_ASYNC |\ | ||
| 167 | PERF_IP_FLAG_INTERRUPT |\ | ||
| 168 | PERF_IP_FLAG_TX_ABORT |\ | ||
| 169 | PERF_IP_FLAG_TRACE_BEGIN |\ | ||
| 170 | PERF_IP_FLAG_TRACE_END) | ||
| 171 | |||
| 146 | struct perf_sample { | 172 | struct perf_sample { |
| 147 | u64 ip; | 173 | u64 ip; |
| 148 | u32 pid, tid; | 174 | u32 pid, tid; |
| @@ -162,6 +188,7 @@ struct perf_sample { | |||
| 162 | struct ip_callchain *callchain; | 188 | struct ip_callchain *callchain; |
| 163 | struct branch_stack *branch_stack; | 189 | struct branch_stack *branch_stack; |
| 164 | struct regs_dump user_regs; | 190 | struct regs_dump user_regs; |
| 191 | struct regs_dump intr_regs; | ||
| 165 | struct stack_dump user_stack; | 192 | struct stack_dump user_stack; |
| 166 | struct sample_read read; | 193 | struct sample_read read; |
| 167 | }; | 194 | }; |
| @@ -187,6 +214,7 @@ enum perf_user_event_type { /* above any possible kernel type */ | |||
| 187 | PERF_RECORD_HEADER_TRACING_DATA = 66, | 214 | PERF_RECORD_HEADER_TRACING_DATA = 66, |
| 188 | PERF_RECORD_HEADER_BUILD_ID = 67, | 215 | PERF_RECORD_HEADER_BUILD_ID = 67, |
| 189 | PERF_RECORD_FINISHED_ROUND = 68, | 216 | PERF_RECORD_FINISHED_ROUND = 68, |
| 217 | PERF_RECORD_ID_INDEX = 69, | ||
| 190 | PERF_RECORD_HEADER_MAX | 218 | PERF_RECORD_HEADER_MAX |
| 191 | }; | 219 | }; |
| 192 | 220 | ||
| @@ -239,6 +267,19 @@ struct tracing_data_event { | |||
| 239 | u32 size; | 267 | u32 size; |
| 240 | }; | 268 | }; |
| 241 | 269 | ||
| 270 | struct id_index_entry { | ||
| 271 | u64 id; | ||
| 272 | u64 idx; | ||
| 273 | u64 cpu; | ||
| 274 | u64 tid; | ||
| 275 | }; | ||
| 276 | |||
| 277 | struct id_index_event { | ||
| 278 | struct perf_event_header header; | ||
| 279 | u64 nr; | ||
| 280 | struct id_index_entry entries[0]; | ||
| 281 | }; | ||
| 282 | |||
| 242 | union perf_event { | 283 | union perf_event { |
| 243 | struct perf_event_header header; | 284 | struct perf_event_header header; |
| 244 | struct mmap_event mmap; | 285 | struct mmap_event mmap; |
| @@ -253,6 +294,7 @@ union perf_event { | |||
| 253 | struct event_type_event event_type; | 294 | struct event_type_event event_type; |
| 254 | struct tracing_data_event tracing_data; | 295 | struct tracing_data_event tracing_data; |
| 255 | struct build_id_event build_id; | 296 | struct build_id_event build_id; |
| 297 | struct id_index_event id_index; | ||
| 256 | }; | 298 | }; |
| 257 | 299 | ||
| 258 | void perf_event__print_totals(void); | 300 | void perf_event__print_totals(void); |
| @@ -322,7 +364,6 @@ bool is_bts_event(struct perf_event_attr *attr); | |||
| 322 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); | 364 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); |
| 323 | void perf_event__preprocess_sample_addr(union perf_event *event, | 365 | void perf_event__preprocess_sample_addr(union perf_event *event, |
| 324 | struct perf_sample *sample, | 366 | struct perf_sample *sample, |
| 325 | struct machine *machine, | ||
| 326 | struct thread *thread, | 367 | struct thread *thread, |
| 327 | struct addr_location *al); | 368 | struct addr_location *al); |
| 328 | 369 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3c9e77d6b4c2..cfbe2b99b9aa 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -413,7 +413,7 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | |||
| 413 | int nfds = 0; | 413 | int nfds = 0; |
| 414 | struct perf_evsel *evsel; | 414 | struct perf_evsel *evsel; |
| 415 | 415 | ||
| 416 | list_for_each_entry(evsel, &evlist->entries, node) { | 416 | evlist__for_each(evlist, evsel) { |
| 417 | if (evsel->system_wide) | 417 | if (evsel->system_wide) |
| 418 | nfds += nr_cpus; | 418 | nfds += nr_cpus; |
| 419 | else | 419 | else |
| @@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
| 527 | return 0; | 527 | return 0; |
| 528 | } | 528 | } |
| 529 | 529 | ||
| 530 | static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, | ||
| 531 | struct perf_evsel *evsel, int idx, int cpu, | ||
| 532 | int thread) | ||
| 533 | { | ||
| 534 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | ||
| 535 | sid->idx = idx; | ||
| 536 | if (evlist->cpus && cpu >= 0) | ||
| 537 | sid->cpu = evlist->cpus->map[cpu]; | ||
| 538 | else | ||
| 539 | sid->cpu = -1; | ||
| 540 | if (!evsel->system_wide && evlist->threads && thread >= 0) | ||
| 541 | sid->tid = evlist->threads->map[thread]; | ||
| 542 | else | ||
| 543 | sid->tid = -1; | ||
| 544 | } | ||
| 545 | |||
| 530 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) | 546 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) |
| 531 | { | 547 | { |
| 532 | struct hlist_head *head; | 548 | struct hlist_head *head; |
| @@ -800,14 +816,26 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
| 800 | perf_evlist__mmap_get(evlist, idx); | 816 | perf_evlist__mmap_get(evlist, idx); |
| 801 | } | 817 | } |
| 802 | 818 | ||
| 803 | if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | 819 | /* |
| 820 | * The system_wide flag causes a selected event to be opened | ||
| 821 | * always without a pid. Consequently it will never get a | ||
| 822 | * POLLHUP, but it is used for tracking in combination with | ||
| 823 | * other events, so it should not need to be polled anyway. | ||
| 824 | * Therefore don't add it for polling. | ||
| 825 | */ | ||
| 826 | if (!evsel->system_wide && | ||
| 827 | __perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | ||
| 804 | perf_evlist__mmap_put(evlist, idx); | 828 | perf_evlist__mmap_put(evlist, idx); |
| 805 | return -1; | 829 | return -1; |
| 806 | } | 830 | } |
| 807 | 831 | ||
| 808 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 832 | if (evsel->attr.read_format & PERF_FORMAT_ID) { |
| 809 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | 833 | if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, |
| 810 | return -1; | 834 | fd) < 0) |
| 835 | return -1; | ||
| 836 | perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, | ||
| 837 | thread); | ||
| 838 | } | ||
| 811 | } | 839 | } |
| 812 | 840 | ||
| 813 | return 0; | 841 | return 0; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2f9e68025ede..f2dc91fb87fa 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -658,9 +658,22 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 658 | attr->mmap_data = track; | 658 | attr->mmap_data = track; |
| 659 | } | 659 | } |
| 660 | 660 | ||
| 661 | /* | ||
| 662 | * We don't allow user space callchains for function trace | ||
| 663 | * event, due to issues with page faults while tracing page | ||
| 664 | * fault handler and its overall trickiness nature. | ||
| 665 | */ | ||
| 666 | if (perf_evsel__is_function_event(evsel)) | ||
| 667 | evsel->attr.exclude_callchain_user = 1; | ||
| 668 | |||
| 661 | if (callchain_param.enabled && !evsel->no_aux_samples) | 669 | if (callchain_param.enabled && !evsel->no_aux_samples) |
| 662 | perf_evsel__config_callgraph(evsel); | 670 | perf_evsel__config_callgraph(evsel); |
| 663 | 671 | ||
| 672 | if (opts->sample_intr_regs) { | ||
| 673 | attr->sample_regs_intr = PERF_REGS_MASK; | ||
| 674 | perf_evsel__set_sample_bit(evsel, REGS_INTR); | ||
| 675 | } | ||
| 676 | |||
| 664 | if (target__has_cpu(&opts->target)) | 677 | if (target__has_cpu(&opts->target)) |
| 665 | perf_evsel__set_sample_bit(evsel, CPU); | 678 | perf_evsel__set_sample_bit(evsel, CPU); |
| 666 | 679 | ||
| @@ -853,8 +866,6 @@ void perf_evsel__exit(struct perf_evsel *evsel) | |||
| 853 | perf_evsel__free_id(evsel); | 866 | perf_evsel__free_id(evsel); |
| 854 | close_cgroup(evsel->cgrp); | 867 | close_cgroup(evsel->cgrp); |
| 855 | zfree(&evsel->group_name); | 868 | zfree(&evsel->group_name); |
| 856 | if (evsel->tp_format) | ||
| 857 | pevent_free_format(evsel->tp_format); | ||
| 858 | zfree(&evsel->name); | 869 | zfree(&evsel->name); |
| 859 | perf_evsel__object.fini(evsel); | 870 | perf_evsel__object.fini(evsel); |
| 860 | } | 871 | } |
| @@ -1039,6 +1050,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
| 1039 | ret += PRINT_ATTR_X64(branch_sample_type); | 1050 | ret += PRINT_ATTR_X64(branch_sample_type); |
| 1040 | ret += PRINT_ATTR_X64(sample_regs_user); | 1051 | ret += PRINT_ATTR_X64(sample_regs_user); |
| 1041 | ret += PRINT_ATTR_U32(sample_stack_user); | 1052 | ret += PRINT_ATTR_U32(sample_stack_user); |
| 1053 | ret += PRINT_ATTR_X64(sample_regs_intr); | ||
| 1042 | 1054 | ||
| 1043 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | 1055 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); |
| 1044 | 1056 | ||
| @@ -1538,6 +1550,23 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
| 1538 | array++; | 1550 | array++; |
| 1539 | } | 1551 | } |
| 1540 | 1552 | ||
| 1553 | data->intr_regs.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
| 1554 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
| 1555 | OVERFLOW_CHECK_u64(array); | ||
| 1556 | data->intr_regs.abi = *array; | ||
| 1557 | array++; | ||
| 1558 | |||
| 1559 | if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) { | ||
| 1560 | u64 mask = evsel->attr.sample_regs_intr; | ||
| 1561 | |||
| 1562 | sz = hweight_long(mask) * sizeof(u64); | ||
| 1563 | OVERFLOW_CHECK(array, sz, max_size); | ||
| 1564 | data->intr_regs.mask = mask; | ||
| 1565 | data->intr_regs.regs = (u64 *)array; | ||
| 1566 | array = (void *)array + sz; | ||
| 1567 | } | ||
| 1568 | } | ||
| 1569 | |||
| 1541 | return 0; | 1570 | return 0; |
| 1542 | } | 1571 | } |
| 1543 | 1572 | ||
| @@ -1633,6 +1662,16 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | |||
| 1633 | if (type & PERF_SAMPLE_TRANSACTION) | 1662 | if (type & PERF_SAMPLE_TRANSACTION) |
| 1634 | result += sizeof(u64); | 1663 | result += sizeof(u64); |
| 1635 | 1664 | ||
| 1665 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
| 1666 | if (sample->intr_regs.abi) { | ||
| 1667 | result += sizeof(u64); | ||
| 1668 | sz = hweight_long(sample->intr_regs.mask) * sizeof(u64); | ||
| 1669 | result += sz; | ||
| 1670 | } else { | ||
| 1671 | result += sizeof(u64); | ||
| 1672 | } | ||
| 1673 | } | ||
| 1674 | |||
| 1636 | return result; | 1675 | return result; |
| 1637 | } | 1676 | } |
| 1638 | 1677 | ||
| @@ -1811,6 +1850,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
| 1811 | array++; | 1850 | array++; |
| 1812 | } | 1851 | } |
| 1813 | 1852 | ||
| 1853 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
| 1854 | if (sample->intr_regs.abi) { | ||
| 1855 | *array++ = sample->intr_regs.abi; | ||
| 1856 | sz = hweight_long(sample->intr_regs.mask) * sizeof(u64); | ||
| 1857 | memcpy(array, sample->intr_regs.regs, sz); | ||
| 1858 | array = (void *)array + sz; | ||
| 1859 | } else { | ||
| 1860 | *array++ = 0; | ||
| 1861 | } | ||
| 1862 | } | ||
| 1863 | |||
| 1814 | return 0; | 1864 | return 0; |
| 1815 | } | 1865 | } |
| 1816 | 1866 | ||
| @@ -1940,7 +1990,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | |||
| 1940 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | 1990 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), |
| 1941 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | 1991 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), |
| 1942 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | 1992 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), |
| 1943 | bit_name(IDENTIFIER), | 1993 | bit_name(IDENTIFIER), bit_name(REGS_INTR), |
| 1944 | { .name = NULL, } | 1994 | { .name = NULL, } |
| 1945 | }; | 1995 | }; |
| 1946 | #undef bit_name | 1996 | #undef bit_name |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 163c5604e5d1..979790951bfb 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -36,6 +36,9 @@ struct perf_sample_id { | |||
| 36 | struct hlist_node node; | 36 | struct hlist_node node; |
| 37 | u64 id; | 37 | u64 id; |
| 38 | struct perf_evsel *evsel; | 38 | struct perf_evsel *evsel; |
| 39 | int idx; | ||
| 40 | int cpu; | ||
| 41 | pid_t tid; | ||
| 39 | 42 | ||
| 40 | /* Holds total ID period value for PERF_SAMPLE_READ processing. */ | 43 | /* Holds total ID period value for PERF_SAMPLE_READ processing. */ |
| 41 | u64 period; | 44 | u64 period; |
| @@ -54,6 +57,7 @@ struct cgroup_sel; | |||
| 54 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or | 57 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or |
| 55 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all | 58 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all |
| 56 | * is used there is an id sample appended to non-sample events | 59 | * is used there is an id sample appended to non-sample events |
| 60 | * @priv: And what is in its containing unnamed union are tool specific | ||
| 57 | */ | 61 | */ |
| 58 | struct perf_evsel { | 62 | struct perf_evsel { |
| 59 | struct list_head node; | 63 | struct list_head node; |
| @@ -73,6 +77,7 @@ struct perf_evsel { | |||
| 73 | union { | 77 | union { |
| 74 | void *priv; | 78 | void *priv; |
| 75 | off_t id_offset; | 79 | off_t id_offset; |
| 80 | u64 db_id; | ||
| 76 | }; | 81 | }; |
| 77 | struct cgroup_sel *cgrp; | 82 | struct cgroup_sel *cgrp; |
| 78 | void *handler; | 83 | void *handler; |
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c new file mode 100644 index 000000000000..95ef1cffc056 --- /dev/null +++ b/tools/perf/util/find-vdso-map.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | static int find_vdso_map(void **start, void **end) | ||
| 2 | { | ||
| 3 | FILE *maps; | ||
| 4 | char line[128]; | ||
| 5 | int found = 0; | ||
| 6 | |||
| 7 | maps = fopen("/proc/self/maps", "r"); | ||
| 8 | if (!maps) { | ||
| 9 | fprintf(stderr, "vdso: cannot open maps\n"); | ||
| 10 | return -1; | ||
| 11 | } | ||
| 12 | |||
| 13 | while (!found && fgets(line, sizeof(line), maps)) { | ||
| 14 | int m = -1; | ||
| 15 | |||
| 16 | /* We care only about private r-x mappings. */ | ||
| 17 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
| 18 | start, end, &m)) | ||
| 19 | continue; | ||
| 20 | if (m < 0) | ||
| 21 | continue; | ||
| 22 | |||
| 23 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
| 24 | sizeof(VDSO__MAP_NAME) - 1)) | ||
| 25 | found = 1; | ||
| 26 | } | ||
| 27 | |||
| 28 | fclose(maps); | ||
| 29 | return !found; | ||
| 30 | } | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 26f5b2fe5dc8..b20e40c74468 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -24,8 +24,6 @@ | |||
| 24 | #include "build-id.h" | 24 | #include "build-id.h" |
| 25 | #include "data.h" | 25 | #include "data.h" |
| 26 | 26 | ||
| 27 | static bool no_buildid_cache = false; | ||
| 28 | |||
| 29 | static u32 header_argc; | 27 | static u32 header_argc; |
| 30 | static const char **header_argv; | 28 | static const char **header_argv; |
| 31 | 29 | ||
| @@ -79,10 +77,7 @@ static int do_write(int fd, const void *buf, size_t size) | |||
| 79 | return 0; | 77 | return 0; |
| 80 | } | 78 | } |
| 81 | 79 | ||
| 82 | #define NAME_ALIGN 64 | 80 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned) |
| 83 | |||
| 84 | static int write_padded(int fd, const void *bf, size_t count, | ||
| 85 | size_t count_aligned) | ||
| 86 | { | 81 | { |
| 87 | static const char zero_buf[NAME_ALIGN]; | 82 | static const char zero_buf[NAME_ALIGN]; |
| 88 | int err = do_write(fd, bf, count); | 83 | int err = do_write(fd, bf, count); |
| @@ -171,340 +166,6 @@ perf_header__set_cmdline(int argc, const char **argv) | |||
| 171 | return 0; | 166 | return 0; |
| 172 | } | 167 | } |
| 173 | 168 | ||
| 174 | #define dsos__for_each_with_build_id(pos, head) \ | ||
| 175 | list_for_each_entry(pos, head, node) \ | ||
| 176 | if (!pos->has_build_id) \ | ||
| 177 | continue; \ | ||
| 178 | else | ||
| 179 | |||
| 180 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
| 181 | pid_t pid, u16 misc, int fd) | ||
| 182 | { | ||
| 183 | int err; | ||
| 184 | struct build_id_event b; | ||
| 185 | size_t len; | ||
| 186 | |||
| 187 | len = name_len + 1; | ||
| 188 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
| 189 | |||
| 190 | memset(&b, 0, sizeof(b)); | ||
| 191 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
| 192 | b.pid = pid; | ||
| 193 | b.header.misc = misc; | ||
| 194 | b.header.size = sizeof(b) + len; | ||
| 195 | |||
| 196 | err = do_write(fd, &b, sizeof(b)); | ||
| 197 | if (err < 0) | ||
| 198 | return err; | ||
| 199 | |||
| 200 | return write_padded(fd, name, name_len + 1, len); | ||
| 201 | } | ||
| 202 | |||
| 203 | static int __dsos__hit_all(struct list_head *head) | ||
| 204 | { | ||
| 205 | struct dso *pos; | ||
| 206 | |||
| 207 | list_for_each_entry(pos, head, node) | ||
| 208 | pos->hit = true; | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int machine__hit_all_dsos(struct machine *machine) | ||
| 214 | { | ||
| 215 | int err; | ||
| 216 | |||
| 217 | err = __dsos__hit_all(&machine->kernel_dsos.head); | ||
| 218 | if (err) | ||
| 219 | return err; | ||
| 220 | |||
| 221 | return __dsos__hit_all(&machine->user_dsos.head); | ||
| 222 | } | ||
| 223 | |||
| 224 | int dsos__hit_all(struct perf_session *session) | ||
| 225 | { | ||
| 226 | struct rb_node *nd; | ||
| 227 | int err; | ||
| 228 | |||
| 229 | err = machine__hit_all_dsos(&session->machines.host); | ||
| 230 | if (err) | ||
| 231 | return err; | ||
| 232 | |||
| 233 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 234 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 235 | |||
| 236 | err = machine__hit_all_dsos(pos); | ||
| 237 | if (err) | ||
| 238 | return err; | ||
| 239 | } | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int __dsos__write_buildid_table(struct list_head *head, | ||
| 245 | struct machine *machine, | ||
| 246 | pid_t pid, u16 misc, int fd) | ||
| 247 | { | ||
| 248 | char nm[PATH_MAX]; | ||
| 249 | struct dso *pos; | ||
| 250 | |||
| 251 | dsos__for_each_with_build_id(pos, head) { | ||
| 252 | int err; | ||
| 253 | const char *name; | ||
| 254 | size_t name_len; | ||
| 255 | |||
| 256 | if (!pos->hit) | ||
| 257 | continue; | ||
| 258 | |||
| 259 | if (dso__is_vdso(pos)) { | ||
| 260 | name = pos->short_name; | ||
| 261 | name_len = pos->short_name_len + 1; | ||
| 262 | } else if (dso__is_kcore(pos)) { | ||
| 263 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
| 264 | name = nm; | ||
| 265 | name_len = strlen(nm) + 1; | ||
| 266 | } else { | ||
| 267 | name = pos->long_name; | ||
| 268 | name_len = pos->long_name_len + 1; | ||
| 269 | } | ||
| 270 | |||
| 271 | err = write_buildid(name, name_len, pos->build_id, | ||
| 272 | pid, misc, fd); | ||
| 273 | if (err) | ||
| 274 | return err; | ||
| 275 | } | ||
| 276 | |||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
| 281 | { | ||
| 282 | int err; | ||
| 283 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
| 284 | umisc = PERF_RECORD_MISC_USER; | ||
| 285 | |||
| 286 | if (!machine__is_host(machine)) { | ||
| 287 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
| 288 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
| 289 | } | ||
| 290 | |||
| 291 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, | ||
| 292 | machine->pid, kmisc, fd); | ||
| 293 | if (err == 0) | ||
| 294 | err = __dsos__write_buildid_table(&machine->user_dsos.head, | ||
| 295 | machine, machine->pid, umisc, | ||
| 296 | fd); | ||
| 297 | return err; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int dsos__write_buildid_table(struct perf_header *header, int fd) | ||
| 301 | { | ||
| 302 | struct perf_session *session = container_of(header, | ||
| 303 | struct perf_session, header); | ||
| 304 | struct rb_node *nd; | ||
| 305 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
| 306 | |||
| 307 | if (err) | ||
| 308 | return err; | ||
| 309 | |||
| 310 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 311 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 312 | err = machine__write_buildid_table(pos, fd); | ||
| 313 | if (err) | ||
| 314 | break; | ||
| 315 | } | ||
| 316 | return err; | ||
| 317 | } | ||
| 318 | |||
| 319 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
| 320 | const char *name, bool is_kallsyms, bool is_vdso) | ||
| 321 | { | ||
| 322 | const size_t size = PATH_MAX; | ||
| 323 | char *realname, *filename = zalloc(size), | ||
| 324 | *linkname = zalloc(size), *targetname; | ||
| 325 | int len, err = -1; | ||
| 326 | bool slash = is_kallsyms || is_vdso; | ||
| 327 | |||
| 328 | if (is_kallsyms) { | ||
| 329 | if (symbol_conf.kptr_restrict) { | ||
| 330 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
| 331 | err = 0; | ||
| 332 | goto out_free; | ||
| 333 | } | ||
| 334 | realname = (char *) name; | ||
| 335 | } else | ||
| 336 | realname = realpath(name, NULL); | ||
| 337 | |||
| 338 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
| 339 | goto out_free; | ||
| 340 | |||
| 341 | len = scnprintf(filename, size, "%s%s%s", | ||
| 342 | debugdir, slash ? "/" : "", | ||
| 343 | is_vdso ? DSO__NAME_VDSO : realname); | ||
| 344 | if (mkdir_p(filename, 0755)) | ||
| 345 | goto out_free; | ||
| 346 | |||
| 347 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
| 348 | |||
| 349 | if (access(filename, F_OK)) { | ||
| 350 | if (is_kallsyms) { | ||
| 351 | if (copyfile("/proc/kallsyms", filename)) | ||
| 352 | goto out_free; | ||
| 353 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
| 354 | goto out_free; | ||
| 355 | } | ||
| 356 | |||
| 357 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
| 358 | debugdir, sbuild_id); | ||
| 359 | |||
| 360 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
| 361 | goto out_free; | ||
| 362 | |||
| 363 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
| 364 | targetname = filename + strlen(debugdir) - 5; | ||
| 365 | memcpy(targetname, "../..", 5); | ||
| 366 | |||
| 367 | if (symlink(targetname, linkname) == 0) | ||
| 368 | err = 0; | ||
| 369 | out_free: | ||
| 370 | if (!is_kallsyms) | ||
| 371 | free(realname); | ||
| 372 | free(filename); | ||
| 373 | free(linkname); | ||
| 374 | return err; | ||
| 375 | } | ||
| 376 | |||
| 377 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
| 378 | const char *name, const char *debugdir, | ||
| 379 | bool is_kallsyms, bool is_vdso) | ||
| 380 | { | ||
| 381 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 382 | |||
| 383 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
| 384 | |||
| 385 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
| 386 | is_kallsyms, is_vdso); | ||
| 387 | } | ||
| 388 | |||
| 389 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
| 390 | { | ||
| 391 | const size_t size = PATH_MAX; | ||
| 392 | char *filename = zalloc(size), | ||
| 393 | *linkname = zalloc(size); | ||
| 394 | int err = -1; | ||
| 395 | |||
| 396 | if (filename == NULL || linkname == NULL) | ||
| 397 | goto out_free; | ||
| 398 | |||
| 399 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
| 400 | debugdir, sbuild_id, sbuild_id + 2); | ||
| 401 | |||
| 402 | if (access(linkname, F_OK)) | ||
| 403 | goto out_free; | ||
| 404 | |||
| 405 | if (readlink(linkname, filename, size - 1) < 0) | ||
| 406 | goto out_free; | ||
| 407 | |||
| 408 | if (unlink(linkname)) | ||
| 409 | goto out_free; | ||
| 410 | |||
| 411 | /* | ||
| 412 | * Since the link is relative, we must make it absolute: | ||
| 413 | */ | ||
| 414 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
| 415 | debugdir, sbuild_id, filename); | ||
| 416 | |||
| 417 | if (unlink(linkname)) | ||
| 418 | goto out_free; | ||
| 419 | |||
| 420 | err = 0; | ||
| 421 | out_free: | ||
| 422 | free(filename); | ||
| 423 | free(linkname); | ||
| 424 | return err; | ||
| 425 | } | ||
| 426 | |||
| 427 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
| 428 | const char *debugdir) | ||
| 429 | { | ||
| 430 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
| 431 | bool is_vdso = dso__is_vdso(dso); | ||
| 432 | const char *name = dso->long_name; | ||
| 433 | char nm[PATH_MAX]; | ||
| 434 | |||
| 435 | if (dso__is_kcore(dso)) { | ||
| 436 | is_kallsyms = true; | ||
| 437 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
| 438 | name = nm; | ||
| 439 | } | ||
| 440 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
| 441 | debugdir, is_kallsyms, is_vdso); | ||
| 442 | } | ||
| 443 | |||
| 444 | static int __dsos__cache_build_ids(struct list_head *head, | ||
| 445 | struct machine *machine, const char *debugdir) | ||
| 446 | { | ||
| 447 | struct dso *pos; | ||
| 448 | int err = 0; | ||
| 449 | |||
| 450 | dsos__for_each_with_build_id(pos, head) | ||
| 451 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
| 452 | err = -1; | ||
| 453 | |||
| 454 | return err; | ||
| 455 | } | ||
| 456 | |||
| 457 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
| 458 | { | ||
| 459 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, | ||
| 460 | debugdir); | ||
| 461 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, | ||
| 462 | debugdir); | ||
| 463 | return ret; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int perf_session__cache_build_ids(struct perf_session *session) | ||
| 467 | { | ||
| 468 | struct rb_node *nd; | ||
| 469 | int ret; | ||
| 470 | char debugdir[PATH_MAX]; | ||
| 471 | |||
| 472 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | ||
| 473 | |||
| 474 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
| 475 | return -1; | ||
| 476 | |||
| 477 | ret = machine__cache_build_ids(&session->machines.host, debugdir); | ||
| 478 | |||
| 479 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 480 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 481 | ret |= machine__cache_build_ids(pos, debugdir); | ||
| 482 | } | ||
| 483 | return ret ? -1 : 0; | ||
| 484 | } | ||
| 485 | |||
| 486 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
| 487 | { | ||
| 488 | bool ret; | ||
| 489 | |||
| 490 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
| 491 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
| 492 | return ret; | ||
| 493 | } | ||
| 494 | |||
| 495 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
| 496 | { | ||
| 497 | struct rb_node *nd; | ||
| 498 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
| 499 | |||
| 500 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
| 501 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
| 502 | ret |= machine__read_build_ids(pos, with_hits); | ||
| 503 | } | ||
| 504 | |||
| 505 | return ret; | ||
| 506 | } | ||
| 507 | |||
| 508 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, | 169 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, |
| 509 | struct perf_evlist *evlist) | 170 | struct perf_evlist *evlist) |
| 510 | { | 171 | { |
| @@ -523,13 +184,12 @@ static int write_build_id(int fd, struct perf_header *h, | |||
| 523 | if (!perf_session__read_build_ids(session, true)) | 184 | if (!perf_session__read_build_ids(session, true)) |
| 524 | return -1; | 185 | return -1; |
| 525 | 186 | ||
| 526 | err = dsos__write_buildid_table(h, fd); | 187 | err = perf_session__write_buildid_table(session, fd); |
| 527 | if (err < 0) { | 188 | if (err < 0) { |
| 528 | pr_debug("failed to write buildid table\n"); | 189 | pr_debug("failed to write buildid table\n"); |
| 529 | return err; | 190 | return err; |
| 530 | } | 191 | } |
| 531 | if (!no_buildid_cache) | 192 | perf_session__cache_build_ids(session); |
| 532 | perf_session__cache_build_ids(session); | ||
| 533 | 193 | ||
| 534 | return 0; | 194 | return 0; |
| 535 | } | 195 | } |
| @@ -601,8 +261,10 @@ static int __write_cpudesc(int fd, const char *cpuinfo_proc) | |||
| 601 | break; | 261 | break; |
| 602 | } | 262 | } |
| 603 | 263 | ||
| 604 | if (ret) | 264 | if (ret) { |
| 265 | ret = -1; | ||
| 605 | goto done; | 266 | goto done; |
| 267 | } | ||
| 606 | 268 | ||
| 607 | s = buf; | 269 | s = buf; |
| 608 | 270 | ||
| @@ -965,7 +627,8 @@ static int write_total_mem(int fd, struct perf_header *h __maybe_unused, | |||
| 965 | n = sscanf(buf, "%*s %"PRIu64, &mem); | 627 | n = sscanf(buf, "%*s %"PRIu64, &mem); |
| 966 | if (n == 1) | 628 | if (n == 1) |
| 967 | ret = do_write(fd, &mem, sizeof(mem)); | 629 | ret = do_write(fd, &mem, sizeof(mem)); |
| 968 | } | 630 | } else |
| 631 | ret = -1; | ||
| 969 | free(buf); | 632 | free(buf); |
| 970 | fclose(fp); | 633 | fclose(fp); |
| 971 | return ret; | 634 | return ret; |
| @@ -1603,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1603 | 1266 | ||
| 1604 | dso__set_build_id(dso, &bev->build_id); | 1267 | dso__set_build_id(dso, &bev->build_id); |
| 1605 | 1268 | ||
| 1606 | if (filename[0] == '[') | 1269 | if (!is_kernel_module(filename, NULL)) |
| 1607 | dso->kernel = dso_type; | 1270 | dso->kernel = dso_type; |
| 1608 | 1271 | ||
| 1609 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1272 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
| @@ -2477,6 +2140,7 @@ static const int attr_file_abi_sizes[] = { | |||
| 2477 | [1] = PERF_ATTR_SIZE_VER1, | 2140 | [1] = PERF_ATTR_SIZE_VER1, |
| 2478 | [2] = PERF_ATTR_SIZE_VER2, | 2141 | [2] = PERF_ATTR_SIZE_VER2, |
| 2479 | [3] = PERF_ATTR_SIZE_VER3, | 2142 | [3] = PERF_ATTR_SIZE_VER3, |
| 2143 | [4] = PERF_ATTR_SIZE_VER4, | ||
| 2480 | 0, | 2144 | 0, |
| 2481 | }; | 2145 | }; |
| 2482 | 2146 | ||
| @@ -3124,8 +2788,3 @@ int perf_event__process_build_id(struct perf_tool *tool __maybe_unused, | |||
| 3124 | session); | 2788 | session); |
| 3125 | return 0; | 2789 | return 0; |
| 3126 | } | 2790 | } |
| 3127 | |||
| 3128 | void disable_buildid_cache(void) | ||
| 3129 | { | ||
| 3130 | no_buildid_cache = true; | ||
| 3131 | } | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 8f5cbaea64a5..3bb90ac172a1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
| 122 | 122 | ||
| 123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | 123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); |
| 124 | 124 | ||
| 125 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
| 126 | const char *name, bool is_kallsyms, bool is_vdso); | ||
| 127 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
| 128 | |||
| 129 | int perf_event__synthesize_attr(struct perf_tool *tool, | 125 | int perf_event__synthesize_attr(struct perf_tool *tool, |
| 130 | struct perf_event_attr *attr, u32 ids, u64 *id, | 126 | struct perf_event_attr *attr, u32 ids, u64 *id, |
| 131 | perf_event__handler_t process); | 127 | perf_event__handler_t process); |
| @@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool, | |||
| 151 | struct perf_session *session); | 147 | struct perf_session *session); |
| 152 | bool is_perf_magic(u64 magic); | 148 | bool is_perf_magic(u64 magic); |
| 153 | 149 | ||
| 154 | int dsos__hit_all(struct perf_session *session); | 150 | #define NAME_ALIGN 64 |
| 151 | |||
| 152 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); | ||
| 155 | 153 | ||
| 156 | /* | 154 | /* |
| 157 | * arch specific callback | 155 | * arch specific callback |
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 01ffd12dc791..40bd21488032 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
| @@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, | |||
| 46 | __bitmap_or(dst, src1, src2, nbits); | 46 | __bitmap_or(dst, src1, src2, nbits); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | /** | ||
| 50 | * test_and_set_bit - Set a bit and return its old value | ||
| 51 | * @nr: Bit to set | ||
| 52 | * @addr: Address to count from | ||
| 53 | */ | ||
| 54 | static inline int test_and_set_bit(int nr, unsigned long *addr) | ||
| 55 | { | ||
| 56 | unsigned long mask = BIT_MASK(nr); | ||
| 57 | unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); | ||
| 58 | unsigned long old; | ||
| 59 | |||
| 60 | old = *p; | ||
| 61 | *p = old | mask; | ||
| 62 | |||
| 63 | return (old & mask) != 0; | ||
| 64 | } | ||
| 65 | |||
| 49 | #endif /* _PERF_BITOPS_H */ | 66 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index dadfa7e54287..c3294163de17 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) | 15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) |
| 16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) | 16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) |
| 17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) | 17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) |
| 18 | #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) | ||
| 19 | #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) | ||
| 18 | 20 | ||
| 19 | #define for_each_set_bit(bit, addr, size) \ | 21 | #define for_each_set_bit(bit, addr, size) \ |
| 20 | for ((bit) = find_first_bit((addr), (size)); \ | 22 | for ((bit) = find_first_bit((addr), (size)); \ |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 34fc7c8672e4..d97309c87bd6 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -21,7 +21,7 @@ static void dsos__init(struct dsos *dsos) | |||
| 21 | 21 | ||
| 22 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 22 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
| 23 | { | 23 | { |
| 24 | map_groups__init(&machine->kmaps); | 24 | map_groups__init(&machine->kmaps, machine); |
| 25 | RB_CLEAR_NODE(&machine->rb_node); | 25 | RB_CLEAR_NODE(&machine->rb_node); |
| 26 | dsos__init(&machine->user_dsos); | 26 | dsos__init(&machine->user_dsos); |
| 27 | dsos__init(&machine->kernel_dsos); | 27 | dsos__init(&machine->kernel_dsos); |
| @@ -32,7 +32,6 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
| 32 | 32 | ||
| 33 | machine->vdso_info = NULL; | 33 | machine->vdso_info = NULL; |
| 34 | 34 | ||
| 35 | machine->kmaps.machine = machine; | ||
| 36 | machine->pid = pid; | 35 | machine->pid = pid; |
| 37 | 36 | ||
| 38 | machine->symbol_filter = NULL; | 37 | machine->symbol_filter = NULL; |
| @@ -319,7 +318,7 @@ static void machine__update_thread_pid(struct machine *machine, | |||
| 319 | goto out_err; | 318 | goto out_err; |
| 320 | 319 | ||
| 321 | if (!leader->mg) | 320 | if (!leader->mg) |
| 322 | leader->mg = map_groups__new(); | 321 | leader->mg = map_groups__new(machine); |
| 323 | 322 | ||
| 324 | if (!leader->mg) | 323 | if (!leader->mg) |
| 325 | goto out_err; | 324 | goto out_err; |
| @@ -465,6 +464,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 465 | { | 464 | { |
| 466 | struct map *map; | 465 | struct map *map; |
| 467 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | 466 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); |
| 467 | bool compressed; | ||
| 468 | 468 | ||
| 469 | if (dso == NULL) | 469 | if (dso == NULL) |
| 470 | return NULL; | 470 | return NULL; |
| @@ -477,6 +477,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 477 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | 477 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; |
| 478 | else | 478 | else |
| 479 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | 479 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; |
| 480 | |||
| 481 | /* _KMODULE_COMP should be next to _KMODULE */ | ||
| 482 | if (is_kernel_module(filename, &compressed) && compressed) | ||
| 483 | dso->symtab_type++; | ||
| 484 | |||
| 480 | map_groups__insert(&machine->kmaps, map); | 485 | map_groups__insert(&machine->kmaps, map); |
| 481 | return map; | 486 | return map; |
| 482 | } | 487 | } |
| @@ -862,8 +867,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, | |||
| 862 | struct map *map; | 867 | struct map *map; |
| 863 | char *long_name; | 868 | char *long_name; |
| 864 | 869 | ||
| 865 | if (dot == NULL || strcmp(dot, ".ko")) | 870 | if (dot == NULL) |
| 866 | continue; | 871 | continue; |
| 872 | |||
| 873 | /* On some system, modules are compressed like .ko.gz */ | ||
| 874 | if (is_supported_compression(dot + 1) && | ||
| 875 | is_kmodule_extension(dot - 2)) | ||
| 876 | dot -= 3; | ||
| 877 | |||
| 867 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 878 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", |
| 868 | (int)(dot - dent->d_name), dent->d_name); | 879 | (int)(dot - dent->d_name), dent->d_name); |
| 869 | 880 | ||
| @@ -1045,6 +1056,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1045 | dot = strrchr(name, '.'); | 1056 | dot = strrchr(name, '.'); |
| 1046 | if (dot == NULL) | 1057 | if (dot == NULL) |
| 1047 | goto out_problem; | 1058 | goto out_problem; |
| 1059 | /* On some system, modules are compressed like .ko.gz */ | ||
| 1060 | if (is_supported_compression(dot + 1)) | ||
| 1061 | dot -= 3; | ||
| 1062 | if (!is_kmodule_extension(dot + 1)) | ||
| 1063 | goto out_problem; | ||
| 1048 | snprintf(short_module_name, sizeof(short_module_name), | 1064 | snprintf(short_module_name, sizeof(short_module_name), |
| 1049 | "[%.*s]", (int)(dot - name), name); | 1065 | "[%.*s]", (int)(dot - name), name); |
| 1050 | strxfrchar(short_module_name, '-', '_'); | 1066 | strxfrchar(short_module_name, '-', '_'); |
| @@ -1069,8 +1085,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1069 | * Should be there already, from the build-id table in | 1085 | * Should be there already, from the build-id table in |
| 1070 | * the header. | 1086 | * the header. |
| 1071 | */ | 1087 | */ |
| 1072 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | 1088 | struct dso *kernel = NULL; |
| 1073 | kmmap_prefix); | 1089 | struct dso *dso; |
| 1090 | |||
| 1091 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { | ||
| 1092 | if (is_kernel_module(dso->long_name, NULL)) | ||
| 1093 | continue; | ||
| 1094 | |||
| 1095 | kernel = dso; | ||
| 1096 | break; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (kernel == NULL) | ||
| 1100 | kernel = __dsos__findnew(&machine->kernel_dsos, | ||
| 1101 | kmmap_prefix); | ||
| 1074 | if (kernel == NULL) | 1102 | if (kernel == NULL) |
| 1075 | goto out_problem; | 1103 | goto out_problem; |
| 1076 | 1104 | ||
| @@ -1078,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1078 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 1106 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
| 1079 | goto out_problem; | 1107 | goto out_problem; |
| 1080 | 1108 | ||
| 1109 | if (strstr(dso->long_name, "vmlinux")) | ||
| 1110 | dso__set_short_name(dso, "[kernel.vmlinux]", false); | ||
| 1111 | |||
| 1081 | machine__set_kernel_mmap_len(machine, event); | 1112 | machine__set_kernel_mmap_len(machine, event); |
| 1082 | 1113 | ||
| 1083 | /* | 1114 | /* |
| @@ -1290,7 +1321,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex) | |||
| 1290 | return 0; | 1321 | return 0; |
| 1291 | } | 1322 | } |
| 1292 | 1323 | ||
| 1293 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | 1324 | static void ip__resolve_ams(struct thread *thread, |
| 1294 | struct addr_map_symbol *ams, | 1325 | struct addr_map_symbol *ams, |
| 1295 | u64 ip) | 1326 | u64 ip) |
| 1296 | { | 1327 | { |
| @@ -1304,7 +1335,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
| 1304 | * Thus, we have to try consecutively until we find a match | 1335 | * Thus, we have to try consecutively until we find a match |
| 1305 | * or else, the symbol is unknown | 1336 | * or else, the symbol is unknown |
| 1306 | */ | 1337 | */ |
| 1307 | thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); | 1338 | thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al); |
| 1308 | 1339 | ||
| 1309 | ams->addr = ip; | 1340 | ams->addr = ip; |
| 1310 | ams->al_addr = al.addr; | 1341 | ams->al_addr = al.addr; |
| @@ -1312,23 +1343,21 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
| 1312 | ams->map = al.map; | 1343 | ams->map = al.map; |
| 1313 | } | 1344 | } |
| 1314 | 1345 | ||
| 1315 | static void ip__resolve_data(struct machine *machine, struct thread *thread, | 1346 | static void ip__resolve_data(struct thread *thread, |
| 1316 | u8 m, struct addr_map_symbol *ams, u64 addr) | 1347 | u8 m, struct addr_map_symbol *ams, u64 addr) |
| 1317 | { | 1348 | { |
| 1318 | struct addr_location al; | 1349 | struct addr_location al; |
| 1319 | 1350 | ||
| 1320 | memset(&al, 0, sizeof(al)); | 1351 | memset(&al, 0, sizeof(al)); |
| 1321 | 1352 | ||
| 1322 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1353 | thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al); |
| 1323 | &al); | ||
| 1324 | if (al.map == NULL) { | 1354 | if (al.map == NULL) { |
| 1325 | /* | 1355 | /* |
| 1326 | * some shared data regions have execute bit set which puts | 1356 | * some shared data regions have execute bit set which puts |
| 1327 | * their mapping in the MAP__FUNCTION type array. | 1357 | * their mapping in the MAP__FUNCTION type array. |
| 1328 | * Check there as a fallback option before dropping the sample. | 1358 | * Check there as a fallback option before dropping the sample. |
| 1329 | */ | 1359 | */ |
| 1330 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, | 1360 | thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al); |
| 1331 | &al); | ||
| 1332 | } | 1361 | } |
| 1333 | 1362 | ||
| 1334 | ams->addr = addr; | 1363 | ams->addr = addr; |
| @@ -1345,14 +1374,41 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, | |||
| 1345 | if (!mi) | 1374 | if (!mi) |
| 1346 | return NULL; | 1375 | return NULL; |
| 1347 | 1376 | ||
| 1348 | ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); | 1377 | ip__resolve_ams(al->thread, &mi->iaddr, sample->ip); |
| 1349 | ip__resolve_data(al->machine, al->thread, al->cpumode, | 1378 | ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr); |
| 1350 | &mi->daddr, sample->addr); | ||
| 1351 | mi->data_src.val = sample->data_src; | 1379 | mi->data_src.val = sample->data_src; |
| 1352 | 1380 | ||
| 1353 | return mi; | 1381 | return mi; |
| 1354 | } | 1382 | } |
| 1355 | 1383 | ||
| 1384 | static int add_callchain_ip(struct thread *thread, | ||
| 1385 | struct symbol **parent, | ||
| 1386 | struct addr_location *root_al, | ||
| 1387 | int cpumode, | ||
| 1388 | u64 ip) | ||
| 1389 | { | ||
| 1390 | struct addr_location al; | ||
| 1391 | |||
| 1392 | al.filtered = 0; | ||
| 1393 | al.sym = NULL; | ||
| 1394 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | ||
| 1395 | ip, &al); | ||
| 1396 | if (al.sym != NULL) { | ||
| 1397 | if (sort__has_parent && !*parent && | ||
| 1398 | symbol__match_regex(al.sym, &parent_regex)) | ||
| 1399 | *parent = al.sym; | ||
| 1400 | else if (have_ignore_callees && root_al && | ||
| 1401 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
| 1402 | /* Treat this symbol as the root, | ||
| 1403 | forgetting its callees. */ | ||
| 1404 | *root_al = al; | ||
| 1405 | callchain_cursor_reset(&callchain_cursor); | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); | ||
| 1410 | } | ||
| 1411 | |||
| 1356 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | 1412 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, |
| 1357 | struct addr_location *al) | 1413 | struct addr_location *al) |
| 1358 | { | 1414 | { |
| @@ -1364,15 +1420,14 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
| 1364 | return NULL; | 1420 | return NULL; |
| 1365 | 1421 | ||
| 1366 | for (i = 0; i < bs->nr; i++) { | 1422 | for (i = 0; i < bs->nr; i++) { |
| 1367 | ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); | 1423 | ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to); |
| 1368 | ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from); | 1424 | ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from); |
| 1369 | bi[i].flags = bs->entries[i].flags; | 1425 | bi[i].flags = bs->entries[i].flags; |
| 1370 | } | 1426 | } |
| 1371 | return bi; | 1427 | return bi; |
| 1372 | } | 1428 | } |
| 1373 | 1429 | ||
| 1374 | static int machine__resolve_callchain_sample(struct machine *machine, | 1430 | static int thread__resolve_callchain_sample(struct thread *thread, |
| 1375 | struct thread *thread, | ||
| 1376 | struct ip_callchain *chain, | 1431 | struct ip_callchain *chain, |
| 1377 | struct symbol **parent, | 1432 | struct symbol **parent, |
| 1378 | struct addr_location *root_al, | 1433 | struct addr_location *root_al, |
| @@ -1396,11 +1451,10 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1396 | * Based on DWARF debug information, some architectures skip | 1451 | * Based on DWARF debug information, some architectures skip |
| 1397 | * a callchain entry saved by the kernel. | 1452 | * a callchain entry saved by the kernel. |
| 1398 | */ | 1453 | */ |
| 1399 | skip_idx = arch_skip_callchain_idx(machine, thread, chain); | 1454 | skip_idx = arch_skip_callchain_idx(thread, chain); |
| 1400 | 1455 | ||
| 1401 | for (i = 0; i < chain_nr; i++) { | 1456 | for (i = 0; i < chain_nr; i++) { |
| 1402 | u64 ip; | 1457 | u64 ip; |
| 1403 | struct addr_location al; | ||
| 1404 | 1458 | ||
| 1405 | if (callchain_param.order == ORDER_CALLEE) | 1459 | if (callchain_param.order == ORDER_CALLEE) |
| 1406 | j = i; | 1460 | j = i; |
| @@ -1437,24 +1491,10 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1437 | continue; | 1491 | continue; |
| 1438 | } | 1492 | } |
| 1439 | 1493 | ||
| 1440 | al.filtered = 0; | 1494 | err = add_callchain_ip(thread, parent, root_al, |
| 1441 | thread__find_addr_location(thread, machine, cpumode, | 1495 | cpumode, ip); |
| 1442 | MAP__FUNCTION, ip, &al); | 1496 | if (err == -EINVAL) |
| 1443 | if (al.sym != NULL) { | 1497 | break; |
| 1444 | if (sort__has_parent && !*parent && | ||
| 1445 | symbol__match_regex(al.sym, &parent_regex)) | ||
| 1446 | *parent = al.sym; | ||
| 1447 | else if (have_ignore_callees && root_al && | ||
| 1448 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
| 1449 | /* Treat this symbol as the root, | ||
| 1450 | forgetting its callees. */ | ||
| 1451 | *root_al = al; | ||
| 1452 | callchain_cursor_reset(&callchain_cursor); | ||
| 1453 | } | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | err = callchain_cursor_append(&callchain_cursor, | ||
| 1457 | ip, al.map, al.sym); | ||
| 1458 | if (err) | 1498 | if (err) |
| 1459 | return err; | 1499 | return err; |
| 1460 | } | 1500 | } |
| @@ -1469,19 +1509,15 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
| 1469 | entry->map, entry->sym); | 1509 | entry->map, entry->sym); |
| 1470 | } | 1510 | } |
| 1471 | 1511 | ||
| 1472 | int machine__resolve_callchain(struct machine *machine, | 1512 | int thread__resolve_callchain(struct thread *thread, |
| 1473 | struct perf_evsel *evsel, | 1513 | struct perf_evsel *evsel, |
| 1474 | struct thread *thread, | 1514 | struct perf_sample *sample, |
| 1475 | struct perf_sample *sample, | 1515 | struct symbol **parent, |
| 1476 | struct symbol **parent, | 1516 | struct addr_location *root_al, |
| 1477 | struct addr_location *root_al, | 1517 | int max_stack) |
| 1478 | int max_stack) | ||
| 1479 | { | 1518 | { |
| 1480 | int ret; | 1519 | int ret = thread__resolve_callchain_sample(thread, sample->callchain, |
| 1481 | 1520 | parent, root_al, max_stack); | |
| 1482 | ret = machine__resolve_callchain_sample(machine, thread, | ||
| 1483 | sample->callchain, parent, | ||
| 1484 | root_al, max_stack); | ||
| 1485 | if (ret) | 1521 | if (ret) |
| 1486 | return ret; | 1522 | return ret; |
| 1487 | 1523 | ||
| @@ -1495,7 +1531,7 @@ int machine__resolve_callchain(struct machine *machine, | |||
| 1495 | (!sample->user_stack.size)) | 1531 | (!sample->user_stack.size)) |
| 1496 | return 0; | 1532 | return 0; |
| 1497 | 1533 | ||
| 1498 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | 1534 | return unwind__get_entries(unwind_entry, &callchain_cursor, |
| 1499 | thread, sample, max_stack); | 1535 | thread, sample, max_stack); |
| 1500 | 1536 | ||
| 1501 | } | 1537 | } |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2b651a7f5d0d..e8b7779a0a3f 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
| @@ -40,6 +40,10 @@ struct machine { | |||
| 40 | u64 kernel_start; | 40 | u64 kernel_start; |
| 41 | symbol_filter_t symbol_filter; | 41 | symbol_filter_t symbol_filter; |
| 42 | pid_t *current_tid; | 42 | pid_t *current_tid; |
| 43 | union { /* Tool specific area */ | ||
| 44 | void *priv; | ||
| 45 | u64 db_id; | ||
| 46 | }; | ||
| 43 | }; | 47 | }; |
| 44 | 48 | ||
| 45 | static inline | 49 | static inline |
| @@ -122,13 +126,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
| 122 | struct addr_location *al); | 126 | struct addr_location *al); |
| 123 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, | 127 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, |
| 124 | struct addr_location *al); | 128 | struct addr_location *al); |
| 125 | int machine__resolve_callchain(struct machine *machine, | 129 | int thread__resolve_callchain(struct thread *thread, |
| 126 | struct perf_evsel *evsel, | 130 | struct perf_evsel *evsel, |
| 127 | struct thread *thread, | 131 | struct perf_sample *sample, |
| 128 | struct perf_sample *sample, | 132 | struct symbol **parent, |
| 129 | struct symbol **parent, | 133 | struct addr_location *root_al, |
| 130 | struct addr_location *root_al, | 134 | int max_stack); |
| 131 | int max_stack); | ||
| 132 | 135 | ||
| 133 | /* | 136 | /* |
| 134 | * Default guest kernel is defined by parameter --guestkallsyms | 137 | * Default guest kernel is defined by parameter --guestkallsyms |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 2137c4596ec7..040a785c857b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -413,14 +413,14 @@ u64 map__objdump_2mem(struct map *map, u64 ip) | |||
| 413 | return ip + map->reloc; | 413 | return ip + map->reloc; |
| 414 | } | 414 | } |
| 415 | 415 | ||
| 416 | void map_groups__init(struct map_groups *mg) | 416 | void map_groups__init(struct map_groups *mg, struct machine *machine) |
| 417 | { | 417 | { |
| 418 | int i; | 418 | int i; |
| 419 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 419 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
| 420 | mg->maps[i] = RB_ROOT; | 420 | mg->maps[i] = RB_ROOT; |
| 421 | INIT_LIST_HEAD(&mg->removed_maps[i]); | 421 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
| 422 | } | 422 | } |
| 423 | mg->machine = NULL; | 423 | mg->machine = machine; |
| 424 | mg->refcnt = 1; | 424 | mg->refcnt = 1; |
| 425 | } | 425 | } |
| 426 | 426 | ||
| @@ -471,12 +471,12 @@ bool map_groups__empty(struct map_groups *mg) | |||
| 471 | return true; | 471 | return true; |
| 472 | } | 472 | } |
| 473 | 473 | ||
| 474 | struct map_groups *map_groups__new(void) | 474 | struct map_groups *map_groups__new(struct machine *machine) |
| 475 | { | 475 | { |
| 476 | struct map_groups *mg = malloc(sizeof(*mg)); | 476 | struct map_groups *mg = malloc(sizeof(*mg)); |
| 477 | 477 | ||
| 478 | if (mg != NULL) | 478 | if (mg != NULL) |
| 479 | map_groups__init(mg); | 479 | map_groups__init(mg, machine); |
| 480 | 480 | ||
| 481 | return mg; | 481 | return mg; |
| 482 | } | 482 | } |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2f83954af050..6951a9d42339 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
| @@ -64,7 +64,7 @@ struct map_groups { | |||
| 64 | int refcnt; | 64 | int refcnt; |
| 65 | }; | 65 | }; |
| 66 | 66 | ||
| 67 | struct map_groups *map_groups__new(void); | 67 | struct map_groups *map_groups__new(struct machine *machine); |
| 68 | void map_groups__delete(struct map_groups *mg); | 68 | void map_groups__delete(struct map_groups *mg); |
| 69 | bool map_groups__empty(struct map_groups *mg); | 69 | bool map_groups__empty(struct map_groups *mg); |
| 70 | 70 | ||
| @@ -150,7 +150,7 @@ void maps__remove(struct rb_root *maps, struct map *map); | |||
| 150 | struct map *maps__find(struct rb_root *maps, u64 addr); | 150 | struct map *maps__find(struct rb_root *maps, u64 addr); |
| 151 | struct map *maps__first(struct rb_root *maps); | 151 | struct map *maps__first(struct rb_root *maps); |
| 152 | struct map *maps__next(struct map *map); | 152 | struct map *maps__next(struct map *map); |
| 153 | void map_groups__init(struct map_groups *mg); | 153 | void map_groups__init(struct map_groups *mg, struct machine *machine); |
| 154 | void map_groups__exit(struct map_groups *mg); | 154 | void map_groups__exit(struct map_groups *mg); |
| 155 | int map_groups__clone(struct map_groups *mg, | 155 | int map_groups__clone(struct map_groups *mg, |
| 156 | struct map_groups *parent, enum map_type type); | 156 | struct map_groups *parent, enum map_type type); |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c659a3ca1283..efa1ff4cca63 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -526,7 +526,7 @@ do { \ | |||
| 526 | } | 526 | } |
| 527 | 527 | ||
| 528 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 528 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
| 529 | void *ptr, char *type) | 529 | void *ptr, char *type, u64 len) |
| 530 | { | 530 | { |
| 531 | struct perf_event_attr attr; | 531 | struct perf_event_attr attr; |
| 532 | 532 | ||
| @@ -536,14 +536,15 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, | |||
| 536 | if (parse_breakpoint_type(type, &attr)) | 536 | if (parse_breakpoint_type(type, &attr)) |
| 537 | return -EINVAL; | 537 | return -EINVAL; |
| 538 | 538 | ||
| 539 | /* | 539 | /* Provide some defaults if len is not specified */ |
| 540 | * We should find a nice way to override the access length | 540 | if (!len) { |
| 541 | * Provide some defaults for now | 541 | if (attr.bp_type == HW_BREAKPOINT_X) |
| 542 | */ | 542 | len = sizeof(long); |
| 543 | if (attr.bp_type == HW_BREAKPOINT_X) | 543 | else |
| 544 | attr.bp_len = sizeof(long); | 544 | len = HW_BREAKPOINT_LEN_4; |
| 545 | else | 545 | } |
| 546 | attr.bp_len = HW_BREAKPOINT_LEN_4; | 546 | |
| 547 | attr.bp_len = len; | ||
| 547 | 548 | ||
| 548 | attr.type = PERF_TYPE_BREAKPOINT; | 549 | attr.type = PERF_TYPE_BREAKPOINT; |
| 549 | attr.sample_period = 1; | 550 | attr.sample_period = 1; |
| @@ -1364,7 +1365,7 @@ void print_events(const char *event_glob, bool name_only) | |||
| 1364 | printf("\n"); | 1365 | printf("\n"); |
| 1365 | 1366 | ||
| 1366 | printf(" %-50s [%s]\n", | 1367 | printf(" %-50s [%s]\n", |
| 1367 | "mem:<addr>[:access]", | 1368 | "mem:<addr>[/len][:access]", |
| 1368 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1369 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
| 1369 | printf("\n"); | 1370 | printf("\n"); |
| 1370 | } | 1371 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index db2cf78ff0f3..a19fbeb80943 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -104,7 +104,7 @@ int parse_events_add_numeric(struct list_head *list, int *idx, | |||
| 104 | int parse_events_add_cache(struct list_head *list, int *idx, | 104 | int parse_events_add_cache(struct list_head *list, int *idx, |
| 105 | char *type, char *op_result1, char *op_result2); | 105 | char *type, char *op_result1, char *op_result2); |
| 106 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 106 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
| 107 | void *ptr, char *type); | 107 | void *ptr, char *type, u64 len); |
| 108 | int parse_events_add_pmu(struct list_head *list, int *idx, | 108 | int parse_events_add_pmu(struct list_head *list, int *idx, |
| 109 | char *pmu , struct list_head *head_config); | 109 | char *pmu , struct list_head *head_config); |
| 110 | enum perf_pmu_event_symbol_type | 110 | enum perf_pmu_event_symbol_type |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 906630bbf8eb..94eacb6c1ef7 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
| @@ -159,6 +159,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE | |||
| 159 | <mem>{ | 159 | <mem>{ |
| 160 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } | 160 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } |
| 161 | : { return ':'; } | 161 | : { return ':'; } |
| 162 | "/" { return '/'; } | ||
| 162 | {num_dec} { return value(yyscanner, 10); } | 163 | {num_dec} { return value(yyscanner, 10); } |
| 163 | {num_hex} { return value(yyscanner, 16); } | 164 | {num_hex} { return value(yyscanner, 16); } |
| 164 | /* | 165 | /* |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 93c4c9fbc922..72def077dbbf 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
| @@ -326,6 +326,28 @@ PE_NAME_CACHE_TYPE | |||
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | event_legacy_mem: | 328 | event_legacy_mem: |
| 329 | PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc | ||
| 330 | { | ||
| 331 | struct parse_events_evlist *data = _data; | ||
| 332 | struct list_head *list; | ||
| 333 | |||
| 334 | ALLOC_LIST(list); | ||
| 335 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
| 336 | (void *) $2, $6, $4)); | ||
| 337 | $$ = list; | ||
| 338 | } | ||
| 339 | | | ||
| 340 | PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc | ||
| 341 | { | ||
| 342 | struct parse_events_evlist *data = _data; | ||
| 343 | struct list_head *list; | ||
| 344 | |||
| 345 | ALLOC_LIST(list); | ||
| 346 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
| 347 | (void *) $2, NULL, $4)); | ||
| 348 | $$ = list; | ||
| 349 | } | ||
| 350 | | | ||
| 329 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | 351 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc |
| 330 | { | 352 | { |
| 331 | struct parse_events_evlist *data = _data; | 353 | struct parse_events_evlist *data = _data; |
| @@ -333,7 +355,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | |||
| 333 | 355 | ||
| 334 | ALLOC_LIST(list); | 356 | ALLOC_LIST(list); |
| 335 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | 357 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, |
| 336 | (void *) $2, $4)); | 358 | (void *) $2, $4, 0)); |
| 337 | $$ = list; | 359 | $$ = list; |
| 338 | } | 360 | } |
| 339 | | | 361 | | |
| @@ -344,7 +366,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
| 344 | 366 | ||
| 345 | ALLOC_LIST(list); | 367 | ALLOC_LIST(list); |
| 346 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | 368 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, |
| 347 | (void *) $2, NULL)); | 369 | (void *) $2, NULL, 0)); |
| 348 | $$ = list; | 370 | $$ = list; |
| 349 | } | 371 | } |
| 350 | 372 | ||
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index bf48092983c6..f62dee7bd924 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
| @@ -42,7 +42,26 @@ static int get_value(struct parse_opt_ctx_t *p, | |||
| 42 | return opterror(opt, "takes no value", flags); | 42 | return opterror(opt, "takes no value", flags); |
| 43 | if (unset && (opt->flags & PARSE_OPT_NONEG)) | 43 | if (unset && (opt->flags & PARSE_OPT_NONEG)) |
| 44 | return opterror(opt, "isn't available", flags); | 44 | return opterror(opt, "isn't available", flags); |
| 45 | 45 | if (opt->flags & PARSE_OPT_DISABLED) | |
| 46 | return opterror(opt, "is not usable", flags); | ||
| 47 | |||
| 48 | if (opt->flags & PARSE_OPT_EXCLUSIVE) { | ||
| 49 | if (p->excl_opt) { | ||
| 50 | char msg[128]; | ||
| 51 | |||
| 52 | if (((flags & OPT_SHORT) && p->excl_opt->short_name) || | ||
| 53 | p->excl_opt->long_name == NULL) { | ||
| 54 | scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'", | ||
| 55 | p->excl_opt->short_name); | ||
| 56 | } else { | ||
| 57 | scnprintf(msg, sizeof(msg), "cannot be used with %s", | ||
| 58 | p->excl_opt->long_name); | ||
| 59 | } | ||
| 60 | opterror(opt, msg, flags); | ||
| 61 | return -3; | ||
| 62 | } | ||
| 63 | p->excl_opt = opt; | ||
| 64 | } | ||
| 46 | if (!(flags & OPT_SHORT) && p->opt) { | 65 | if (!(flags & OPT_SHORT) && p->opt) { |
| 47 | switch (opt->type) { | 66 | switch (opt->type) { |
| 48 | case OPTION_CALLBACK: | 67 | case OPTION_CALLBACK: |
| @@ -343,13 +362,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
| 343 | const char * const usagestr[]) | 362 | const char * const usagestr[]) |
| 344 | { | 363 | { |
| 345 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); | 364 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); |
| 365 | int excl_short_opt = 1; | ||
| 366 | const char *arg; | ||
| 346 | 367 | ||
| 347 | /* we must reset ->opt, unknown short option leave it dangling */ | 368 | /* we must reset ->opt, unknown short option leave it dangling */ |
| 348 | ctx->opt = NULL; | 369 | ctx->opt = NULL; |
| 349 | 370 | ||
| 350 | for (; ctx->argc; ctx->argc--, ctx->argv++) { | 371 | for (; ctx->argc; ctx->argc--, ctx->argv++) { |
| 351 | const char *arg = ctx->argv[0]; | 372 | arg = ctx->argv[0]; |
| 352 | |||
| 353 | if (*arg != '-' || !arg[1]) { | 373 | if (*arg != '-' || !arg[1]) { |
| 354 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) | 374 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) |
| 355 | break; | 375 | break; |
| @@ -358,19 +378,21 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
| 358 | } | 378 | } |
| 359 | 379 | ||
| 360 | if (arg[1] != '-') { | 380 | if (arg[1] != '-') { |
| 361 | ctx->opt = arg + 1; | 381 | ctx->opt = ++arg; |
| 362 | if (internal_help && *ctx->opt == 'h') | 382 | if (internal_help && *ctx->opt == 'h') |
| 363 | return usage_with_options_internal(usagestr, options, 0); | 383 | return usage_with_options_internal(usagestr, options, 0); |
| 364 | switch (parse_short_opt(ctx, options)) { | 384 | switch (parse_short_opt(ctx, options)) { |
| 365 | case -1: | 385 | case -1: |
| 366 | return parse_options_usage(usagestr, options, arg + 1, 1); | 386 | return parse_options_usage(usagestr, options, arg, 1); |
| 367 | case -2: | 387 | case -2: |
| 368 | goto unknown; | 388 | goto unknown; |
| 389 | case -3: | ||
| 390 | goto exclusive; | ||
| 369 | default: | 391 | default: |
| 370 | break; | 392 | break; |
| 371 | } | 393 | } |
| 372 | if (ctx->opt) | 394 | if (ctx->opt) |
| 373 | check_typos(arg + 1, options); | 395 | check_typos(arg, options); |
| 374 | while (ctx->opt) { | 396 | while (ctx->opt) { |
| 375 | if (internal_help && *ctx->opt == 'h') | 397 | if (internal_help && *ctx->opt == 'h') |
| 376 | return usage_with_options_internal(usagestr, options, 0); | 398 | return usage_with_options_internal(usagestr, options, 0); |
| @@ -387,6 +409,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
| 387 | ctx->argv[0] = strdup(ctx->opt - 1); | 409 | ctx->argv[0] = strdup(ctx->opt - 1); |
| 388 | *(char *)ctx->argv[0] = '-'; | 410 | *(char *)ctx->argv[0] = '-'; |
| 389 | goto unknown; | 411 | goto unknown; |
| 412 | case -3: | ||
| 413 | goto exclusive; | ||
| 390 | default: | 414 | default: |
| 391 | break; | 415 | break; |
| 392 | } | 416 | } |
| @@ -402,19 +426,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
| 402 | break; | 426 | break; |
| 403 | } | 427 | } |
| 404 | 428 | ||
| 405 | if (internal_help && !strcmp(arg + 2, "help-all")) | 429 | arg += 2; |
| 430 | if (internal_help && !strcmp(arg, "help-all")) | ||
| 406 | return usage_with_options_internal(usagestr, options, 1); | 431 | return usage_with_options_internal(usagestr, options, 1); |
| 407 | if (internal_help && !strcmp(arg + 2, "help")) | 432 | if (internal_help && !strcmp(arg, "help")) |
| 408 | return usage_with_options_internal(usagestr, options, 0); | 433 | return usage_with_options_internal(usagestr, options, 0); |
| 409 | if (!strcmp(arg + 2, "list-opts")) | 434 | if (!strcmp(arg, "list-opts")) |
| 410 | return PARSE_OPT_LIST_OPTS; | 435 | return PARSE_OPT_LIST_OPTS; |
| 411 | if (!strcmp(arg + 2, "list-cmds")) | 436 | if (!strcmp(arg, "list-cmds")) |
| 412 | return PARSE_OPT_LIST_SUBCMDS; | 437 | return PARSE_OPT_LIST_SUBCMDS; |
| 413 | switch (parse_long_opt(ctx, arg + 2, options)) { | 438 | switch (parse_long_opt(ctx, arg, options)) { |
| 414 | case -1: | 439 | case -1: |
| 415 | return parse_options_usage(usagestr, options, arg + 2, 0); | 440 | return parse_options_usage(usagestr, options, arg, 0); |
| 416 | case -2: | 441 | case -2: |
| 417 | goto unknown; | 442 | goto unknown; |
| 443 | case -3: | ||
| 444 | excl_short_opt = 0; | ||
| 445 | goto exclusive; | ||
| 418 | default: | 446 | default: |
| 419 | break; | 447 | break; |
| 420 | } | 448 | } |
| @@ -426,6 +454,17 @@ unknown: | |||
| 426 | ctx->opt = NULL; | 454 | ctx->opt = NULL; |
| 427 | } | 455 | } |
| 428 | return PARSE_OPT_DONE; | 456 | return PARSE_OPT_DONE; |
| 457 | |||
| 458 | exclusive: | ||
| 459 | parse_options_usage(usagestr, options, arg, excl_short_opt); | ||
| 460 | if ((excl_short_opt && ctx->excl_opt->short_name) || | ||
| 461 | ctx->excl_opt->long_name == NULL) { | ||
| 462 | char opt = ctx->excl_opt->short_name; | ||
| 463 | parse_options_usage(NULL, options, &opt, 1); | ||
| 464 | } else { | ||
| 465 | parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); | ||
| 466 | } | ||
| 467 | return PARSE_OPT_HELP; | ||
| 429 | } | 468 | } |
| 430 | 469 | ||
| 431 | int parse_options_end(struct parse_opt_ctx_t *ctx) | 470 | int parse_options_end(struct parse_opt_ctx_t *ctx) |
| @@ -509,6 +548,8 @@ static void print_option_help(const struct option *opts, int full) | |||
| 509 | } | 548 | } |
| 510 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | 549 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) |
| 511 | return; | 550 | return; |
| 551 | if (opts->flags & PARSE_OPT_DISABLED) | ||
| 552 | return; | ||
| 512 | 553 | ||
| 513 | pos = fprintf(stderr, " "); | 554 | pos = fprintf(stderr, " "); |
| 514 | if (opts->short_name) | 555 | if (opts->short_name) |
| @@ -679,3 +720,16 @@ int parse_opt_verbosity_cb(const struct option *opt, | |||
| 679 | } | 720 | } |
| 680 | return 0; | 721 | return 0; |
| 681 | } | 722 | } |
| 723 | |||
| 724 | void set_option_flag(struct option *opts, int shortopt, const char *longopt, | ||
| 725 | int flag) | ||
| 726 | { | ||
| 727 | for (; opts->type != OPTION_END; opts++) { | ||
| 728 | if ((shortopt && opts->short_name == shortopt) || | ||
| 729 | (opts->long_name && longopt && | ||
| 730 | !strcmp(opts->long_name, longopt))) { | ||
| 731 | opts->flags |= flag; | ||
| 732 | break; | ||
| 733 | } | ||
| 734 | } | ||
| 735 | } | ||
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b59ba858e73d..97b153fb4999 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
| @@ -38,6 +38,8 @@ enum parse_opt_option_flags { | |||
| 38 | PARSE_OPT_NONEG = 4, | 38 | PARSE_OPT_NONEG = 4, |
| 39 | PARSE_OPT_HIDDEN = 8, | 39 | PARSE_OPT_HIDDEN = 8, |
| 40 | PARSE_OPT_LASTARG_DEFAULT = 16, | 40 | PARSE_OPT_LASTARG_DEFAULT = 16, |
| 41 | PARSE_OPT_DISABLED = 32, | ||
| 42 | PARSE_OPT_EXCLUSIVE = 64, | ||
| 41 | }; | 43 | }; |
| 42 | 44 | ||
| 43 | struct option; | 45 | struct option; |
| @@ -173,6 +175,7 @@ struct parse_opt_ctx_t { | |||
| 173 | const char **out; | 175 | const char **out; |
| 174 | int argc, cpidx; | 176 | int argc, cpidx; |
| 175 | const char *opt; | 177 | const char *opt; |
| 178 | const struct option *excl_opt; | ||
| 176 | int flags; | 179 | int flags; |
| 177 | }; | 180 | }; |
| 178 | 181 | ||
| @@ -211,4 +214,5 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | |||
| 211 | 214 | ||
| 212 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | 215 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); |
| 213 | 216 | ||
| 217 | void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); | ||
| 214 | #endif /* __PERF_PARSE_OPTIONS_H */ | 218 | #endif /* __PERF_PARSE_OPTIONS_H */ |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index e243ad962a4d..881b75490533 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
| @@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
| 747 | 747 | ||
| 748 | pmu = NULL; | 748 | pmu = NULL; |
| 749 | len = 0; | 749 | len = 0; |
| 750 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | 750 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
| 751 | list_for_each_entry(alias, &pmu->aliases, list) | 751 | list_for_each_entry(alias, &pmu->aliases, list) |
| 752 | len++; | 752 | len++; |
| 753 | aliases = malloc(sizeof(char *) * len); | 753 | if (pmu->selectable) |
| 754 | len++; | ||
| 755 | } | ||
| 756 | aliases = zalloc(sizeof(char *) * len); | ||
| 754 | if (!aliases) | 757 | if (!aliases) |
| 755 | return; | 758 | goto out_enomem; |
| 756 | pmu = NULL; | 759 | pmu = NULL; |
| 757 | j = 0; | 760 | j = 0; |
| 758 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | 761 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
| 759 | list_for_each_entry(alias, &pmu->aliases, list) { | 762 | list_for_each_entry(alias, &pmu->aliases, list) { |
| 760 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | 763 | char *name = format_alias(buf, sizeof(buf), pmu, alias); |
| 761 | bool is_cpu = !strcmp(pmu->name, "cpu"); | 764 | bool is_cpu = !strcmp(pmu->name, "cpu"); |
| @@ -765,13 +768,23 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
| 765 | (!is_cpu && strglobmatch(alias->name, | 768 | (!is_cpu && strglobmatch(alias->name, |
| 766 | event_glob)))) | 769 | event_glob)))) |
| 767 | continue; | 770 | continue; |
| 768 | aliases[j] = name; | 771 | |
| 769 | if (is_cpu && !name_only) | 772 | if (is_cpu && !name_only) |
| 770 | aliases[j] = format_alias_or(buf, sizeof(buf), | 773 | name = format_alias_or(buf, sizeof(buf), pmu, alias); |
| 771 | pmu, alias); | 774 | |
| 772 | aliases[j] = strdup(aliases[j]); | 775 | aliases[j] = strdup(name); |
| 776 | if (aliases[j] == NULL) | ||
| 777 | goto out_enomem; | ||
| 773 | j++; | 778 | j++; |
| 774 | } | 779 | } |
| 780 | if (pmu->selectable) { | ||
| 781 | char *s; | ||
| 782 | if (asprintf(&s, "%s//", pmu->name) < 0) | ||
| 783 | goto out_enomem; | ||
| 784 | aliases[j] = s; | ||
| 785 | j++; | ||
| 786 | } | ||
| 787 | } | ||
| 775 | len = j; | 788 | len = j; |
| 776 | qsort(aliases, len, sizeof(char *), cmp_string); | 789 | qsort(aliases, len, sizeof(char *), cmp_string); |
| 777 | for (j = 0; j < len; j++) { | 790 | for (j = 0; j < len; j++) { |
| @@ -780,12 +793,20 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
| 780 | continue; | 793 | continue; |
| 781 | } | 794 | } |
| 782 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | 795 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); |
| 783 | zfree(&aliases[j]); | ||
| 784 | printed++; | 796 | printed++; |
| 785 | } | 797 | } |
| 786 | if (printed) | 798 | if (printed) |
| 787 | printf("\n"); | 799 | printf("\n"); |
| 788 | free(aliases); | 800 | out_free: |
| 801 | for (j = 0; j < len; j++) | ||
| 802 | zfree(&aliases[j]); | ||
| 803 | zfree(&aliases); | ||
| 804 | return; | ||
| 805 | |||
| 806 | out_enomem: | ||
| 807 | printf("FATAL: not enough memory to print PMU events\n"); | ||
| 808 | if (aliases) | ||
| 809 | goto out_free; | ||
| 789 | } | 810 | } |
| 790 | 811 | ||
| 791 | bool pmu_have_event(const char *pname, const char *name) | 812 | bool pmu_have_event(const char *pname, const char *name) |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index fe9dfbee8eed..8092de78e818 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
| @@ -18,6 +18,7 @@ struct perf_event_attr; | |||
| 18 | struct perf_pmu { | 18 | struct perf_pmu { |
| 19 | char *name; | 19 | char *name; |
| 20 | __u32 type; | 20 | __u32 type; |
| 21 | bool selectable; | ||
| 21 | struct perf_event_attr *default_config; | 22 | struct perf_event_attr *default_config; |
| 22 | struct cpu_map *cpus; | 23 | struct cpu_map *cpus; |
| 23 | struct list_head format; /* HEAD struct perf_pmu_format -> list */ | 24 | struct list_head format; /* HEAD struct perf_pmu_format -> list */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index c150ca4343eb..28eb1417cb2a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -1910,21 +1910,21 @@ static int show_perf_probe_event(struct perf_probe_event *pev, | |||
| 1910 | if (ret < 0) | 1910 | if (ret < 0) |
| 1911 | return ret; | 1911 | return ret; |
| 1912 | 1912 | ||
| 1913 | printf(" %-20s (on %s", buf, place); | 1913 | pr_info(" %-20s (on %s", buf, place); |
| 1914 | if (module) | 1914 | if (module) |
| 1915 | printf(" in %s", module); | 1915 | pr_info(" in %s", module); |
| 1916 | 1916 | ||
| 1917 | if (pev->nargs > 0) { | 1917 | if (pev->nargs > 0) { |
| 1918 | printf(" with"); | 1918 | pr_info(" with"); |
| 1919 | for (i = 0; i < pev->nargs; i++) { | 1919 | for (i = 0; i < pev->nargs; i++) { |
| 1920 | ret = synthesize_perf_probe_arg(&pev->args[i], | 1920 | ret = synthesize_perf_probe_arg(&pev->args[i], |
| 1921 | buf, 128); | 1921 | buf, 128); |
| 1922 | if (ret < 0) | 1922 | if (ret < 0) |
| 1923 | break; | 1923 | break; |
| 1924 | printf(" %s", buf); | 1924 | pr_info(" %s", buf); |
| 1925 | } | 1925 | } |
| 1926 | } | 1926 | } |
| 1927 | printf(")\n"); | 1927 | pr_info(")\n"); |
| 1928 | free(place); | 1928 | free(place); |
| 1929 | return ret; | 1929 | return ret; |
| 1930 | } | 1930 | } |
| @@ -2124,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
| 2124 | } | 2124 | } |
| 2125 | 2125 | ||
| 2126 | ret = 0; | 2126 | ret = 0; |
| 2127 | printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); | 2127 | pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); |
| 2128 | for (i = 0; i < ntevs; i++) { | 2128 | for (i = 0; i < ntevs; i++) { |
| 2129 | tev = &tevs[i]; | 2129 | tev = &tevs[i]; |
| 2130 | if (pev->event) | 2130 | if (pev->event) |
| @@ -2179,8 +2179,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
| 2179 | 2179 | ||
| 2180 | if (ret >= 0) { | 2180 | if (ret >= 0) { |
| 2181 | /* Show how to use the event. */ | 2181 | /* Show how to use the event. */ |
| 2182 | printf("\nYou can now use it in all perf tools, such as:\n\n"); | 2182 | pr_info("\nYou can now use it in all perf tools, such as:\n\n"); |
| 2183 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 2183 | pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
| 2184 | tev->event); | 2184 | tev->event); |
| 2185 | } | 2185 | } |
| 2186 | 2186 | ||
| @@ -2444,7 +2444,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
| 2444 | goto error; | 2444 | goto error; |
| 2445 | } | 2445 | } |
| 2446 | 2446 | ||
| 2447 | printf("Removed event: %s\n", ent->s); | 2447 | pr_info("Removed event: %s\n", ent->s); |
| 2448 | return 0; | 2448 | return 0; |
| 2449 | error: | 2449 | error: |
| 2450 | pr_warning("Failed to delete event: %s\n", | 2450 | pr_warning("Failed to delete event: %s\n", |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 0a01bac4ce02..22ebc46226e7 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <string.h> | 24 | #include <string.h> |
| 25 | #include <ctype.h> | 25 | #include <ctype.h> |
| 26 | #include <errno.h> | 26 | #include <errno.h> |
| 27 | #include <linux/bitmap.h> | ||
| 27 | 28 | ||
| 28 | #include "../util.h" | 29 | #include "../util.h" |
| 29 | #include <EXTERN.h> | 30 | #include <EXTERN.h> |
| @@ -57,7 +58,7 @@ INTERP my_perl; | |||
| 57 | #define FTRACE_MAX_EVENT \ | 58 | #define FTRACE_MAX_EVENT \ |
| 58 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 59 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
| 59 | 60 | ||
| 60 | struct event_format *events[FTRACE_MAX_EVENT]; | 61 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
| 61 | 62 | ||
| 62 | extern struct scripting_context *scripting_context; | 63 | extern struct scripting_context *scripting_context; |
| 63 | 64 | ||
| @@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event, | |||
| 238 | define_event_symbols(event, ev_name, args->next); | 239 | define_event_symbols(event, ev_name, args->next); |
| 239 | } | 240 | } |
| 240 | 241 | ||
| 241 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
| 242 | { | ||
| 243 | static char ev_name[256]; | ||
| 244 | struct event_format *event; | ||
| 245 | int type = evsel->attr.config; | ||
| 246 | |||
| 247 | if (events[type]) | ||
| 248 | return events[type]; | ||
| 249 | |||
| 250 | events[type] = event = evsel->tp_format; | ||
| 251 | if (!event) | ||
| 252 | return NULL; | ||
| 253 | |||
| 254 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
| 255 | |||
| 256 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
| 257 | |||
| 258 | return event; | ||
| 259 | } | ||
| 260 | |||
| 261 | static void perl_process_tracepoint(struct perf_sample *sample, | 242 | static void perl_process_tracepoint(struct perf_sample *sample, |
| 262 | struct perf_evsel *evsel, | 243 | struct perf_evsel *evsel, |
| 263 | struct thread *thread) | 244 | struct thread *thread) |
| 264 | { | 245 | { |
| 246 | struct event_format *event = evsel->tp_format; | ||
| 265 | struct format_field *field; | 247 | struct format_field *field; |
| 266 | static char handler[256]; | 248 | static char handler[256]; |
| 267 | unsigned long long val; | 249 | unsigned long long val; |
| 268 | unsigned long s, ns; | 250 | unsigned long s, ns; |
| 269 | struct event_format *event; | ||
| 270 | int pid; | 251 | int pid; |
| 271 | int cpu = sample->cpu; | 252 | int cpu = sample->cpu; |
| 272 | void *data = sample->raw_data; | 253 | void *data = sample->raw_data; |
| @@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
| 278 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 259 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
| 279 | return; | 260 | return; |
| 280 | 261 | ||
| 281 | event = find_cache_event(evsel); | ||
| 282 | if (!event) | 262 | if (!event) |
| 283 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); | 263 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
| 284 | 264 | ||
| @@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
| 286 | 266 | ||
| 287 | sprintf(handler, "%s::%s", event->system, event->name); | 267 | sprintf(handler, "%s::%s", event->system, event->name); |
| 288 | 268 | ||
| 269 | if (!test_and_set_bit(event->id, events_defined)) | ||
| 270 | define_event_symbols(event, handler, event->print_fmt.args); | ||
| 271 | |||
| 289 | s = nsecs / NSECS_PER_SEC; | 272 | s = nsecs / NSECS_PER_SEC; |
| 290 | ns = nsecs - s * NSECS_PER_SEC; | 273 | ns = nsecs - s * NSECS_PER_SEC; |
| 291 | 274 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 496f21cadd97..d808a328f4dc 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
| @@ -24,7 +24,9 @@ | |||
| 24 | #include <stdio.h> | 24 | #include <stdio.h> |
| 25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
| 26 | #include <string.h> | 26 | #include <string.h> |
| 27 | #include <stdbool.h> | ||
| 27 | #include <errno.h> | 28 | #include <errno.h> |
| 29 | #include <linux/bitmap.h> | ||
| 28 | 30 | ||
| 29 | #include "../../perf.h" | 31 | #include "../../perf.h" |
| 30 | #include "../debug.h" | 32 | #include "../debug.h" |
| @@ -33,6 +35,10 @@ | |||
| 33 | #include "../util.h" | 35 | #include "../util.h" |
| 34 | #include "../event.h" | 36 | #include "../event.h" |
| 35 | #include "../thread.h" | 37 | #include "../thread.h" |
| 38 | #include "../comm.h" | ||
| 39 | #include "../machine.h" | ||
| 40 | #include "../db-export.h" | ||
| 41 | #include "../thread-stack.h" | ||
| 36 | #include "../trace-event.h" | 42 | #include "../trace-event.h" |
| 37 | #include "../machine.h" | 43 | #include "../machine.h" |
| 38 | 44 | ||
| @@ -41,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void); | |||
| 41 | #define FTRACE_MAX_EVENT \ | 47 | #define FTRACE_MAX_EVENT \ |
| 42 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 48 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
| 43 | 49 | ||
| 44 | struct event_format *events[FTRACE_MAX_EVENT]; | 50 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
| 45 | 51 | ||
| 46 | #define MAX_FIELDS 64 | 52 | #define MAX_FIELDS 64 |
| 47 | #define N_COMMON_FIELDS 7 | 53 | #define N_COMMON_FIELDS 7 |
| @@ -53,6 +59,24 @@ static int zero_flag_atom; | |||
| 53 | 59 | ||
| 54 | static PyObject *main_module, *main_dict; | 60 | static PyObject *main_module, *main_dict; |
| 55 | 61 | ||
| 62 | struct tables { | ||
| 63 | struct db_export dbe; | ||
| 64 | PyObject *evsel_handler; | ||
| 65 | PyObject *machine_handler; | ||
| 66 | PyObject *thread_handler; | ||
| 67 | PyObject *comm_handler; | ||
| 68 | PyObject *comm_thread_handler; | ||
| 69 | PyObject *dso_handler; | ||
| 70 | PyObject *symbol_handler; | ||
| 71 | PyObject *branch_type_handler; | ||
| 72 | PyObject *sample_handler; | ||
| 73 | PyObject *call_path_handler; | ||
| 74 | PyObject *call_return_handler; | ||
| 75 | bool db_export_mode; | ||
| 76 | }; | ||
| 77 | |||
| 78 | static struct tables tables_global; | ||
| 79 | |||
| 56 | static void handler_call_die(const char *handler_name) NORETURN; | 80 | static void handler_call_die(const char *handler_name) NORETURN; |
| 57 | static void handler_call_die(const char *handler_name) | 81 | static void handler_call_die(const char *handler_name) |
| 58 | { | 82 | { |
| @@ -232,31 +256,6 @@ static void define_event_symbols(struct event_format *event, | |||
| 232 | define_event_symbols(event, ev_name, args->next); | 256 | define_event_symbols(event, ev_name, args->next); |
| 233 | } | 257 | } |
| 234 | 258 | ||
| 235 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
| 236 | { | ||
| 237 | static char ev_name[256]; | ||
| 238 | struct event_format *event; | ||
| 239 | int type = evsel->attr.config; | ||
| 240 | |||
| 241 | /* | ||
| 242 | * XXX: Do we really need to cache this since now we have evsel->tp_format | ||
| 243 | * cached already? Need to re-read this "cache" routine that as well calls | ||
| 244 | * define_event_symbols() :-\ | ||
| 245 | */ | ||
| 246 | if (events[type]) | ||
| 247 | return events[type]; | ||
| 248 | |||
| 249 | events[type] = event = evsel->tp_format; | ||
| 250 | if (!event) | ||
| 251 | return NULL; | ||
| 252 | |||
| 253 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
| 254 | |||
| 255 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
| 256 | |||
| 257 | return event; | ||
| 258 | } | ||
| 259 | |||
| 260 | static PyObject *get_field_numeric_entry(struct event_format *event, | 259 | static PyObject *get_field_numeric_entry(struct event_format *event, |
| 261 | struct format_field *field, void *data) | 260 | struct format_field *field, void *data) |
| 262 | { | 261 | { |
| @@ -312,9 +311,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample, | |||
| 312 | if (!symbol_conf.use_callchain || !sample->callchain) | 311 | if (!symbol_conf.use_callchain || !sample->callchain) |
| 313 | goto exit; | 312 | goto exit; |
| 314 | 313 | ||
| 315 | if (machine__resolve_callchain(al->machine, evsel, al->thread, | 314 | if (thread__resolve_callchain(al->thread, evsel, |
| 316 | sample, NULL, NULL, | 315 | sample, NULL, NULL, |
| 317 | PERF_MAX_STACK_DEPTH) != 0) { | 316 | PERF_MAX_STACK_DEPTH) != 0) { |
| 318 | pr_err("Failed to resolve callchain. Skipping\n"); | 317 | pr_err("Failed to resolve callchain. Skipping\n"); |
| 319 | goto exit; | 318 | goto exit; |
| 320 | } | 319 | } |
| @@ -380,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 380 | struct thread *thread, | 379 | struct thread *thread, |
| 381 | struct addr_location *al) | 380 | struct addr_location *al) |
| 382 | { | 381 | { |
| 382 | struct event_format *event = evsel->tp_format; | ||
| 383 | PyObject *handler, *context, *t, *obj, *callchain; | 383 | PyObject *handler, *context, *t, *obj, *callchain; |
| 384 | PyObject *dict = NULL; | 384 | PyObject *dict = NULL; |
| 385 | static char handler_name[256]; | 385 | static char handler_name[256]; |
| 386 | struct format_field *field; | 386 | struct format_field *field; |
| 387 | unsigned long s, ns; | 387 | unsigned long s, ns; |
| 388 | struct event_format *event; | ||
| 389 | unsigned n = 0; | 388 | unsigned n = 0; |
| 390 | int pid; | 389 | int pid; |
| 391 | int cpu = sample->cpu; | 390 | int cpu = sample->cpu; |
| @@ -397,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 397 | if (!t) | 396 | if (!t) |
| 398 | Py_FatalError("couldn't create Python tuple"); | 397 | Py_FatalError("couldn't create Python tuple"); |
| 399 | 398 | ||
| 400 | event = find_cache_event(evsel); | ||
| 401 | if (!event) | 399 | if (!event) |
| 402 | die("ug! no event found for type %d", (int)evsel->attr.config); | 400 | die("ug! no event found for type %d", (int)evsel->attr.config); |
| 403 | 401 | ||
| @@ -405,6 +403,9 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 405 | 403 | ||
| 406 | sprintf(handler_name, "%s__%s", event->system, event->name); | 404 | sprintf(handler_name, "%s__%s", event->system, event->name); |
| 407 | 405 | ||
| 406 | if (!test_and_set_bit(event->id, events_defined)) | ||
| 407 | define_event_symbols(event, handler_name, event->print_fmt.args); | ||
| 408 | |||
| 408 | handler = get_handler(handler_name); | 409 | handler = get_handler(handler_name); |
| 409 | if (!handler) { | 410 | if (!handler) { |
| 410 | dict = PyDict_New(); | 411 | dict = PyDict_New(); |
| @@ -475,6 +476,289 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 475 | Py_DECREF(t); | 476 | Py_DECREF(t); |
| 476 | } | 477 | } |
| 477 | 478 | ||
| 479 | static PyObject *tuple_new(unsigned int sz) | ||
| 480 | { | ||
| 481 | PyObject *t; | ||
| 482 | |||
| 483 | t = PyTuple_New(sz); | ||
| 484 | if (!t) | ||
| 485 | Py_FatalError("couldn't create Python tuple"); | ||
| 486 | return t; | ||
| 487 | } | ||
| 488 | |||
| 489 | static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val) | ||
| 490 | { | ||
| 491 | #if BITS_PER_LONG == 64 | ||
| 492 | return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); | ||
| 493 | #endif | ||
| 494 | #if BITS_PER_LONG == 32 | ||
| 495 | return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val)); | ||
| 496 | #endif | ||
| 497 | } | ||
| 498 | |||
| 499 | static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val) | ||
| 500 | { | ||
| 501 | return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); | ||
| 502 | } | ||
| 503 | |||
| 504 | static int tuple_set_string(PyObject *t, unsigned int pos, const char *s) | ||
| 505 | { | ||
| 506 | return PyTuple_SetItem(t, pos, PyString_FromString(s)); | ||
| 507 | } | ||
| 508 | |||
| 509 | static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel) | ||
| 510 | { | ||
| 511 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 512 | PyObject *t; | ||
| 513 | |||
| 514 | t = tuple_new(2); | ||
| 515 | |||
| 516 | tuple_set_u64(t, 0, evsel->db_id); | ||
| 517 | tuple_set_string(t, 1, perf_evsel__name(evsel)); | ||
| 518 | |||
| 519 | call_object(tables->evsel_handler, t, "evsel_table"); | ||
| 520 | |||
| 521 | Py_DECREF(t); | ||
| 522 | |||
| 523 | return 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | static int python_export_machine(struct db_export *dbe, | ||
| 527 | struct machine *machine) | ||
| 528 | { | ||
| 529 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 530 | PyObject *t; | ||
| 531 | |||
| 532 | t = tuple_new(3); | ||
| 533 | |||
| 534 | tuple_set_u64(t, 0, machine->db_id); | ||
| 535 | tuple_set_s32(t, 1, machine->pid); | ||
| 536 | tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : ""); | ||
| 537 | |||
| 538 | call_object(tables->machine_handler, t, "machine_table"); | ||
| 539 | |||
| 540 | Py_DECREF(t); | ||
| 541 | |||
| 542 | return 0; | ||
| 543 | } | ||
| 544 | |||
| 545 | static int python_export_thread(struct db_export *dbe, struct thread *thread, | ||
| 546 | u64 main_thread_db_id, struct machine *machine) | ||
| 547 | { | ||
| 548 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 549 | PyObject *t; | ||
| 550 | |||
| 551 | t = tuple_new(5); | ||
| 552 | |||
| 553 | tuple_set_u64(t, 0, thread->db_id); | ||
| 554 | tuple_set_u64(t, 1, machine->db_id); | ||
| 555 | tuple_set_u64(t, 2, main_thread_db_id); | ||
| 556 | tuple_set_s32(t, 3, thread->pid_); | ||
| 557 | tuple_set_s32(t, 4, thread->tid); | ||
| 558 | |||
| 559 | call_object(tables->thread_handler, t, "thread_table"); | ||
| 560 | |||
| 561 | Py_DECREF(t); | ||
| 562 | |||
| 563 | return 0; | ||
| 564 | } | ||
| 565 | |||
| 566 | static int python_export_comm(struct db_export *dbe, struct comm *comm) | ||
| 567 | { | ||
| 568 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 569 | PyObject *t; | ||
| 570 | |||
| 571 | t = tuple_new(2); | ||
| 572 | |||
| 573 | tuple_set_u64(t, 0, comm->db_id); | ||
| 574 | tuple_set_string(t, 1, comm__str(comm)); | ||
| 575 | |||
| 576 | call_object(tables->comm_handler, t, "comm_table"); | ||
| 577 | |||
| 578 | Py_DECREF(t); | ||
| 579 | |||
| 580 | return 0; | ||
| 581 | } | ||
| 582 | |||
| 583 | static int python_export_comm_thread(struct db_export *dbe, u64 db_id, | ||
| 584 | struct comm *comm, struct thread *thread) | ||
| 585 | { | ||
| 586 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 587 | PyObject *t; | ||
| 588 | |||
| 589 | t = tuple_new(3); | ||
| 590 | |||
| 591 | tuple_set_u64(t, 0, db_id); | ||
| 592 | tuple_set_u64(t, 1, comm->db_id); | ||
| 593 | tuple_set_u64(t, 2, thread->db_id); | ||
| 594 | |||
| 595 | call_object(tables->comm_thread_handler, t, "comm_thread_table"); | ||
| 596 | |||
| 597 | Py_DECREF(t); | ||
| 598 | |||
| 599 | return 0; | ||
| 600 | } | ||
| 601 | |||
| 602 | static int python_export_dso(struct db_export *dbe, struct dso *dso, | ||
| 603 | struct machine *machine) | ||
| 604 | { | ||
| 605 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 606 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 607 | PyObject *t; | ||
| 608 | |||
| 609 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
| 610 | |||
| 611 | t = tuple_new(5); | ||
| 612 | |||
| 613 | tuple_set_u64(t, 0, dso->db_id); | ||
| 614 | tuple_set_u64(t, 1, machine->db_id); | ||
| 615 | tuple_set_string(t, 2, dso->short_name); | ||
| 616 | tuple_set_string(t, 3, dso->long_name); | ||
| 617 | tuple_set_string(t, 4, sbuild_id); | ||
| 618 | |||
| 619 | call_object(tables->dso_handler, t, "dso_table"); | ||
| 620 | |||
| 621 | Py_DECREF(t); | ||
| 622 | |||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | static int python_export_symbol(struct db_export *dbe, struct symbol *sym, | ||
| 627 | struct dso *dso) | ||
| 628 | { | ||
| 629 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 630 | u64 *sym_db_id = symbol__priv(sym); | ||
| 631 | PyObject *t; | ||
| 632 | |||
| 633 | t = tuple_new(6); | ||
| 634 | |||
| 635 | tuple_set_u64(t, 0, *sym_db_id); | ||
| 636 | tuple_set_u64(t, 1, dso->db_id); | ||
| 637 | tuple_set_u64(t, 2, sym->start); | ||
| 638 | tuple_set_u64(t, 3, sym->end); | ||
| 639 | tuple_set_s32(t, 4, sym->binding); | ||
| 640 | tuple_set_string(t, 5, sym->name); | ||
| 641 | |||
| 642 | call_object(tables->symbol_handler, t, "symbol_table"); | ||
| 643 | |||
| 644 | Py_DECREF(t); | ||
| 645 | |||
| 646 | return 0; | ||
| 647 | } | ||
| 648 | |||
| 649 | static int python_export_branch_type(struct db_export *dbe, u32 branch_type, | ||
| 650 | const char *name) | ||
| 651 | { | ||
| 652 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 653 | PyObject *t; | ||
| 654 | |||
| 655 | t = tuple_new(2); | ||
| 656 | |||
| 657 | tuple_set_s32(t, 0, branch_type); | ||
| 658 | tuple_set_string(t, 1, name); | ||
| 659 | |||
| 660 | call_object(tables->branch_type_handler, t, "branch_type_table"); | ||
| 661 | |||
| 662 | Py_DECREF(t); | ||
| 663 | |||
| 664 | return 0; | ||
| 665 | } | ||
| 666 | |||
| 667 | static int python_export_sample(struct db_export *dbe, | ||
| 668 | struct export_sample *es) | ||
| 669 | { | ||
| 670 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 671 | PyObject *t; | ||
| 672 | |||
| 673 | t = tuple_new(21); | ||
| 674 | |||
| 675 | tuple_set_u64(t, 0, es->db_id); | ||
| 676 | tuple_set_u64(t, 1, es->evsel->db_id); | ||
| 677 | tuple_set_u64(t, 2, es->al->machine->db_id); | ||
| 678 | tuple_set_u64(t, 3, es->thread->db_id); | ||
| 679 | tuple_set_u64(t, 4, es->comm_db_id); | ||
| 680 | tuple_set_u64(t, 5, es->dso_db_id); | ||
| 681 | tuple_set_u64(t, 6, es->sym_db_id); | ||
| 682 | tuple_set_u64(t, 7, es->offset); | ||
| 683 | tuple_set_u64(t, 8, es->sample->ip); | ||
| 684 | tuple_set_u64(t, 9, es->sample->time); | ||
| 685 | tuple_set_s32(t, 10, es->sample->cpu); | ||
| 686 | tuple_set_u64(t, 11, es->addr_dso_db_id); | ||
| 687 | tuple_set_u64(t, 12, es->addr_sym_db_id); | ||
| 688 | tuple_set_u64(t, 13, es->addr_offset); | ||
| 689 | tuple_set_u64(t, 14, es->sample->addr); | ||
| 690 | tuple_set_u64(t, 15, es->sample->period); | ||
| 691 | tuple_set_u64(t, 16, es->sample->weight); | ||
| 692 | tuple_set_u64(t, 17, es->sample->transaction); | ||
| 693 | tuple_set_u64(t, 18, es->sample->data_src); | ||
| 694 | tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK); | ||
| 695 | tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); | ||
| 696 | |||
| 697 | call_object(tables->sample_handler, t, "sample_table"); | ||
| 698 | |||
| 699 | Py_DECREF(t); | ||
| 700 | |||
| 701 | return 0; | ||
| 702 | } | ||
| 703 | |||
| 704 | static int python_export_call_path(struct db_export *dbe, struct call_path *cp) | ||
| 705 | { | ||
| 706 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 707 | PyObject *t; | ||
| 708 | u64 parent_db_id, sym_db_id; | ||
| 709 | |||
| 710 | parent_db_id = cp->parent ? cp->parent->db_id : 0; | ||
| 711 | sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; | ||
| 712 | |||
| 713 | t = tuple_new(4); | ||
| 714 | |||
| 715 | tuple_set_u64(t, 0, cp->db_id); | ||
| 716 | tuple_set_u64(t, 1, parent_db_id); | ||
| 717 | tuple_set_u64(t, 2, sym_db_id); | ||
| 718 | tuple_set_u64(t, 3, cp->ip); | ||
| 719 | |||
| 720 | call_object(tables->call_path_handler, t, "call_path_table"); | ||
| 721 | |||
| 722 | Py_DECREF(t); | ||
| 723 | |||
| 724 | return 0; | ||
| 725 | } | ||
| 726 | |||
| 727 | static int python_export_call_return(struct db_export *dbe, | ||
| 728 | struct call_return *cr) | ||
| 729 | { | ||
| 730 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
| 731 | u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; | ||
| 732 | PyObject *t; | ||
| 733 | |||
| 734 | t = tuple_new(11); | ||
| 735 | |||
| 736 | tuple_set_u64(t, 0, cr->db_id); | ||
| 737 | tuple_set_u64(t, 1, cr->thread->db_id); | ||
| 738 | tuple_set_u64(t, 2, comm_db_id); | ||
| 739 | tuple_set_u64(t, 3, cr->cp->db_id); | ||
| 740 | tuple_set_u64(t, 4, cr->call_time); | ||
| 741 | tuple_set_u64(t, 5, cr->return_time); | ||
| 742 | tuple_set_u64(t, 6, cr->branch_count); | ||
| 743 | tuple_set_u64(t, 7, cr->call_ref); | ||
| 744 | tuple_set_u64(t, 8, cr->return_ref); | ||
| 745 | tuple_set_u64(t, 9, cr->cp->parent->db_id); | ||
| 746 | tuple_set_s32(t, 10, cr->flags); | ||
| 747 | |||
| 748 | call_object(tables->call_return_handler, t, "call_return_table"); | ||
| 749 | |||
| 750 | Py_DECREF(t); | ||
| 751 | |||
| 752 | return 0; | ||
| 753 | } | ||
| 754 | |||
| 755 | static int python_process_call_return(struct call_return *cr, void *data) | ||
| 756 | { | ||
| 757 | struct db_export *dbe = data; | ||
| 758 | |||
| 759 | return db_export__call_return(dbe, cr); | ||
| 760 | } | ||
| 761 | |||
| 478 | static void python_process_general_event(struct perf_sample *sample, | 762 | static void python_process_general_event(struct perf_sample *sample, |
| 479 | struct perf_evsel *evsel, | 763 | struct perf_evsel *evsel, |
| 480 | struct thread *thread, | 764 | struct thread *thread, |
| @@ -551,19 +835,25 @@ exit: | |||
| 551 | Py_DECREF(t); | 835 | Py_DECREF(t); |
| 552 | } | 836 | } |
| 553 | 837 | ||
| 554 | static void python_process_event(union perf_event *event __maybe_unused, | 838 | static void python_process_event(union perf_event *event, |
| 555 | struct perf_sample *sample, | 839 | struct perf_sample *sample, |
| 556 | struct perf_evsel *evsel, | 840 | struct perf_evsel *evsel, |
| 557 | struct thread *thread, | 841 | struct thread *thread, |
| 558 | struct addr_location *al) | 842 | struct addr_location *al) |
| 559 | { | 843 | { |
| 844 | struct tables *tables = &tables_global; | ||
| 845 | |||
| 560 | switch (evsel->attr.type) { | 846 | switch (evsel->attr.type) { |
| 561 | case PERF_TYPE_TRACEPOINT: | 847 | case PERF_TYPE_TRACEPOINT: |
| 562 | python_process_tracepoint(sample, evsel, thread, al); | 848 | python_process_tracepoint(sample, evsel, thread, al); |
| 563 | break; | 849 | break; |
| 564 | /* Reserve for future process_hw/sw/raw APIs */ | 850 | /* Reserve for future process_hw/sw/raw APIs */ |
| 565 | default: | 851 | default: |
| 566 | python_process_general_event(sample, evsel, thread, al); | 852 | if (tables->db_export_mode) |
| 853 | db_export__sample(&tables->dbe, event, sample, evsel, | ||
| 854 | thread, al); | ||
| 855 | else | ||
| 856 | python_process_general_event(sample, evsel, thread, al); | ||
| 567 | } | 857 | } |
| 568 | } | 858 | } |
| 569 | 859 | ||
| @@ -589,11 +879,79 @@ error: | |||
| 589 | return -1; | 879 | return -1; |
| 590 | } | 880 | } |
| 591 | 881 | ||
| 882 | #define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \ | ||
| 883 | tables->handler_name = get_handler(#table_name); \ | ||
| 884 | if (tables->handler_name) \ | ||
| 885 | tables->dbe.export_ ## name = python_export_ ## name; \ | ||
| 886 | } while (0) | ||
| 887 | |||
| 888 | #define SET_TABLE_HANDLER(name) \ | ||
| 889 | SET_TABLE_HANDLER_(name, name ## _handler, name ## _table) | ||
| 890 | |||
| 891 | static void set_table_handlers(struct tables *tables) | ||
| 892 | { | ||
| 893 | const char *perf_db_export_mode = "perf_db_export_mode"; | ||
| 894 | const char *perf_db_export_calls = "perf_db_export_calls"; | ||
| 895 | PyObject *db_export_mode, *db_export_calls; | ||
| 896 | bool export_calls = false; | ||
| 897 | int ret; | ||
| 898 | |||
| 899 | memset(tables, 0, sizeof(struct tables)); | ||
| 900 | if (db_export__init(&tables->dbe)) | ||
| 901 | Py_FatalError("failed to initialize export"); | ||
| 902 | |||
| 903 | db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode); | ||
| 904 | if (!db_export_mode) | ||
| 905 | return; | ||
| 906 | |||
| 907 | ret = PyObject_IsTrue(db_export_mode); | ||
| 908 | if (ret == -1) | ||
| 909 | handler_call_die(perf_db_export_mode); | ||
| 910 | if (!ret) | ||
| 911 | return; | ||
| 912 | |||
| 913 | tables->dbe.crp = NULL; | ||
| 914 | db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); | ||
| 915 | if (db_export_calls) { | ||
| 916 | ret = PyObject_IsTrue(db_export_calls); | ||
| 917 | if (ret == -1) | ||
| 918 | handler_call_die(perf_db_export_calls); | ||
| 919 | export_calls = !!ret; | ||
| 920 | } | ||
| 921 | |||
| 922 | if (export_calls) { | ||
| 923 | tables->dbe.crp = | ||
| 924 | call_return_processor__new(python_process_call_return, | ||
| 925 | &tables->dbe); | ||
| 926 | if (!tables->dbe.crp) | ||
| 927 | Py_FatalError("failed to create calls processor"); | ||
| 928 | } | ||
| 929 | |||
| 930 | tables->db_export_mode = true; | ||
| 931 | /* | ||
| 932 | * Reserve per symbol space for symbol->db_id via symbol__priv() | ||
| 933 | */ | ||
| 934 | symbol_conf.priv_size = sizeof(u64); | ||
| 935 | |||
| 936 | SET_TABLE_HANDLER(evsel); | ||
| 937 | SET_TABLE_HANDLER(machine); | ||
| 938 | SET_TABLE_HANDLER(thread); | ||
| 939 | SET_TABLE_HANDLER(comm); | ||
| 940 | SET_TABLE_HANDLER(comm_thread); | ||
| 941 | SET_TABLE_HANDLER(dso); | ||
| 942 | SET_TABLE_HANDLER(symbol); | ||
| 943 | SET_TABLE_HANDLER(branch_type); | ||
| 944 | SET_TABLE_HANDLER(sample); | ||
| 945 | SET_TABLE_HANDLER(call_path); | ||
| 946 | SET_TABLE_HANDLER(call_return); | ||
| 947 | } | ||
| 948 | |||
| 592 | /* | 949 | /* |
| 593 | * Start trace script | 950 | * Start trace script |
| 594 | */ | 951 | */ |
| 595 | static int python_start_script(const char *script, int argc, const char **argv) | 952 | static int python_start_script(const char *script, int argc, const char **argv) |
| 596 | { | 953 | { |
| 954 | struct tables *tables = &tables_global; | ||
| 597 | const char **command_line; | 955 | const char **command_line; |
| 598 | char buf[PATH_MAX]; | 956 | char buf[PATH_MAX]; |
| 599 | int i, err = 0; | 957 | int i, err = 0; |
| @@ -632,6 +990,14 @@ static int python_start_script(const char *script, int argc, const char **argv) | |||
| 632 | 990 | ||
| 633 | free(command_line); | 991 | free(command_line); |
| 634 | 992 | ||
| 993 | set_table_handlers(tables); | ||
| 994 | |||
| 995 | if (tables->db_export_mode) { | ||
| 996 | err = db_export__branch_types(&tables->dbe); | ||
| 997 | if (err) | ||
| 998 | goto error; | ||
| 999 | } | ||
| 1000 | |||
| 635 | return err; | 1001 | return err; |
| 636 | error: | 1002 | error: |
| 637 | Py_Finalize(); | 1003 | Py_Finalize(); |
| @@ -642,7 +1008,9 @@ error: | |||
| 642 | 1008 | ||
| 643 | static int python_flush_script(void) | 1009 | static int python_flush_script(void) |
| 644 | { | 1010 | { |
| 645 | return 0; | 1011 | struct tables *tables = &tables_global; |
| 1012 | |||
| 1013 | return db_export__flush(&tables->dbe); | ||
| 646 | } | 1014 | } |
| 647 | 1015 | ||
| 648 | /* | 1016 | /* |
| @@ -650,8 +1018,12 @@ static int python_flush_script(void) | |||
| 650 | */ | 1018 | */ |
| 651 | static int python_stop_script(void) | 1019 | static int python_stop_script(void) |
| 652 | { | 1020 | { |
| 1021 | struct tables *tables = &tables_global; | ||
| 1022 | |||
| 653 | try_call_object("trace_end", NULL); | 1023 | try_call_object("trace_end", NULL); |
| 654 | 1024 | ||
| 1025 | db_export__exit(&tables->dbe); | ||
| 1026 | |||
| 655 | Py_XDECREF(main_dict); | 1027 | Py_XDECREF(main_dict); |
| 656 | Py_XDECREF(main_module); | 1028 | Py_XDECREF(main_module); |
| 657 | Py_Finalize(); | 1029 | Py_Finalize(); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 6702ac28754b..6ac62ae6b8fa 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool, | |||
| 228 | union perf_event *event, | 228 | union perf_event *event, |
| 229 | struct perf_session *session); | 229 | struct perf_session *session); |
| 230 | 230 | ||
| 231 | static int process_id_index_stub(struct perf_tool *tool __maybe_unused, | ||
| 232 | union perf_event *event __maybe_unused, | ||
| 233 | struct perf_session *perf_session | ||
| 234 | __maybe_unused) | ||
| 235 | { | ||
| 236 | dump_printf(": unhandled!\n"); | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 231 | void perf_tool__fill_defaults(struct perf_tool *tool) | 240 | void perf_tool__fill_defaults(struct perf_tool *tool) |
| 232 | { | 241 | { |
| 233 | if (tool->sample == NULL) | 242 | if (tool->sample == NULL) |
| @@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
| 262 | else | 271 | else |
| 263 | tool->finished_round = process_finished_round_stub; | 272 | tool->finished_round = process_finished_round_stub; |
| 264 | } | 273 | } |
| 274 | if (tool->id_index == NULL) | ||
| 275 | tool->id_index = process_id_index_stub; | ||
| 265 | } | 276 | } |
| 266 | 277 | ||
| 267 | static void swap_sample_id_all(union perf_event *event, void *data) | 278 | static void swap_sample_id_all(union perf_event *event, void *data) |
| @@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
| 460 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 471 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
| 461 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 472 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
| 462 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 473 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
| 474 | [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, | ||
| 463 | [PERF_RECORD_HEADER_MAX] = NULL, | 475 | [PERF_RECORD_HEADER_MAX] = NULL, |
| 464 | }; | 476 | }; |
| 465 | 477 | ||
| @@ -580,15 +592,46 @@ static void regs_dump__printf(u64 mask, u64 *regs) | |||
| 580 | } | 592 | } |
| 581 | } | 593 | } |
| 582 | 594 | ||
| 595 | static const char *regs_abi[] = { | ||
| 596 | [PERF_SAMPLE_REGS_ABI_NONE] = "none", | ||
| 597 | [PERF_SAMPLE_REGS_ABI_32] = "32-bit", | ||
| 598 | [PERF_SAMPLE_REGS_ABI_64] = "64-bit", | ||
| 599 | }; | ||
| 600 | |||
| 601 | static inline const char *regs_dump_abi(struct regs_dump *d) | ||
| 602 | { | ||
| 603 | if (d->abi > PERF_SAMPLE_REGS_ABI_64) | ||
| 604 | return "unknown"; | ||
| 605 | |||
| 606 | return regs_abi[d->abi]; | ||
| 607 | } | ||
| 608 | |||
| 609 | static void regs__printf(const char *type, struct regs_dump *regs) | ||
| 610 | { | ||
| 611 | u64 mask = regs->mask; | ||
| 612 | |||
| 613 | printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n", | ||
| 614 | type, | ||
| 615 | mask, | ||
| 616 | regs_dump_abi(regs)); | ||
| 617 | |||
| 618 | regs_dump__printf(mask, regs->regs); | ||
| 619 | } | ||
| 620 | |||
| 583 | static void regs_user__printf(struct perf_sample *sample) | 621 | static void regs_user__printf(struct perf_sample *sample) |
| 584 | { | 622 | { |
| 585 | struct regs_dump *user_regs = &sample->user_regs; | 623 | struct regs_dump *user_regs = &sample->user_regs; |
| 586 | 624 | ||
| 587 | if (user_regs->regs) { | 625 | if (user_regs->regs) |
| 588 | u64 mask = user_regs->mask; | 626 | regs__printf("user", user_regs); |
| 589 | printf("... user regs: mask 0x%" PRIx64 "\n", mask); | 627 | } |
| 590 | regs_dump__printf(mask, user_regs->regs); | 628 | |
| 591 | } | 629 | static void regs_intr__printf(struct perf_sample *sample) |
| 630 | { | ||
| 631 | struct regs_dump *intr_regs = &sample->intr_regs; | ||
| 632 | |||
| 633 | if (intr_regs->regs) | ||
| 634 | regs__printf("intr", intr_regs); | ||
| 592 | } | 635 | } |
| 593 | 636 | ||
| 594 | static void stack_user__printf(struct stack_dump *dump) | 637 | static void stack_user__printf(struct stack_dump *dump) |
| @@ -687,6 +730,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
| 687 | if (sample_type & PERF_SAMPLE_REGS_USER) | 730 | if (sample_type & PERF_SAMPLE_REGS_USER) |
| 688 | regs_user__printf(sample); | 731 | regs_user__printf(sample); |
| 689 | 732 | ||
| 733 | if (sample_type & PERF_SAMPLE_REGS_INTR) | ||
| 734 | regs_intr__printf(sample); | ||
| 735 | |||
| 690 | if (sample_type & PERF_SAMPLE_STACK_USER) | 736 | if (sample_type & PERF_SAMPLE_STACK_USER) |
| 691 | stack_user__printf(&sample->user_stack); | 737 | stack_user__printf(&sample->user_stack); |
| 692 | 738 | ||
| @@ -888,11 +934,26 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
| 888 | return tool->build_id(tool, event, session); | 934 | return tool->build_id(tool, event, session); |
| 889 | case PERF_RECORD_FINISHED_ROUND: | 935 | case PERF_RECORD_FINISHED_ROUND: |
| 890 | return tool->finished_round(tool, event, session); | 936 | return tool->finished_round(tool, event, session); |
| 937 | case PERF_RECORD_ID_INDEX: | ||
| 938 | return tool->id_index(tool, event, session); | ||
| 891 | default: | 939 | default: |
| 892 | return -EINVAL; | 940 | return -EINVAL; |
| 893 | } | 941 | } |
| 894 | } | 942 | } |
| 895 | 943 | ||
| 944 | int perf_session__deliver_synth_event(struct perf_session *session, | ||
| 945 | union perf_event *event, | ||
| 946 | struct perf_sample *sample, | ||
| 947 | struct perf_tool *tool) | ||
| 948 | { | ||
| 949 | events_stats__inc(&session->stats, event->header.type); | ||
| 950 | |||
| 951 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | ||
| 952 | return perf_session__process_user_event(session, event, tool, 0); | ||
| 953 | |||
| 954 | return perf_session__deliver_event(session, event, sample, tool, 0); | ||
| 955 | } | ||
| 956 | |||
| 896 | static void event_swap(union perf_event *event, bool sample_id_all) | 957 | static void event_swap(union perf_event *event, bool sample_id_all) |
| 897 | { | 958 | { |
| 898 | perf_event__swap_op swap; | 959 | perf_event__swap_op swap; |
| @@ -1417,9 +1478,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, | |||
| 1417 | if (symbol_conf.use_callchain && sample->callchain) { | 1478 | if (symbol_conf.use_callchain && sample->callchain) { |
| 1418 | struct addr_location node_al; | 1479 | struct addr_location node_al; |
| 1419 | 1480 | ||
| 1420 | if (machine__resolve_callchain(al->machine, evsel, al->thread, | 1481 | if (thread__resolve_callchain(al->thread, evsel, |
| 1421 | sample, NULL, NULL, | 1482 | sample, NULL, NULL, |
| 1422 | PERF_MAX_STACK_DEPTH) != 0) { | 1483 | PERF_MAX_STACK_DEPTH) != 0) { |
| 1423 | if (verbose) | 1484 | if (verbose) |
| 1424 | error("Failed to resolve callchain. Skipping\n"); | 1485 | error("Failed to resolve callchain. Skipping\n"); |
| 1425 | return; | 1486 | return; |
| @@ -1594,3 +1655,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
| 1594 | out: | 1655 | out: |
| 1595 | return err; | 1656 | return err; |
| 1596 | } | 1657 | } |
| 1658 | |||
| 1659 | int perf_event__process_id_index(struct perf_tool *tool __maybe_unused, | ||
| 1660 | union perf_event *event, | ||
| 1661 | struct perf_session *session) | ||
| 1662 | { | ||
| 1663 | struct perf_evlist *evlist = session->evlist; | ||
| 1664 | struct id_index_event *ie = &event->id_index; | ||
| 1665 | size_t i, nr, max_nr; | ||
| 1666 | |||
| 1667 | max_nr = (ie->header.size - sizeof(struct id_index_event)) / | ||
| 1668 | sizeof(struct id_index_entry); | ||
| 1669 | nr = ie->nr; | ||
| 1670 | if (nr > max_nr) | ||
| 1671 | return -EINVAL; | ||
| 1672 | |||
| 1673 | if (dump_trace) | ||
| 1674 | fprintf(stdout, " nr: %zu\n", nr); | ||
| 1675 | |||
| 1676 | for (i = 0; i < nr; i++) { | ||
| 1677 | struct id_index_entry *e = &ie->entries[i]; | ||
| 1678 | struct perf_sample_id *sid; | ||
| 1679 | |||
| 1680 | if (dump_trace) { | ||
| 1681 | fprintf(stdout, " ... id: %"PRIu64, e->id); | ||
| 1682 | fprintf(stdout, " idx: %"PRIu64, e->idx); | ||
| 1683 | fprintf(stdout, " cpu: %"PRId64, e->cpu); | ||
| 1684 | fprintf(stdout, " tid: %"PRId64"\n", e->tid); | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | sid = perf_evlist__id2sid(evlist, e->id); | ||
| 1688 | if (!sid) | ||
| 1689 | return -ENOENT; | ||
| 1690 | sid->idx = e->idx; | ||
| 1691 | sid->cpu = e->cpu; | ||
| 1692 | sid->tid = e->tid; | ||
| 1693 | } | ||
| 1694 | return 0; | ||
| 1695 | } | ||
| 1696 | |||
| 1697 | int perf_event__synthesize_id_index(struct perf_tool *tool, | ||
| 1698 | perf_event__handler_t process, | ||
| 1699 | struct perf_evlist *evlist, | ||
| 1700 | struct machine *machine) | ||
| 1701 | { | ||
| 1702 | union perf_event *ev; | ||
| 1703 | struct perf_evsel *evsel; | ||
| 1704 | size_t nr = 0, i = 0, sz, max_nr, n; | ||
| 1705 | int err; | ||
| 1706 | |||
| 1707 | pr_debug2("Synthesizing id index\n"); | ||
| 1708 | |||
| 1709 | max_nr = (UINT16_MAX - sizeof(struct id_index_event)) / | ||
| 1710 | sizeof(struct id_index_entry); | ||
| 1711 | |||
| 1712 | evlist__for_each(evlist, evsel) | ||
| 1713 | nr += evsel->ids; | ||
| 1714 | |||
| 1715 | n = nr > max_nr ? max_nr : nr; | ||
| 1716 | sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry); | ||
| 1717 | ev = zalloc(sz); | ||
| 1718 | if (!ev) | ||
| 1719 | return -ENOMEM; | ||
| 1720 | |||
| 1721 | ev->id_index.header.type = PERF_RECORD_ID_INDEX; | ||
| 1722 | ev->id_index.header.size = sz; | ||
| 1723 | ev->id_index.nr = n; | ||
| 1724 | |||
| 1725 | evlist__for_each(evlist, evsel) { | ||
| 1726 | u32 j; | ||
| 1727 | |||
| 1728 | for (j = 0; j < evsel->ids; j++) { | ||
| 1729 | struct id_index_entry *e; | ||
| 1730 | struct perf_sample_id *sid; | ||
| 1731 | |||
| 1732 | if (i >= n) { | ||
| 1733 | err = process(tool, ev, NULL, machine); | ||
| 1734 | if (err) | ||
| 1735 | goto out_err; | ||
| 1736 | nr -= n; | ||
| 1737 | i = 0; | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | e = &ev->id_index.entries[i++]; | ||
| 1741 | |||
| 1742 | e->id = evsel->id[j]; | ||
| 1743 | |||
| 1744 | sid = perf_evlist__id2sid(evlist, e->id); | ||
| 1745 | if (!sid) { | ||
| 1746 | free(ev); | ||
| 1747 | return -ENOENT; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | e->idx = sid->idx; | ||
| 1751 | e->cpu = sid->cpu; | ||
| 1752 | e->tid = sid->tid; | ||
| 1753 | } | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry); | ||
| 1757 | ev->id_index.header.size = sz; | ||
| 1758 | ev->id_index.nr = nr; | ||
| 1759 | |||
| 1760 | err = process(tool, ev, NULL, machine); | ||
| 1761 | out_err: | ||
| 1762 | free(ev); | ||
| 1763 | |||
| 1764 | return err; | ||
| 1765 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index a4be851f1a90..dc26ebf60fe4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -126,4 +126,19 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
| 126 | extern volatile int session_done; | 126 | extern volatile int session_done; |
| 127 | 127 | ||
| 128 | #define session_done() ACCESS_ONCE(session_done) | 128 | #define session_done() ACCESS_ONCE(session_done) |
| 129 | |||
| 130 | int perf_session__deliver_synth_event(struct perf_session *session, | ||
| 131 | union perf_event *event, | ||
| 132 | struct perf_sample *sample, | ||
| 133 | struct perf_tool *tool); | ||
| 134 | |||
| 135 | int perf_event__process_id_index(struct perf_tool *tool, | ||
| 136 | union perf_event *event, | ||
| 137 | struct perf_session *session); | ||
| 138 | |||
| 139 | int perf_event__synthesize_id_index(struct perf_tool *tool, | ||
| 140 | perf_event__handler_t process, | ||
| 141 | struct perf_evlist *evlist, | ||
| 142 | struct machine *machine); | ||
| 143 | |||
| 129 | #endif /* __PERF_SESSION_H */ | 144 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9402885a77f3..82a5596241a7 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -309,7 +309,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 309 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 309 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
| 310 | size_t size, unsigned int width) | 310 | size_t size, unsigned int width) |
| 311 | { | 311 | { |
| 312 | return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline); | 312 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline); |
| 313 | } | 313 | } |
| 314 | 314 | ||
| 315 | struct sort_entry sort_srcline = { | 315 | struct sort_entry sort_srcline = { |
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index f3e4bc5fe5d2..77c180637138 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c | |||
| @@ -274,7 +274,7 @@ char *get_srcline(struct dso *dso, unsigned long addr) | |||
| 274 | if (!addr2line(dso_name, addr, &file, &line, dso)) | 274 | if (!addr2line(dso_name, addr, &file, &line, dso)) |
| 275 | goto out; | 275 | goto out; |
| 276 | 276 | ||
| 277 | if (asprintf(&srcline, "%s:%u", file, line) < 0) { | 277 | if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) { |
| 278 | free(file); | 278 | free(file); |
| 279 | goto out; | 279 | goto out; |
| 280 | } | 280 | } |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 1e23a5bfb044..efc7eb6b8f0f 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
| @@ -546,6 +546,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata) | |||
| 546 | return 0; | 546 | return 0; |
| 547 | } | 547 | } |
| 548 | 548 | ||
| 549 | static int decompress_kmodule(struct dso *dso, const char *name, | ||
| 550 | enum dso_binary_type type) | ||
| 551 | { | ||
| 552 | int fd; | ||
| 553 | const char *ext = strrchr(name, '.'); | ||
| 554 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; | ||
| 555 | |||
| 556 | if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && | ||
| 557 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) || | ||
| 558 | type != dso->symtab_type) | ||
| 559 | return -1; | ||
| 560 | |||
| 561 | if (!ext || !is_supported_compression(ext + 1)) | ||
| 562 | return -1; | ||
| 563 | |||
| 564 | fd = mkstemp(tmpbuf); | ||
| 565 | if (fd < 0) | ||
| 566 | return -1; | ||
| 567 | |||
| 568 | if (!decompress_to_file(ext + 1, name, fd)) { | ||
| 569 | close(fd); | ||
| 570 | fd = -1; | ||
| 571 | } | ||
| 572 | |||
| 573 | unlink(tmpbuf); | ||
| 574 | |||
| 575 | return fd; | ||
| 576 | } | ||
| 577 | |||
| 549 | bool symsrc__possibly_runtime(struct symsrc *ss) | 578 | bool symsrc__possibly_runtime(struct symsrc *ss) |
| 550 | { | 579 | { |
| 551 | return ss->dynsym || ss->opdsec; | 580 | return ss->dynsym || ss->opdsec; |
| @@ -571,7 +600,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
| 571 | Elf *elf; | 600 | Elf *elf; |
| 572 | int fd; | 601 | int fd; |
| 573 | 602 | ||
| 574 | fd = open(name, O_RDONLY); | 603 | if (dso__needs_decompress(dso)) |
| 604 | fd = decompress_kmodule(dso, name, type); | ||
| 605 | else | ||
| 606 | fd = open(name, O_RDONLY); | ||
| 607 | |||
| 575 | if (fd < 0) | 608 | if (fd < 0) |
| 576 | return -1; | 609 | return -1; |
| 577 | 610 | ||
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index c9541fea9514..fa585c63f56a 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
| @@ -341,7 +341,6 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, | |||
| 341 | 341 | ||
| 342 | if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { | 342 | if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { |
| 343 | dso__set_build_id(dso, build_id); | 343 | dso__set_build_id(dso, build_id); |
| 344 | return 1; | ||
| 345 | } | 344 | } |
| 346 | return 0; | 345 | return 0; |
| 347 | } | 346 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 078331140d8c..c24c5b83156c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -51,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
| 51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
| 52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
| 53 | DSO_BINARY_TYPE__GUEST_KMODULE, | 53 | DSO_BINARY_TYPE__GUEST_KMODULE, |
| 54 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
| 54 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 55 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
| 56 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
| 55 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 57 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
| 56 | DSO_BINARY_TYPE__NOT_FOUND, | 58 | DSO_BINARY_TYPE__NOT_FOUND, |
| 57 | }; | 59 | }; |
| @@ -1300,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | |||
| 1300 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; | 1302 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; |
| 1301 | 1303 | ||
| 1302 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 1304 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
| 1305 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | ||
| 1303 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 1306 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
| 1307 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | ||
| 1304 | /* | 1308 | /* |
| 1305 | * kernel modules know their symtab type - it's set when | 1309 | * kernel modules know their symtab type - it's set when |
| 1306 | * creating a module dso in machine__new_module(). | 1310 | * creating a module dso in machine__new_module(). |
| @@ -1368,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
| 1368 | return -1; | 1372 | return -1; |
| 1369 | 1373 | ||
| 1370 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | 1374 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || |
| 1371 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | 1375 | dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || |
| 1376 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE || | ||
| 1377 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
| 1372 | 1378 | ||
| 1373 | /* | 1379 | /* |
| 1374 | * Iterate over candidate debug images. | 1380 | * Iterate over candidate debug images. |
| @@ -1505,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
| 1505 | symbol_filter_t filter) | 1511 | symbol_filter_t filter) |
| 1506 | { | 1512 | { |
| 1507 | int i, err = 0; | 1513 | int i, err = 0; |
| 1508 | char *filename; | 1514 | char *filename = NULL; |
| 1509 | 1515 | ||
| 1510 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1516 | if (!symbol_conf.ignore_vmlinux_buildid) |
| 1511 | vmlinux_path__nr_entries + 1); | 1517 | filename = dso__build_id_filename(dso, NULL, 0); |
| 1512 | |||
| 1513 | filename = dso__build_id_filename(dso, NULL, 0); | ||
| 1514 | if (filename != NULL) { | 1518 | if (filename != NULL) { |
| 1515 | err = dso__load_vmlinux(dso, map, filename, true, filter); | 1519 | err = dso__load_vmlinux(dso, map, filename, true, filter); |
| 1516 | if (err > 0) | 1520 | if (err > 0) |
| @@ -1518,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
| 1518 | free(filename); | 1522 | free(filename); |
| 1519 | } | 1523 | } |
| 1520 | 1524 | ||
| 1525 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
| 1526 | vmlinux_path__nr_entries + 1); | ||
| 1527 | |||
| 1521 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1528 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
| 1522 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); | 1529 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); |
| 1523 | if (err > 0) | 1530 | if (err > 0) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index eb2c19bf8d90..ded3ca7266de 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -105,6 +105,7 @@ struct symbol_conf { | |||
| 105 | unsigned short nr_events; | 105 | unsigned short nr_events; |
| 106 | bool try_vmlinux_path, | 106 | bool try_vmlinux_path, |
| 107 | ignore_vmlinux, | 107 | ignore_vmlinux, |
| 108 | ignore_vmlinux_buildid, | ||
| 108 | show_kernel_path, | 109 | show_kernel_path, |
| 109 | use_modules, | 110 | use_modules, |
| 110 | sort_by_name, | 111 | sort_by_name, |
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c new file mode 100644 index 000000000000..9ed59a452d1f --- /dev/null +++ b/tools/perf/util/thread-stack.c | |||
| @@ -0,0 +1,747 @@ | |||
| 1 | /* | ||
| 2 | * thread-stack.c: Synthesize a thread's stack using call / return events | ||
| 3 | * Copyright (c) 2014, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/rbtree.h> | ||
| 17 | #include <linux/list.h> | ||
| 18 | #include "thread.h" | ||
| 19 | #include "event.h" | ||
| 20 | #include "machine.h" | ||
| 21 | #include "util.h" | ||
| 22 | #include "debug.h" | ||
| 23 | #include "symbol.h" | ||
| 24 | #include "comm.h" | ||
| 25 | #include "thread-stack.h" | ||
| 26 | |||
| 27 | #define CALL_PATH_BLOCK_SHIFT 8 | ||
| 28 | #define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) | ||
| 29 | #define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) | ||
| 30 | |||
| 31 | struct call_path_block { | ||
| 32 | struct call_path cp[CALL_PATH_BLOCK_SIZE]; | ||
| 33 | struct list_head node; | ||
| 34 | }; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * struct call_path_root - root of all call paths. | ||
| 38 | * @call_path: root call path | ||
| 39 | * @blocks: list of blocks to store call paths | ||
| 40 | * @next: next free space | ||
| 41 | * @sz: number of spaces | ||
| 42 | */ | ||
| 43 | struct call_path_root { | ||
| 44 | struct call_path call_path; | ||
| 45 | struct list_head blocks; | ||
| 46 | size_t next; | ||
| 47 | size_t sz; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * struct call_return_processor - provides a call-back to consume call-return | ||
| 52 | * information. | ||
| 53 | * @cpr: call path root | ||
| 54 | * @process: call-back that accepts call/return information | ||
| 55 | * @data: anonymous data for call-back | ||
| 56 | */ | ||
| 57 | struct call_return_processor { | ||
| 58 | struct call_path_root *cpr; | ||
| 59 | int (*process)(struct call_return *cr, void *data); | ||
| 60 | void *data; | ||
| 61 | }; | ||
| 62 | |||
| 63 | #define STACK_GROWTH 2048 | ||
| 64 | |||
| 65 | /** | ||
| 66 | * struct thread_stack_entry - thread stack entry. | ||
| 67 | * @ret_addr: return address | ||
| 68 | * @timestamp: timestamp (if known) | ||
| 69 | * @ref: external reference (e.g. db_id of sample) | ||
| 70 | * @branch_count: the branch count when the entry was created | ||
| 71 | * @cp: call path | ||
| 72 | * @no_call: a 'call' was not seen | ||
| 73 | */ | ||
| 74 | struct thread_stack_entry { | ||
| 75 | u64 ret_addr; | ||
| 76 | u64 timestamp; | ||
| 77 | u64 ref; | ||
| 78 | u64 branch_count; | ||
| 79 | struct call_path *cp; | ||
| 80 | bool no_call; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /** | ||
| 84 | * struct thread_stack - thread stack constructed from 'call' and 'return' | ||
| 85 | * branch samples. | ||
| 86 | * @stack: array that holds the stack | ||
| 87 | * @cnt: number of entries in the stack | ||
| 88 | * @sz: current maximum stack size | ||
| 89 | * @trace_nr: current trace number | ||
| 90 | * @branch_count: running branch count | ||
| 91 | * @kernel_start: kernel start address | ||
| 92 | * @last_time: last timestamp | ||
| 93 | * @crp: call/return processor | ||
| 94 | * @comm: current comm | ||
| 95 | */ | ||
| 96 | struct thread_stack { | ||
| 97 | struct thread_stack_entry *stack; | ||
| 98 | size_t cnt; | ||
| 99 | size_t sz; | ||
| 100 | u64 trace_nr; | ||
| 101 | u64 branch_count; | ||
| 102 | u64 kernel_start; | ||
| 103 | u64 last_time; | ||
| 104 | struct call_return_processor *crp; | ||
| 105 | struct comm *comm; | ||
| 106 | }; | ||
| 107 | |||
| 108 | static int thread_stack__grow(struct thread_stack *ts) | ||
| 109 | { | ||
| 110 | struct thread_stack_entry *new_stack; | ||
| 111 | size_t sz, new_sz; | ||
| 112 | |||
| 113 | new_sz = ts->sz + STACK_GROWTH; | ||
| 114 | sz = new_sz * sizeof(struct thread_stack_entry); | ||
| 115 | |||
| 116 | new_stack = realloc(ts->stack, sz); | ||
| 117 | if (!new_stack) | ||
| 118 | return -ENOMEM; | ||
| 119 | |||
| 120 | ts->stack = new_stack; | ||
| 121 | ts->sz = new_sz; | ||
| 122 | |||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | static struct thread_stack *thread_stack__new(struct thread *thread, | ||
| 127 | struct call_return_processor *crp) | ||
| 128 | { | ||
| 129 | struct thread_stack *ts; | ||
| 130 | |||
| 131 | ts = zalloc(sizeof(struct thread_stack)); | ||
| 132 | if (!ts) | ||
| 133 | return NULL; | ||
| 134 | |||
| 135 | if (thread_stack__grow(ts)) { | ||
| 136 | free(ts); | ||
| 137 | return NULL; | ||
| 138 | } | ||
| 139 | |||
| 140 | if (thread->mg && thread->mg->machine) | ||
| 141 | ts->kernel_start = machine__kernel_start(thread->mg->machine); | ||
| 142 | else | ||
| 143 | ts->kernel_start = 1ULL << 63; | ||
| 144 | ts->crp = crp; | ||
| 145 | |||
| 146 | return ts; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | ||
| 150 | { | ||
| 151 | int err = 0; | ||
| 152 | |||
| 153 | if (ts->cnt == ts->sz) { | ||
| 154 | err = thread_stack__grow(ts); | ||
| 155 | if (err) { | ||
| 156 | pr_warning("Out of memory: discarding thread stack\n"); | ||
| 157 | ts->cnt = 0; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | ts->stack[ts->cnt++].ret_addr = ret_addr; | ||
| 162 | |||
| 163 | return err; | ||
| 164 | } | ||
| 165 | |||
| 166 | static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) | ||
| 167 | { | ||
| 168 | size_t i; | ||
| 169 | |||
| 170 | /* | ||
| 171 | * In some cases there may be functions which are not seen to return. | ||
| 172 | * For example when setjmp / longjmp has been used. Or the perf context | ||
| 173 | * switch in the kernel which doesn't stop and start tracing in exactly | ||
| 174 | * the same code path. When that happens the return address will be | ||
| 175 | * further down the stack. If the return address is not found at all, | ||
| 176 | * we assume the opposite (i.e. this is a return for a call that wasn't | ||
| 177 | * seen for some reason) and leave the stack alone. | ||
| 178 | */ | ||
| 179 | for (i = ts->cnt; i; ) { | ||
| 180 | if (ts->stack[--i].ret_addr == ret_addr) { | ||
| 181 | ts->cnt = i; | ||
| 182 | return; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | static bool thread_stack__in_kernel(struct thread_stack *ts) | ||
| 188 | { | ||
| 189 | if (!ts->cnt) | ||
| 190 | return false; | ||
| 191 | |||
| 192 | return ts->stack[ts->cnt - 1].cp->in_kernel; | ||
| 193 | } | ||
| 194 | |||
| 195 | static int thread_stack__call_return(struct thread *thread, | ||
| 196 | struct thread_stack *ts, size_t idx, | ||
| 197 | u64 timestamp, u64 ref, bool no_return) | ||
| 198 | { | ||
| 199 | struct call_return_processor *crp = ts->crp; | ||
| 200 | struct thread_stack_entry *tse; | ||
| 201 | struct call_return cr = { | ||
| 202 | .thread = thread, | ||
| 203 | .comm = ts->comm, | ||
| 204 | .db_id = 0, | ||
| 205 | }; | ||
| 206 | |||
| 207 | tse = &ts->stack[idx]; | ||
| 208 | cr.cp = tse->cp; | ||
| 209 | cr.call_time = tse->timestamp; | ||
| 210 | cr.return_time = timestamp; | ||
| 211 | cr.branch_count = ts->branch_count - tse->branch_count; | ||
| 212 | cr.call_ref = tse->ref; | ||
| 213 | cr.return_ref = ref; | ||
| 214 | if (tse->no_call) | ||
| 215 | cr.flags |= CALL_RETURN_NO_CALL; | ||
| 216 | if (no_return) | ||
| 217 | cr.flags |= CALL_RETURN_NO_RETURN; | ||
| 218 | |||
| 219 | return crp->process(&cr, crp->data); | ||
| 220 | } | ||
| 221 | |||
| 222 | static int thread_stack__flush(struct thread *thread, struct thread_stack *ts) | ||
| 223 | { | ||
| 224 | struct call_return_processor *crp = ts->crp; | ||
| 225 | int err; | ||
| 226 | |||
| 227 | if (!crp) { | ||
| 228 | ts->cnt = 0; | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | while (ts->cnt) { | ||
| 233 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 234 | ts->last_time, 0, true); | ||
| 235 | if (err) { | ||
| 236 | pr_err("Error flushing thread stack!\n"); | ||
| 237 | ts->cnt = 0; | ||
| 238 | return err; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
| 246 | u64 to_ip, u16 insn_len, u64 trace_nr) | ||
| 247 | { | ||
| 248 | if (!thread) | ||
| 249 | return -EINVAL; | ||
| 250 | |||
| 251 | if (!thread->ts) { | ||
| 252 | thread->ts = thread_stack__new(thread, NULL); | ||
| 253 | if (!thread->ts) { | ||
| 254 | pr_warning("Out of memory: no thread stack\n"); | ||
| 255 | return -ENOMEM; | ||
| 256 | } | ||
| 257 | thread->ts->trace_nr = trace_nr; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* | ||
| 261 | * When the trace is discontinuous, the trace_nr changes. In that case | ||
| 262 | * the stack might be completely invalid. Better to report nothing than | ||
| 263 | * to report something misleading, so flush the stack. | ||
| 264 | */ | ||
| 265 | if (trace_nr != thread->ts->trace_nr) { | ||
| 266 | if (thread->ts->trace_nr) | ||
| 267 | thread_stack__flush(thread, thread->ts); | ||
| 268 | thread->ts->trace_nr = trace_nr; | ||
| 269 | } | ||
| 270 | |||
| 271 | /* Stop here if thread_stack__process() is in use */ | ||
| 272 | if (thread->ts->crp) | ||
| 273 | return 0; | ||
| 274 | |||
| 275 | if (flags & PERF_IP_FLAG_CALL) { | ||
| 276 | u64 ret_addr; | ||
| 277 | |||
| 278 | if (!to_ip) | ||
| 279 | return 0; | ||
| 280 | ret_addr = from_ip + insn_len; | ||
| 281 | if (ret_addr == to_ip) | ||
| 282 | return 0; /* Zero-length calls are excluded */ | ||
| 283 | return thread_stack__push(thread->ts, ret_addr); | ||
| 284 | } else if (flags & PERF_IP_FLAG_RETURN) { | ||
| 285 | if (!from_ip) | ||
| 286 | return 0; | ||
| 287 | thread_stack__pop(thread->ts, to_ip); | ||
| 288 | } | ||
| 289 | |||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr) | ||
| 294 | { | ||
| 295 | if (!thread || !thread->ts) | ||
| 296 | return; | ||
| 297 | |||
| 298 | if (trace_nr != thread->ts->trace_nr) { | ||
| 299 | if (thread->ts->trace_nr) | ||
| 300 | thread_stack__flush(thread, thread->ts); | ||
| 301 | thread->ts->trace_nr = trace_nr; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | void thread_stack__free(struct thread *thread) | ||
| 306 | { | ||
| 307 | if (thread->ts) { | ||
| 308 | thread_stack__flush(thread, thread->ts); | ||
| 309 | zfree(&thread->ts->stack); | ||
| 310 | zfree(&thread->ts); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
| 315 | size_t sz, u64 ip) | ||
| 316 | { | ||
| 317 | size_t i; | ||
| 318 | |||
| 319 | if (!thread || !thread->ts) | ||
| 320 | chain->nr = 1; | ||
| 321 | else | ||
| 322 | chain->nr = min(sz, thread->ts->cnt + 1); | ||
| 323 | |||
| 324 | chain->ips[0] = ip; | ||
| 325 | |||
| 326 | for (i = 1; i < chain->nr; i++) | ||
| 327 | chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; | ||
| 328 | } | ||
| 329 | |||
| 330 | static void call_path__init(struct call_path *cp, struct call_path *parent, | ||
| 331 | struct symbol *sym, u64 ip, bool in_kernel) | ||
| 332 | { | ||
| 333 | cp->parent = parent; | ||
| 334 | cp->sym = sym; | ||
| 335 | cp->ip = sym ? 0 : ip; | ||
| 336 | cp->db_id = 0; | ||
| 337 | cp->in_kernel = in_kernel; | ||
| 338 | RB_CLEAR_NODE(&cp->rb_node); | ||
| 339 | cp->children = RB_ROOT; | ||
| 340 | } | ||
| 341 | |||
| 342 | static struct call_path_root *call_path_root__new(void) | ||
| 343 | { | ||
| 344 | struct call_path_root *cpr; | ||
| 345 | |||
| 346 | cpr = zalloc(sizeof(struct call_path_root)); | ||
| 347 | if (!cpr) | ||
| 348 | return NULL; | ||
| 349 | call_path__init(&cpr->call_path, NULL, NULL, 0, false); | ||
| 350 | INIT_LIST_HEAD(&cpr->blocks); | ||
| 351 | return cpr; | ||
| 352 | } | ||
| 353 | |||
| 354 | static void call_path_root__free(struct call_path_root *cpr) | ||
| 355 | { | ||
| 356 | struct call_path_block *pos, *n; | ||
| 357 | |||
| 358 | list_for_each_entry_safe(pos, n, &cpr->blocks, node) { | ||
| 359 | list_del(&pos->node); | ||
| 360 | free(pos); | ||
| 361 | } | ||
| 362 | free(cpr); | ||
| 363 | } | ||
| 364 | |||
| 365 | static struct call_path *call_path__new(struct call_path_root *cpr, | ||
| 366 | struct call_path *parent, | ||
| 367 | struct symbol *sym, u64 ip, | ||
| 368 | bool in_kernel) | ||
| 369 | { | ||
| 370 | struct call_path_block *cpb; | ||
| 371 | struct call_path *cp; | ||
| 372 | size_t n; | ||
| 373 | |||
| 374 | if (cpr->next < cpr->sz) { | ||
| 375 | cpb = list_last_entry(&cpr->blocks, struct call_path_block, | ||
| 376 | node); | ||
| 377 | } else { | ||
| 378 | cpb = zalloc(sizeof(struct call_path_block)); | ||
| 379 | if (!cpb) | ||
| 380 | return NULL; | ||
| 381 | list_add_tail(&cpb->node, &cpr->blocks); | ||
| 382 | cpr->sz += CALL_PATH_BLOCK_SIZE; | ||
| 383 | } | ||
| 384 | |||
| 385 | n = cpr->next++ & CALL_PATH_BLOCK_MASK; | ||
| 386 | cp = &cpb->cp[n]; | ||
| 387 | |||
| 388 | call_path__init(cp, parent, sym, ip, in_kernel); | ||
| 389 | |||
| 390 | return cp; | ||
| 391 | } | ||
| 392 | |||
| 393 | static struct call_path *call_path__findnew(struct call_path_root *cpr, | ||
| 394 | struct call_path *parent, | ||
| 395 | struct symbol *sym, u64 ip, u64 ks) | ||
| 396 | { | ||
| 397 | struct rb_node **p; | ||
| 398 | struct rb_node *node_parent = NULL; | ||
| 399 | struct call_path *cp; | ||
| 400 | bool in_kernel = ip >= ks; | ||
| 401 | |||
| 402 | if (sym) | ||
| 403 | ip = 0; | ||
| 404 | |||
| 405 | if (!parent) | ||
| 406 | return call_path__new(cpr, parent, sym, ip, in_kernel); | ||
| 407 | |||
| 408 | p = &parent->children.rb_node; | ||
| 409 | while (*p != NULL) { | ||
| 410 | node_parent = *p; | ||
| 411 | cp = rb_entry(node_parent, struct call_path, rb_node); | ||
| 412 | |||
| 413 | if (cp->sym == sym && cp->ip == ip) | ||
| 414 | return cp; | ||
| 415 | |||
| 416 | if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) | ||
| 417 | p = &(*p)->rb_left; | ||
| 418 | else | ||
| 419 | p = &(*p)->rb_right; | ||
| 420 | } | ||
| 421 | |||
| 422 | cp = call_path__new(cpr, parent, sym, ip, in_kernel); | ||
| 423 | if (!cp) | ||
| 424 | return NULL; | ||
| 425 | |||
| 426 | rb_link_node(&cp->rb_node, node_parent, p); | ||
| 427 | rb_insert_color(&cp->rb_node, &parent->children); | ||
| 428 | |||
| 429 | return cp; | ||
| 430 | } | ||
| 431 | |||
| 432 | struct call_return_processor * | ||
| 433 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
| 434 | void *data) | ||
| 435 | { | ||
| 436 | struct call_return_processor *crp; | ||
| 437 | |||
| 438 | crp = zalloc(sizeof(struct call_return_processor)); | ||
| 439 | if (!crp) | ||
| 440 | return NULL; | ||
| 441 | crp->cpr = call_path_root__new(); | ||
| 442 | if (!crp->cpr) | ||
| 443 | goto out_free; | ||
| 444 | crp->process = process; | ||
| 445 | crp->data = data; | ||
| 446 | return crp; | ||
| 447 | |||
| 448 | out_free: | ||
| 449 | free(crp); | ||
| 450 | return NULL; | ||
| 451 | } | ||
| 452 | |||
| 453 | void call_return_processor__free(struct call_return_processor *crp) | ||
| 454 | { | ||
| 455 | if (crp) { | ||
| 456 | call_path_root__free(crp->cpr); | ||
| 457 | free(crp); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, | ||
| 462 | u64 timestamp, u64 ref, struct call_path *cp, | ||
| 463 | bool no_call) | ||
| 464 | { | ||
| 465 | struct thread_stack_entry *tse; | ||
| 466 | int err; | ||
| 467 | |||
| 468 | if (ts->cnt == ts->sz) { | ||
| 469 | err = thread_stack__grow(ts); | ||
| 470 | if (err) | ||
| 471 | return err; | ||
| 472 | } | ||
| 473 | |||
| 474 | tse = &ts->stack[ts->cnt++]; | ||
| 475 | tse->ret_addr = ret_addr; | ||
| 476 | tse->timestamp = timestamp; | ||
| 477 | tse->ref = ref; | ||
| 478 | tse->branch_count = ts->branch_count; | ||
| 479 | tse->cp = cp; | ||
| 480 | tse->no_call = no_call; | ||
| 481 | |||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts, | ||
| 486 | u64 ret_addr, u64 timestamp, u64 ref, | ||
| 487 | struct symbol *sym) | ||
| 488 | { | ||
| 489 | int err; | ||
| 490 | |||
| 491 | if (!ts->cnt) | ||
| 492 | return 1; | ||
| 493 | |||
| 494 | if (ts->cnt == 1) { | ||
| 495 | struct thread_stack_entry *tse = &ts->stack[0]; | ||
| 496 | |||
| 497 | if (tse->cp->sym == sym) | ||
| 498 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 499 | timestamp, ref, false); | ||
| 500 | } | ||
| 501 | |||
| 502 | if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) { | ||
| 503 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 504 | timestamp, ref, false); | ||
| 505 | } else { | ||
| 506 | size_t i = ts->cnt - 1; | ||
| 507 | |||
| 508 | while (i--) { | ||
| 509 | if (ts->stack[i].ret_addr != ret_addr) | ||
| 510 | continue; | ||
| 511 | i += 1; | ||
| 512 | while (ts->cnt > i) { | ||
| 513 | err = thread_stack__call_return(thread, ts, | ||
| 514 | --ts->cnt, | ||
| 515 | timestamp, ref, | ||
| 516 | true); | ||
| 517 | if (err) | ||
| 518 | return err; | ||
| 519 | } | ||
| 520 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 521 | timestamp, ref, false); | ||
| 522 | } | ||
| 523 | } | ||
| 524 | |||
| 525 | return 1; | ||
| 526 | } | ||
| 527 | |||
| 528 | static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts, | ||
| 529 | struct perf_sample *sample, | ||
| 530 | struct addr_location *from_al, | ||
| 531 | struct addr_location *to_al, u64 ref) | ||
| 532 | { | ||
| 533 | struct call_path_root *cpr = ts->crp->cpr; | ||
| 534 | struct call_path *cp; | ||
| 535 | struct symbol *sym; | ||
| 536 | u64 ip; | ||
| 537 | |||
| 538 | if (sample->ip) { | ||
| 539 | ip = sample->ip; | ||
| 540 | sym = from_al->sym; | ||
| 541 | } else if (sample->addr) { | ||
| 542 | ip = sample->addr; | ||
| 543 | sym = to_al->sym; | ||
| 544 | } else { | ||
| 545 | return 0; | ||
| 546 | } | ||
| 547 | |||
| 548 | cp = call_path__findnew(cpr, &cpr->call_path, sym, ip, | ||
| 549 | ts->kernel_start); | ||
| 550 | if (!cp) | ||
| 551 | return -ENOMEM; | ||
| 552 | |||
| 553 | return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp, | ||
| 554 | true); | ||
| 555 | } | ||
| 556 | |||
| 557 | static int thread_stack__no_call_return(struct thread *thread, | ||
| 558 | struct thread_stack *ts, | ||
| 559 | struct perf_sample *sample, | ||
| 560 | struct addr_location *from_al, | ||
| 561 | struct addr_location *to_al, u64 ref) | ||
| 562 | { | ||
| 563 | struct call_path_root *cpr = ts->crp->cpr; | ||
| 564 | struct call_path *cp, *parent; | ||
| 565 | u64 ks = ts->kernel_start; | ||
| 566 | int err; | ||
| 567 | |||
| 568 | if (sample->ip >= ks && sample->addr < ks) { | ||
| 569 | /* Return to userspace, so pop all kernel addresses */ | ||
| 570 | while (thread_stack__in_kernel(ts)) { | ||
| 571 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 572 | sample->time, ref, | ||
| 573 | true); | ||
| 574 | if (err) | ||
| 575 | return err; | ||
| 576 | } | ||
| 577 | |||
| 578 | /* If the stack is empty, push the userspace address */ | ||
| 579 | if (!ts->cnt) { | ||
| 580 | cp = call_path__findnew(cpr, &cpr->call_path, | ||
| 581 | to_al->sym, sample->addr, | ||
| 582 | ts->kernel_start); | ||
| 583 | if (!cp) | ||
| 584 | return -ENOMEM; | ||
| 585 | return thread_stack__push_cp(ts, 0, sample->time, ref, | ||
| 586 | cp, true); | ||
| 587 | } | ||
| 588 | } else if (thread_stack__in_kernel(ts) && sample->ip < ks) { | ||
| 589 | /* Return to userspace, so pop all kernel addresses */ | ||
| 590 | while (thread_stack__in_kernel(ts)) { | ||
| 591 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 592 | sample->time, ref, | ||
| 593 | true); | ||
| 594 | if (err) | ||
| 595 | return err; | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | if (ts->cnt) | ||
| 600 | parent = ts->stack[ts->cnt - 1].cp; | ||
| 601 | else | ||
| 602 | parent = &cpr->call_path; | ||
| 603 | |||
| 604 | /* This 'return' had no 'call', so push and pop top of stack */ | ||
| 605 | cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip, | ||
| 606 | ts->kernel_start); | ||
| 607 | if (!cp) | ||
| 608 | return -ENOMEM; | ||
| 609 | |||
| 610 | err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp, | ||
| 611 | true); | ||
| 612 | if (err) | ||
| 613 | return err; | ||
| 614 | |||
| 615 | return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref, | ||
| 616 | to_al->sym); | ||
| 617 | } | ||
| 618 | |||
| 619 | static int thread_stack__trace_begin(struct thread *thread, | ||
| 620 | struct thread_stack *ts, u64 timestamp, | ||
| 621 | u64 ref) | ||
| 622 | { | ||
| 623 | struct thread_stack_entry *tse; | ||
| 624 | int err; | ||
| 625 | |||
| 626 | if (!ts->cnt) | ||
| 627 | return 0; | ||
| 628 | |||
| 629 | /* Pop trace end */ | ||
| 630 | tse = &ts->stack[ts->cnt - 1]; | ||
| 631 | if (tse->cp->sym == NULL && tse->cp->ip == 0) { | ||
| 632 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
| 633 | timestamp, ref, false); | ||
| 634 | if (err) | ||
| 635 | return err; | ||
| 636 | } | ||
| 637 | |||
| 638 | return 0; | ||
| 639 | } | ||
| 640 | |||
| 641 | static int thread_stack__trace_end(struct thread_stack *ts, | ||
| 642 | struct perf_sample *sample, u64 ref) | ||
| 643 | { | ||
| 644 | struct call_path_root *cpr = ts->crp->cpr; | ||
| 645 | struct call_path *cp; | ||
| 646 | u64 ret_addr; | ||
| 647 | |||
| 648 | /* No point having 'trace end' on the bottom of the stack */ | ||
| 649 | if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref)) | ||
| 650 | return 0; | ||
| 651 | |||
| 652 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0, | ||
| 653 | ts->kernel_start); | ||
| 654 | if (!cp) | ||
| 655 | return -ENOMEM; | ||
| 656 | |||
| 657 | ret_addr = sample->ip + sample->insn_len; | ||
| 658 | |||
| 659 | return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, | ||
| 660 | false); | ||
| 661 | } | ||
| 662 | |||
| 663 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
| 664 | struct perf_sample *sample, | ||
| 665 | struct addr_location *from_al, | ||
| 666 | struct addr_location *to_al, u64 ref, | ||
| 667 | struct call_return_processor *crp) | ||
| 668 | { | ||
| 669 | struct thread_stack *ts = thread->ts; | ||
| 670 | int err = 0; | ||
| 671 | |||
| 672 | if (ts) { | ||
| 673 | if (!ts->crp) { | ||
| 674 | /* Supersede thread_stack__event() */ | ||
| 675 | thread_stack__free(thread); | ||
| 676 | thread->ts = thread_stack__new(thread, crp); | ||
| 677 | if (!thread->ts) | ||
| 678 | return -ENOMEM; | ||
| 679 | ts = thread->ts; | ||
| 680 | ts->comm = comm; | ||
| 681 | } | ||
| 682 | } else { | ||
| 683 | thread->ts = thread_stack__new(thread, crp); | ||
| 684 | if (!thread->ts) | ||
| 685 | return -ENOMEM; | ||
| 686 | ts = thread->ts; | ||
| 687 | ts->comm = comm; | ||
| 688 | } | ||
| 689 | |||
| 690 | /* Flush stack on exec */ | ||
| 691 | if (ts->comm != comm && thread->pid_ == thread->tid) { | ||
| 692 | err = thread_stack__flush(thread, ts); | ||
| 693 | if (err) | ||
| 694 | return err; | ||
| 695 | ts->comm = comm; | ||
| 696 | } | ||
| 697 | |||
| 698 | /* If the stack is empty, put the current symbol on the stack */ | ||
| 699 | if (!ts->cnt) { | ||
| 700 | err = thread_stack__bottom(thread, ts, sample, from_al, to_al, | ||
| 701 | ref); | ||
| 702 | if (err) | ||
| 703 | return err; | ||
| 704 | } | ||
| 705 | |||
| 706 | ts->branch_count += 1; | ||
| 707 | ts->last_time = sample->time; | ||
| 708 | |||
| 709 | if (sample->flags & PERF_IP_FLAG_CALL) { | ||
| 710 | struct call_path_root *cpr = ts->crp->cpr; | ||
| 711 | struct call_path *cp; | ||
| 712 | u64 ret_addr; | ||
| 713 | |||
| 714 | if (!sample->ip || !sample->addr) | ||
| 715 | return 0; | ||
| 716 | |||
| 717 | ret_addr = sample->ip + sample->insn_len; | ||
| 718 | if (ret_addr == sample->addr) | ||
| 719 | return 0; /* Zero-length calls are excluded */ | ||
| 720 | |||
| 721 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, | ||
| 722 | to_al->sym, sample->addr, | ||
| 723 | ts->kernel_start); | ||
| 724 | if (!cp) | ||
| 725 | return -ENOMEM; | ||
| 726 | err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, | ||
| 727 | cp, false); | ||
| 728 | } else if (sample->flags & PERF_IP_FLAG_RETURN) { | ||
| 729 | if (!sample->ip || !sample->addr) | ||
| 730 | return 0; | ||
| 731 | |||
| 732 | err = thread_stack__pop_cp(thread, ts, sample->addr, | ||
| 733 | sample->time, ref, from_al->sym); | ||
| 734 | if (err) { | ||
| 735 | if (err < 0) | ||
| 736 | return err; | ||
| 737 | err = thread_stack__no_call_return(thread, ts, sample, | ||
| 738 | from_al, to_al, ref); | ||
| 739 | } | ||
| 740 | } else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) { | ||
| 741 | err = thread_stack__trace_begin(thread, ts, sample->time, ref); | ||
| 742 | } else if (sample->flags & PERF_IP_FLAG_TRACE_END) { | ||
| 743 | err = thread_stack__trace_end(ts, sample, ref); | ||
| 744 | } | ||
| 745 | |||
| 746 | return err; | ||
| 747 | } | ||
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h new file mode 100644 index 000000000000..b843bbef8ba2 --- /dev/null +++ b/tools/perf/util/thread-stack.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | /* | ||
| 2 | * thread-stack.h: Synthesize a thread's stack using call / return events | ||
| 3 | * Copyright (c) 2014, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef __PERF_THREAD_STACK_H | ||
| 17 | #define __PERF_THREAD_STACK_H | ||
| 18 | |||
| 19 | #include <sys/types.h> | ||
| 20 | |||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/rbtree.h> | ||
| 23 | |||
| 24 | struct thread; | ||
| 25 | struct comm; | ||
| 26 | struct ip_callchain; | ||
| 27 | struct symbol; | ||
| 28 | struct dso; | ||
| 29 | struct call_return_processor; | ||
| 30 | struct comm; | ||
| 31 | struct perf_sample; | ||
| 32 | struct addr_location; | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Call/Return flags. | ||
| 36 | * | ||
| 37 | * CALL_RETURN_NO_CALL: 'return' but no matching 'call' | ||
| 38 | * CALL_RETURN_NO_RETURN: 'call' but no matching 'return' | ||
| 39 | */ | ||
| 40 | enum { | ||
| 41 | CALL_RETURN_NO_CALL = 1 << 0, | ||
| 42 | CALL_RETURN_NO_RETURN = 1 << 1, | ||
| 43 | }; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * struct call_return - paired call/return information. | ||
| 47 | * @thread: thread in which call/return occurred | ||
| 48 | * @comm: comm in which call/return occurred | ||
| 49 | * @cp: call path | ||
| 50 | * @call_time: timestamp of call (if known) | ||
| 51 | * @return_time: timestamp of return (if known) | ||
| 52 | * @branch_count: number of branches seen between call and return | ||
| 53 | * @call_ref: external reference to 'call' sample (e.g. db_id) | ||
| 54 | * @return_ref: external reference to 'return' sample (e.g. db_id) | ||
| 55 | * @db_id: id used for db-export | ||
| 56 | * @flags: Call/Return flags | ||
| 57 | */ | ||
| 58 | struct call_return { | ||
| 59 | struct thread *thread; | ||
| 60 | struct comm *comm; | ||
| 61 | struct call_path *cp; | ||
| 62 | u64 call_time; | ||
| 63 | u64 return_time; | ||
| 64 | u64 branch_count; | ||
| 65 | u64 call_ref; | ||
| 66 | u64 return_ref; | ||
| 67 | u64 db_id; | ||
| 68 | u32 flags; | ||
| 69 | }; | ||
| 70 | |||
| 71 | /** | ||
| 72 | * struct call_path - node in list of calls leading to a function call. | ||
| 73 | * @parent: call path to the parent function call | ||
| 74 | * @sym: symbol of function called | ||
| 75 | * @ip: only if sym is null, the ip of the function | ||
| 76 | * @db_id: id used for db-export | ||
| 77 | * @in_kernel: whether function is a in the kernel | ||
| 78 | * @rb_node: node in parent's tree of called functions | ||
| 79 | * @children: tree of call paths of functions called | ||
| 80 | * | ||
| 81 | * In combination with the call_return structure, the call_path structure | ||
| 82 | * defines a context-sensitve call-graph. | ||
| 83 | */ | ||
| 84 | struct call_path { | ||
| 85 | struct call_path *parent; | ||
| 86 | struct symbol *sym; | ||
| 87 | u64 ip; | ||
| 88 | u64 db_id; | ||
| 89 | bool in_kernel; | ||
| 90 | struct rb_node rb_node; | ||
| 91 | struct rb_root children; | ||
| 92 | }; | ||
| 93 | |||
| 94 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
| 95 | u64 to_ip, u16 insn_len, u64 trace_nr); | ||
| 96 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr); | ||
| 97 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
| 98 | size_t sz, u64 ip); | ||
| 99 | void thread_stack__free(struct thread *thread); | ||
| 100 | |||
| 101 | struct call_return_processor * | ||
| 102 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
| 103 | void *data); | ||
| 104 | void call_return_processor__free(struct call_return_processor *crp); | ||
| 105 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
| 106 | struct perf_sample *sample, | ||
| 107 | struct addr_location *from_al, | ||
| 108 | struct addr_location *to_al, u64 ref, | ||
| 109 | struct call_return_processor *crp); | ||
| 110 | |||
| 111 | #endif | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index c41411726c7a..9ebc8b1f9be5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include <string.h> | 4 | #include <string.h> |
| 5 | #include "session.h" | 5 | #include "session.h" |
| 6 | #include "thread.h" | 6 | #include "thread.h" |
| 7 | #include "thread-stack.h" | ||
| 7 | #include "util.h" | 8 | #include "util.h" |
| 8 | #include "debug.h" | 9 | #include "debug.h" |
| 9 | #include "comm.h" | 10 | #include "comm.h" |
| @@ -15,7 +16,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine) | |||
| 15 | pid_t pid = thread->pid_; | 16 | pid_t pid = thread->pid_; |
| 16 | 17 | ||
| 17 | if (pid == thread->tid || pid == -1) { | 18 | if (pid == thread->tid || pid == -1) { |
| 18 | thread->mg = map_groups__new(); | 19 | thread->mg = map_groups__new(machine); |
| 19 | } else { | 20 | } else { |
| 20 | leader = machine__findnew_thread(machine, pid, pid); | 21 | leader = machine__findnew_thread(machine, pid, pid); |
| 21 | if (leader) | 22 | if (leader) |
| @@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) | |||
| 66 | { | 67 | { |
| 67 | struct comm *comm, *tmp; | 68 | struct comm *comm, *tmp; |
| 68 | 69 | ||
| 70 | thread_stack__free(thread); | ||
| 71 | |||
| 69 | if (thread->mg) { | 72 | if (thread->mg) { |
| 70 | map_groups__put(thread->mg); | 73 | map_groups__put(thread->mg); |
| 71 | thread->mg = NULL; | 74 | thread->mg = NULL; |
| @@ -100,15 +103,14 @@ struct comm *thread__exec_comm(const struct thread *thread) | |||
| 100 | return last; | 103 | return last; |
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | /* CHECKME: time should always be 0 if event aren't ordered */ | ||
| 104 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, | 106 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, |
| 105 | bool exec) | 107 | bool exec) |
| 106 | { | 108 | { |
| 107 | struct comm *new, *curr = thread__comm(thread); | 109 | struct comm *new, *curr = thread__comm(thread); |
| 108 | int err; | 110 | int err; |
| 109 | 111 | ||
| 110 | /* Override latest entry if it had no specific time coverage */ | 112 | /* Override the default :tid entry */ |
| 111 | if (!curr->start && !curr->exec) { | 113 | if (!thread->comm_set) { |
| 112 | err = comm__override(curr, str, timestamp, exec); | 114 | err = comm__override(curr, str, timestamp, exec); |
| 113 | if (err) | 115 | if (err) |
| 114 | return err; | 116 | return err; |
| @@ -198,7 +200,6 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
| 198 | } | 200 | } |
| 199 | 201 | ||
| 200 | void thread__find_cpumode_addr_location(struct thread *thread, | 202 | void thread__find_cpumode_addr_location(struct thread *thread, |
| 201 | struct machine *machine, | ||
| 202 | enum map_type type, u64 addr, | 203 | enum map_type type, u64 addr, |
| 203 | struct addr_location *al) | 204 | struct addr_location *al) |
| 204 | { | 205 | { |
| @@ -211,8 +212,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, | |||
| 211 | }; | 212 | }; |
| 212 | 213 | ||
| 213 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { | 214 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { |
| 214 | thread__find_addr_location(thread, machine, cpumodes[i], type, | 215 | thread__find_addr_location(thread, cpumodes[i], type, addr, al); |
| 215 | addr, al); | ||
| 216 | if (al->map) | 216 | if (al->map) |
| 217 | break; | 217 | break; |
| 218 | } | 218 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 8c75fa774706..160fd066a7d1 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include "symbol.h" | 8 | #include "symbol.h" |
| 9 | #include <strlist.h> | 9 | #include <strlist.h> |
| 10 | 10 | ||
| 11 | struct thread_stack; | ||
| 12 | |||
| 11 | struct thread { | 13 | struct thread { |
| 12 | union { | 14 | union { |
| 13 | struct rb_node rb_node; | 15 | struct rb_node rb_node; |
| @@ -23,8 +25,10 @@ struct thread { | |||
| 23 | bool dead; /* if set thread has exited */ | 25 | bool dead; /* if set thread has exited */ |
| 24 | struct list_head comm_list; | 26 | struct list_head comm_list; |
| 25 | int comm_len; | 27 | int comm_len; |
| 28 | u64 db_id; | ||
| 26 | 29 | ||
| 27 | void *priv; | 30 | void *priv; |
| 31 | struct thread_stack *ts; | ||
| 28 | }; | 32 | }; |
| 29 | 33 | ||
| 30 | struct machine; | 34 | struct machine; |
| @@ -54,16 +58,15 @@ void thread__insert_map(struct thread *thread, struct map *map); | |||
| 54 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 58 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); |
| 55 | size_t thread__fprintf(struct thread *thread, FILE *fp); | 59 | size_t thread__fprintf(struct thread *thread, FILE *fp); |
| 56 | 60 | ||
| 57 | void thread__find_addr_map(struct thread *thread, struct machine *machine, | 61 | void thread__find_addr_map(struct thread *thread, |
| 58 | u8 cpumode, enum map_type type, u64 addr, | 62 | u8 cpumode, enum map_type type, u64 addr, |
| 59 | struct addr_location *al); | 63 | struct addr_location *al); |
| 60 | 64 | ||
| 61 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 65 | void thread__find_addr_location(struct thread *thread, |
| 62 | u8 cpumode, enum map_type type, u64 addr, | 66 | u8 cpumode, enum map_type type, u64 addr, |
| 63 | struct addr_location *al); | 67 | struct addr_location *al); |
| 64 | 68 | ||
| 65 | void thread__find_cpumode_addr_location(struct thread *thread, | 69 | void thread__find_cpumode_addr_location(struct thread *thread, |
| 66 | struct machine *machine, | ||
| 67 | enum map_type type, u64 addr, | 70 | enum map_type type, u64 addr, |
| 68 | struct addr_location *al); | 71 | struct addr_location *al); |
| 69 | 72 | ||
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index f11636966a0f..bb2708bbfaca 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
| @@ -39,7 +39,8 @@ struct perf_tool { | |||
| 39 | event_attr_op attr; | 39 | event_attr_op attr; |
| 40 | event_op2 tracing_data; | 40 | event_op2 tracing_data; |
| 41 | event_op2 finished_round, | 41 | event_op2 finished_round, |
| 42 | build_id; | 42 | build_id, |
| 43 | id_index; | ||
| 43 | bool ordered_events; | 44 | bool ordered_events; |
| 44 | bool ordering_requires_timestamps; | 45 | bool ordering_requires_timestamps; |
| 45 | }; | 46 | }; |
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 7419768c38b1..2dcfe9a7c8d0 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
| @@ -26,7 +26,7 @@ static int __report_module(struct addr_location *al, u64 ip, | |||
| 26 | Dwfl_Module *mod; | 26 | Dwfl_Module *mod; |
| 27 | struct dso *dso = NULL; | 27 | struct dso *dso = NULL; |
| 28 | 28 | ||
| 29 | thread__find_addr_location(ui->thread, ui->machine, | 29 | thread__find_addr_location(ui->thread, |
| 30 | PERF_RECORD_MISC_USER, | 30 | PERF_RECORD_MISC_USER, |
| 31 | MAP__FUNCTION, ip, al); | 31 | MAP__FUNCTION, ip, al); |
| 32 | 32 | ||
| @@ -89,7 +89,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, | |||
| 89 | struct addr_location al; | 89 | struct addr_location al; |
| 90 | ssize_t size; | 90 | ssize_t size; |
| 91 | 91 | ||
| 92 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 92 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
| 93 | MAP__FUNCTION, addr, &al); | 93 | MAP__FUNCTION, addr, &al); |
| 94 | if (!al.map) { | 94 | if (!al.map) { |
| 95 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | 95 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); |
| @@ -164,14 +164,14 @@ frame_callback(Dwfl_Frame *state, void *arg) | |||
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 166 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
| 167 | struct machine *machine, struct thread *thread, | 167 | struct thread *thread, |
| 168 | struct perf_sample *data, | 168 | struct perf_sample *data, |
| 169 | int max_stack) | 169 | int max_stack) |
| 170 | { | 170 | { |
| 171 | struct unwind_info ui = { | 171 | struct unwind_info ui = { |
| 172 | .sample = data, | 172 | .sample = data, |
| 173 | .thread = thread, | 173 | .thread = thread, |
| 174 | .machine = machine, | 174 | .machine = thread->mg->machine, |
| 175 | .cb = cb, | 175 | .cb = cb, |
| 176 | .arg = arg, | 176 | .arg = arg, |
| 177 | .max_stack = max_stack, | 177 | .max_stack = max_stack, |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 4d45c0dfe343..371219a6daf1 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
| @@ -284,7 +284,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | |||
| 284 | { | 284 | { |
| 285 | struct addr_location al; | 285 | struct addr_location al; |
| 286 | 286 | ||
| 287 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 287 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
| 288 | MAP__FUNCTION, ip, &al); | 288 | MAP__FUNCTION, ip, &al); |
| 289 | return al.map; | 289 | return al.map; |
| 290 | } | 290 | } |
| @@ -374,7 +374,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, | |||
| 374 | struct addr_location al; | 374 | struct addr_location al; |
| 375 | ssize_t size; | 375 | ssize_t size; |
| 376 | 376 | ||
| 377 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 377 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
| 378 | MAP__FUNCTION, addr, &al); | 378 | MAP__FUNCTION, addr, &al); |
| 379 | if (!al.map) { | 379 | if (!al.map) { |
| 380 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | 380 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); |
| @@ -476,14 +476,13 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as, | |||
| 476 | pr_debug("unwind: put_unwind_info called\n"); | 476 | pr_debug("unwind: put_unwind_info called\n"); |
| 477 | } | 477 | } |
| 478 | 478 | ||
| 479 | static int entry(u64 ip, struct thread *thread, struct machine *machine, | 479 | static int entry(u64 ip, struct thread *thread, |
| 480 | unwind_entry_cb_t cb, void *arg) | 480 | unwind_entry_cb_t cb, void *arg) |
| 481 | { | 481 | { |
| 482 | struct unwind_entry e; | 482 | struct unwind_entry e; |
| 483 | struct addr_location al; | 483 | struct addr_location al; |
| 484 | 484 | ||
| 485 | thread__find_addr_location(thread, machine, | 485 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, |
| 486 | PERF_RECORD_MISC_USER, | ||
| 487 | MAP__FUNCTION, ip, &al); | 486 | MAP__FUNCTION, ip, &al); |
| 488 | 487 | ||
| 489 | e.ip = ip; | 488 | e.ip = ip; |
| @@ -586,21 +585,21 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
| 586 | unw_word_t ip; | 585 | unw_word_t ip; |
| 587 | 586 | ||
| 588 | unw_get_reg(&c, UNW_REG_IP, &ip); | 587 | unw_get_reg(&c, UNW_REG_IP, &ip); |
| 589 | ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; | 588 | ret = ip ? entry(ip, ui->thread, cb, arg) : 0; |
| 590 | } | 589 | } |
| 591 | 590 | ||
| 592 | return ret; | 591 | return ret; |
| 593 | } | 592 | } |
| 594 | 593 | ||
| 595 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 594 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
| 596 | struct machine *machine, struct thread *thread, | 595 | struct thread *thread, |
| 597 | struct perf_sample *data, int max_stack) | 596 | struct perf_sample *data, int max_stack) |
| 598 | { | 597 | { |
| 599 | u64 ip; | 598 | u64 ip; |
| 600 | struct unwind_info ui = { | 599 | struct unwind_info ui = { |
| 601 | .sample = data, | 600 | .sample = data, |
| 602 | .thread = thread, | 601 | .thread = thread, |
| 603 | .machine = machine, | 602 | .machine = thread->mg->machine, |
| 604 | }; | 603 | }; |
| 605 | int ret; | 604 | int ret; |
| 606 | 605 | ||
| @@ -611,7 +610,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
| 611 | if (ret) | 610 | if (ret) |
| 612 | return ret; | 611 | return ret; |
| 613 | 612 | ||
| 614 | ret = entry(ip, thread, machine, cb, arg); | 613 | ret = entry(ip, thread, cb, arg); |
| 615 | if (ret) | 614 | if (ret) |
| 616 | return -ENOMEM; | 615 | return -ENOMEM; |
| 617 | 616 | ||
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index f50b737235eb..12790cf94618 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
| @@ -16,7 +16,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); | |||
| 16 | 16 | ||
| 17 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 17 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
| 18 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 18 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
| 19 | struct machine *machine, | ||
| 20 | struct thread *thread, | 19 | struct thread *thread, |
| 21 | struct perf_sample *data, int max_stack); | 20 | struct perf_sample *data, int max_stack); |
| 22 | /* libunwind specific */ | 21 | /* libunwind specific */ |
| @@ -38,7 +37,6 @@ static inline void unwind__finish_access(struct thread *thread __maybe_unused) { | |||
| 38 | static inline int | 37 | static inline int |
| 39 | unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | 38 | unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, |
| 40 | void *arg __maybe_unused, | 39 | void *arg __maybe_unused, |
| 41 | struct machine *machine __maybe_unused, | ||
| 42 | struct thread *thread __maybe_unused, | 40 | struct thread *thread __maybe_unused, |
| 43 | struct perf_sample *data __maybe_unused, | 41 | struct perf_sample *data __maybe_unused, |
| 44 | int max_stack __maybe_unused) | 42 | int max_stack __maybe_unused) |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 80bfdaa0e2a4..76d23d83eae5 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -154,7 +154,6 @@ extern void set_die_routine(void (*routine)(const char *err, va_list params) NOR | |||
| 154 | 154 | ||
| 155 | extern int prefixcmp(const char *str, const char *prefix); | 155 | extern int prefixcmp(const char *str, const char *prefix); |
| 156 | extern void set_buildid_dir(void); | 156 | extern void set_buildid_dir(void); |
| 157 | extern void disable_buildid_cache(void); | ||
| 158 | 157 | ||
| 159 | static inline const char *skip_prefix(const char *str, const char *prefix) | 158 | static inline const char *skip_prefix(const char *str, const char *prefix) |
| 160 | { | 159 | { |
| @@ -351,4 +350,9 @@ void mem_bswap_32(void *src, int byte_size); | |||
| 351 | 350 | ||
| 352 | const char *get_filename_for_perf_kvm(void); | 351 | const char *get_filename_for_perf_kvm(void); |
| 353 | bool find_process(const char *name); | 352 | bool find_process(const char *name); |
| 353 | |||
| 354 | #ifdef HAVE_ZLIB_SUPPORT | ||
| 355 | int gzip_decompress_to_file(const char *input, int output_fd); | ||
| 356 | #endif | ||
| 357 | |||
| 354 | #endif /* GIT_COMPAT_UTIL_H */ | 358 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index adca69384fcc..5c7dd796979d 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c | |||
| @@ -12,9 +12,16 @@ | |||
| 12 | #include "util.h" | 12 | #include "util.h" |
| 13 | #include "symbol.h" | 13 | #include "symbol.h" |
| 14 | #include "machine.h" | 14 | #include "machine.h" |
| 15 | #include "thread.h" | ||
| 15 | #include "linux/string.h" | 16 | #include "linux/string.h" |
| 16 | #include "debug.h" | 17 | #include "debug.h" |
| 17 | 18 | ||
| 19 | /* | ||
| 20 | * Include definition of find_vdso_map() also used in perf-read-vdso.c for | ||
| 21 | * building perf-read-vdso32 and perf-read-vdsox32. | ||
| 22 | */ | ||
| 23 | #include "find-vdso-map.c" | ||
| 24 | |||
| 18 | #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" | 25 | #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" |
| 19 | 26 | ||
| 20 | struct vdso_file { | 27 | struct vdso_file { |
| @@ -22,10 +29,15 @@ struct vdso_file { | |||
| 22 | bool error; | 29 | bool error; |
| 23 | char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; | 30 | char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; |
| 24 | const char *dso_name; | 31 | const char *dso_name; |
| 32 | const char *read_prog; | ||
| 25 | }; | 33 | }; |
| 26 | 34 | ||
| 27 | struct vdso_info { | 35 | struct vdso_info { |
| 28 | struct vdso_file vdso; | 36 | struct vdso_file vdso; |
| 37 | #if BITS_PER_LONG == 64 | ||
| 38 | struct vdso_file vdso32; | ||
| 39 | struct vdso_file vdsox32; | ||
| 40 | #endif | ||
| 29 | }; | 41 | }; |
| 30 | 42 | ||
| 31 | static struct vdso_info *vdso_info__new(void) | 43 | static struct vdso_info *vdso_info__new(void) |
| @@ -35,42 +47,23 @@ static struct vdso_info *vdso_info__new(void) | |||
| 35 | .temp_file_name = VDSO__TEMP_FILE_NAME, | 47 | .temp_file_name = VDSO__TEMP_FILE_NAME, |
| 36 | .dso_name = DSO__NAME_VDSO, | 48 | .dso_name = DSO__NAME_VDSO, |
| 37 | }, | 49 | }, |
| 50 | #if BITS_PER_LONG == 64 | ||
| 51 | .vdso32 = { | ||
| 52 | .temp_file_name = VDSO__TEMP_FILE_NAME, | ||
| 53 | .dso_name = DSO__NAME_VDSO32, | ||
| 54 | .read_prog = "perf-read-vdso32", | ||
| 55 | }, | ||
| 56 | .vdsox32 = { | ||
| 57 | .temp_file_name = VDSO__TEMP_FILE_NAME, | ||
| 58 | .dso_name = DSO__NAME_VDSOX32, | ||
| 59 | .read_prog = "perf-read-vdsox32", | ||
| 60 | }, | ||
| 61 | #endif | ||
| 38 | }; | 62 | }; |
| 39 | 63 | ||
| 40 | return memdup(&vdso_info_init, sizeof(vdso_info_init)); | 64 | return memdup(&vdso_info_init, sizeof(vdso_info_init)); |
| 41 | } | 65 | } |
| 42 | 66 | ||
| 43 | static int find_vdso_map(void **start, void **end) | ||
| 44 | { | ||
| 45 | FILE *maps; | ||
| 46 | char line[128]; | ||
| 47 | int found = 0; | ||
| 48 | |||
| 49 | maps = fopen("/proc/self/maps", "r"); | ||
| 50 | if (!maps) { | ||
| 51 | pr_err("vdso: cannot open maps\n"); | ||
| 52 | return -1; | ||
| 53 | } | ||
| 54 | |||
| 55 | while (!found && fgets(line, sizeof(line), maps)) { | ||
| 56 | int m = -1; | ||
| 57 | |||
| 58 | /* We care only about private r-x mappings. */ | ||
| 59 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
| 60 | start, end, &m)) | ||
| 61 | continue; | ||
| 62 | if (m < 0) | ||
| 63 | continue; | ||
| 64 | |||
| 65 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
| 66 | sizeof(VDSO__MAP_NAME) - 1)) | ||
| 67 | found = 1; | ||
| 68 | } | ||
| 69 | |||
| 70 | fclose(maps); | ||
| 71 | return !found; | ||
| 72 | } | ||
| 73 | |||
| 74 | static char *get_file(struct vdso_file *vdso_file) | 67 | static char *get_file(struct vdso_file *vdso_file) |
| 75 | { | 68 | { |
| 76 | char *vdso = NULL; | 69 | char *vdso = NULL; |
| @@ -117,6 +110,12 @@ void vdso__exit(struct machine *machine) | |||
| 117 | 110 | ||
| 118 | if (vdso_info->vdso.found) | 111 | if (vdso_info->vdso.found) |
| 119 | unlink(vdso_info->vdso.temp_file_name); | 112 | unlink(vdso_info->vdso.temp_file_name); |
| 113 | #if BITS_PER_LONG == 64 | ||
| 114 | if (vdso_info->vdso32.found) | ||
| 115 | unlink(vdso_info->vdso32.temp_file_name); | ||
| 116 | if (vdso_info->vdsox32.found) | ||
| 117 | unlink(vdso_info->vdsox32.temp_file_name); | ||
| 118 | #endif | ||
| 120 | 119 | ||
| 121 | zfree(&machine->vdso_info); | 120 | zfree(&machine->vdso_info); |
| 122 | } | 121 | } |
| @@ -135,6 +134,153 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name, | |||
| 135 | return dso; | 134 | return dso; |
| 136 | } | 135 | } |
| 137 | 136 | ||
| 137 | #if BITS_PER_LONG == 64 | ||
| 138 | |||
| 139 | static enum dso_type machine__thread_dso_type(struct machine *machine, | ||
| 140 | struct thread *thread) | ||
| 141 | { | ||
| 142 | enum dso_type dso_type = DSO__TYPE_UNKNOWN; | ||
| 143 | struct map *map; | ||
| 144 | struct dso *dso; | ||
| 145 | |||
| 146 | map = map_groups__first(thread->mg, MAP__FUNCTION); | ||
| 147 | for (; map ; map = map_groups__next(map)) { | ||
| 148 | dso = map->dso; | ||
| 149 | if (!dso || dso->long_name[0] != '/') | ||
| 150 | continue; | ||
| 151 | dso_type = dso__type(dso, machine); | ||
| 152 | if (dso_type != DSO__TYPE_UNKNOWN) | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | |||
| 156 | return dso_type; | ||
| 157 | } | ||
| 158 | |||
| 159 | static int vdso__do_copy_compat(FILE *f, int fd) | ||
| 160 | { | ||
| 161 | char buf[4096]; | ||
| 162 | size_t count; | ||
| 163 | |||
| 164 | while (1) { | ||
| 165 | count = fread(buf, 1, sizeof(buf), f); | ||
| 166 | if (ferror(f)) | ||
| 167 | return -errno; | ||
| 168 | if (feof(f)) | ||
| 169 | break; | ||
| 170 | if (count && writen(fd, buf, count) != (ssize_t)count) | ||
| 171 | return -errno; | ||
| 172 | } | ||
| 173 | |||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int vdso__copy_compat(const char *prog, int fd) | ||
| 178 | { | ||
| 179 | FILE *f; | ||
| 180 | int err; | ||
| 181 | |||
| 182 | f = popen(prog, "r"); | ||
| 183 | if (!f) | ||
| 184 | return -errno; | ||
| 185 | |||
| 186 | err = vdso__do_copy_compat(f, fd); | ||
| 187 | |||
| 188 | if (pclose(f) == -1) | ||
| 189 | return -errno; | ||
| 190 | |||
| 191 | return err; | ||
| 192 | } | ||
| 193 | |||
| 194 | static int vdso__create_compat_file(const char *prog, char *temp_name) | ||
| 195 | { | ||
| 196 | int fd, err; | ||
| 197 | |||
| 198 | fd = mkstemp(temp_name); | ||
| 199 | if (fd < 0) | ||
| 200 | return -errno; | ||
| 201 | |||
| 202 | err = vdso__copy_compat(prog, fd); | ||
| 203 | |||
| 204 | if (close(fd) == -1) | ||
| 205 | return -errno; | ||
| 206 | |||
| 207 | return err; | ||
| 208 | } | ||
| 209 | |||
| 210 | static const char *vdso__get_compat_file(struct vdso_file *vdso_file) | ||
| 211 | { | ||
| 212 | int err; | ||
| 213 | |||
| 214 | if (vdso_file->found) | ||
| 215 | return vdso_file->temp_file_name; | ||
| 216 | |||
| 217 | if (vdso_file->error) | ||
| 218 | return NULL; | ||
| 219 | |||
| 220 | err = vdso__create_compat_file(vdso_file->read_prog, | ||
| 221 | vdso_file->temp_file_name); | ||
| 222 | if (err) { | ||
| 223 | pr_err("%s failed, error %d\n", vdso_file->read_prog, err); | ||
| 224 | vdso_file->error = true; | ||
| 225 | return NULL; | ||
| 226 | } | ||
| 227 | |||
| 228 | vdso_file->found = true; | ||
| 229 | |||
| 230 | return vdso_file->temp_file_name; | ||
| 231 | } | ||
| 232 | |||
| 233 | static struct dso *vdso__findnew_compat(struct machine *machine, | ||
| 234 | struct vdso_file *vdso_file) | ||
| 235 | { | ||
| 236 | const char *file_name; | ||
| 237 | struct dso *dso; | ||
| 238 | |||
| 239 | dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true); | ||
| 240 | if (dso) | ||
| 241 | return dso; | ||
| 242 | |||
| 243 | file_name = vdso__get_compat_file(vdso_file); | ||
| 244 | if (!file_name) | ||
| 245 | return NULL; | ||
| 246 | |||
| 247 | return vdso__new(machine, vdso_file->dso_name, file_name); | ||
| 248 | } | ||
| 249 | |||
| 250 | static int vdso__dso_findnew_compat(struct machine *machine, | ||
| 251 | struct thread *thread, | ||
| 252 | struct vdso_info *vdso_info, | ||
| 253 | struct dso **dso) | ||
| 254 | { | ||
| 255 | enum dso_type dso_type; | ||
| 256 | |||
| 257 | dso_type = machine__thread_dso_type(machine, thread); | ||
| 258 | |||
| 259 | #ifndef HAVE_PERF_READ_VDSO32 | ||
| 260 | if (dso_type == DSO__TYPE_32BIT) | ||
| 261 | return 0; | ||
| 262 | #endif | ||
| 263 | #ifndef HAVE_PERF_READ_VDSOX32 | ||
| 264 | if (dso_type == DSO__TYPE_X32BIT) | ||
| 265 | return 0; | ||
| 266 | #endif | ||
| 267 | |||
| 268 | switch (dso_type) { | ||
| 269 | case DSO__TYPE_32BIT: | ||
| 270 | *dso = vdso__findnew_compat(machine, &vdso_info->vdso32); | ||
| 271 | return 1; | ||
| 272 | case DSO__TYPE_X32BIT: | ||
| 273 | *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32); | ||
| 274 | return 1; | ||
| 275 | case DSO__TYPE_UNKNOWN: | ||
| 276 | case DSO__TYPE_64BIT: | ||
| 277 | default: | ||
| 278 | return 0; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | #endif | ||
| 283 | |||
| 138 | struct dso *vdso__dso_findnew(struct machine *machine, | 284 | struct dso *vdso__dso_findnew(struct machine *machine, |
| 139 | struct thread *thread __maybe_unused) | 285 | struct thread *thread __maybe_unused) |
| 140 | { | 286 | { |
| @@ -148,6 +294,11 @@ struct dso *vdso__dso_findnew(struct machine *machine, | |||
| 148 | if (!vdso_info) | 294 | if (!vdso_info) |
| 149 | return NULL; | 295 | return NULL; |
| 150 | 296 | ||
| 297 | #if BITS_PER_LONG == 64 | ||
| 298 | if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso)) | ||
| 299 | return dso; | ||
| 300 | #endif | ||
| 301 | |||
| 151 | dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); | 302 | dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); |
| 152 | if (!dso) { | 303 | if (!dso) { |
| 153 | char *file; | 304 | char *file; |
| @@ -164,5 +315,7 @@ struct dso *vdso__dso_findnew(struct machine *machine, | |||
| 164 | 315 | ||
| 165 | bool dso__is_vdso(struct dso *dso) | 316 | bool dso__is_vdso(struct dso *dso) |
| 166 | { | 317 | { |
| 167 | return !strcmp(dso->short_name, DSO__NAME_VDSO); | 318 | return !strcmp(dso->short_name, DSO__NAME_VDSO) || |
| 319 | !strcmp(dso->short_name, DSO__NAME_VDSO32) || | ||
| 320 | !strcmp(dso->short_name, DSO__NAME_VDSOX32); | ||
| 168 | } | 321 | } |
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h index af9d6929a215..d97da1616f0c 100644 --- a/tools/perf/util/vdso.h +++ b/tools/perf/util/vdso.h | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | #define VDSO__MAP_NAME "[vdso]" | 8 | #define VDSO__MAP_NAME "[vdso]" |
| 9 | 9 | ||
| 10 | #define DSO__NAME_VDSO "[vdso]" | 10 | #define DSO__NAME_VDSO "[vdso]" |
| 11 | #define DSO__NAME_VDSO32 "[vdso32]" | ||
| 12 | #define DSO__NAME_VDSOX32 "[vdsox32]" | ||
| 11 | 13 | ||
| 12 | static inline bool is_vdso_map(const char *filename) | 14 | static inline bool is_vdso_map(const char *filename) |
| 13 | { | 15 | { |
diff --git a/tools/perf/util/zlib.c b/tools/perf/util/zlib.c new file mode 100644 index 000000000000..495a449fc25c --- /dev/null +++ b/tools/perf/util/zlib.c | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <unistd.h> | ||
| 3 | #include <sys/stat.h> | ||
| 4 | #include <sys/mman.h> | ||
| 5 | #include <zlib.h> | ||
| 6 | |||
| 7 | #include "util/util.h" | ||
| 8 | #include "util/debug.h" | ||
| 9 | |||
| 10 | |||
| 11 | #define CHUNK_SIZE 16384 | ||
| 12 | |||
| 13 | int gzip_decompress_to_file(const char *input, int output_fd) | ||
| 14 | { | ||
| 15 | int ret = Z_STREAM_ERROR; | ||
| 16 | int input_fd; | ||
| 17 | void *ptr; | ||
| 18 | int len; | ||
| 19 | struct stat stbuf; | ||
| 20 | unsigned char buf[CHUNK_SIZE]; | ||
| 21 | z_stream zs = { | ||
| 22 | .zalloc = Z_NULL, | ||
| 23 | .zfree = Z_NULL, | ||
| 24 | .opaque = Z_NULL, | ||
| 25 | .avail_in = 0, | ||
| 26 | .next_in = Z_NULL, | ||
| 27 | }; | ||
| 28 | |||
| 29 | input_fd = open(input, O_RDONLY); | ||
| 30 | if (input_fd < 0) | ||
| 31 | return -1; | ||
| 32 | |||
| 33 | if (fstat(input_fd, &stbuf) < 0) | ||
| 34 | goto out_close; | ||
| 35 | |||
| 36 | ptr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0); | ||
| 37 | if (ptr == MAP_FAILED) | ||
| 38 | goto out_close; | ||
| 39 | |||
| 40 | if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) | ||
| 41 | goto out_unmap; | ||
| 42 | |||
| 43 | zs.next_in = ptr; | ||
| 44 | zs.avail_in = stbuf.st_size; | ||
| 45 | |||
| 46 | do { | ||
| 47 | zs.next_out = buf; | ||
| 48 | zs.avail_out = CHUNK_SIZE; | ||
| 49 | |||
| 50 | ret = inflate(&zs, Z_NO_FLUSH); | ||
| 51 | switch (ret) { | ||
| 52 | case Z_NEED_DICT: | ||
| 53 | ret = Z_DATA_ERROR; | ||
| 54 | /* fall through */ | ||
| 55 | case Z_DATA_ERROR: | ||
| 56 | case Z_MEM_ERROR: | ||
| 57 | goto out; | ||
| 58 | default: | ||
| 59 | break; | ||
| 60 | } | ||
| 61 | |||
| 62 | len = CHUNK_SIZE - zs.avail_out; | ||
| 63 | if (writen(output_fd, buf, len) != len) { | ||
| 64 | ret = Z_DATA_ERROR; | ||
| 65 | goto out; | ||
| 66 | } | ||
| 67 | |||
| 68 | } while (ret != Z_STREAM_END); | ||
| 69 | |||
| 70 | out: | ||
| 71 | inflateEnd(&zs); | ||
| 72 | out_unmap: | ||
| 73 | munmap(ptr, stbuf.st_size); | ||
| 74 | out_close: | ||
| 75 | close(input_fd); | ||
| 76 | |||
| 77 | return ret == Z_STREAM_END ? 0 : -1; | ||
| 78 | } | ||
