Pull perf updates from Ingo Molnar: "Kernel side changes: - Ftrace is one of the last W^X violators (after this only KLP is left). These patches move it over to the generic text_poke() interface and thereby get rid of this oddity. This requires a surprising amount of surgery, by Peter Zijlstra. - x86/AMD PMUs: add support for 'Large Increment per Cycle Events' to count certain types of events that have a special, quirky hw ABI (by Kim Phillips) - kprobes fixes by Masami Hiramatsu Lots of tooling updates as well, the following subcommands were updated: annotate/report/top, c2c, clang, record, report/top TUI, sched timehist, tests; plus updates were done to the gtk ui, libperf, headers and the parser" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (57 commits) perf/x86/amd: Add support for Large Increment per Cycle Events perf/x86/amd: Constrain Large Increment per Cycle events perf/x86/intel/rapl: Add Comet Lake support tracing: Initialize ret in syscall_enter_define_fields() perf header: Use last modification time for timestamp perf c2c: Fix return type for histogram sorting comparision functions perf beauty sockaddr: Fix augmented syscall format warning perf/ui/gtk: Fix gtk2 build perf ui gtk: Add missing zalloc object perf tools: Use %define api.pure full instead of %pure-parser libperf: Setup initial evlist::all_cpus value perf report: Fix no libunwind compiled warning break s390 issue perf tools: Support --prefix/--prefix-strip perf report: Clarify in help that --children is default tools build: Fix test-clang.cpp with Clang 8+ perf clang: Fix build with Clang 9 kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic tools lib: Fix builds when glibc contains strlcpy() perf report/top: Make 'e' visible in the help and make it toggle showing callchains perf report/top: Do not offer annotation for symbols without samples ...
Unified · Split
| @@ -53,8 +53,8 @@ obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o · 52 unmodified lines | |||
| 53 | obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o | 53 | obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o |
| 54 | obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o | 54 | obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o |
| 55 | obj-$(CONFIG_FUNCTION_TRACER) += entry-ftrace.o | 55 | obj-$(CONFIG_FUNCTION_TRACER) += entry-ftrace.o |
| 56 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o | 56 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o patch.o |
| 57 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o | 57 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o patch.o |
| 58 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o | 58 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o |
| 59 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 59 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
| 60 | # Main staffs in KPROBES are in arch/arm/probes/ . | 60 | # Main staffs in KPROBES are in arch/arm/probes/ . |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | |||
| 22 | #include <asm/ftrace.h> | 22 | #include <asm/ftrace.h> |
| 23 | #include <asm/insn.h> | 23 | #include <asm/insn.h> |
| 24 | #include <asm/set_memory.h> | 24 | #include <asm/set_memory.h> |
| 25 | #include <asm/patch.h> | ||
| 25 | 26 | ||
| 26 | #ifdef CONFIG_THUMB2_KERNEL | 27 | #ifdef CONFIG_THUMB2_KERNEL |
| 27 | #define NOP 0xf85deb04 /* pop.w {lr} */ | 28 | #define NOP 0xf85deb04 /* pop.w {lr} */ |
| @@ -35,9 +36,7 @@ static int __ftrace_modify_code(void *data) · 7 unmodified lines | |||
| 35 | { | 36 | { |
| 36 | int *command = data; | 37 | int *command = data; |
| 37 | 38 | ||
| 38 | set_kernel_text_rw(); | ||
| 39 | ftrace_modify_all_code(*command); | 39 | ftrace_modify_all_code(*command); |
| 40 | set_kernel_text_ro(); | ||
| 41 | 40 | ||
| 42 | return 0; | 41 | return 0; |
| 43 | } | 42 | } |
| @@ -59,13 +58,11 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) · 15 unmodified lines | |||
| 59 | 58 | ||
| 60 | int ftrace_arch_code_modify_prepare(void) | 59 | int ftrace_arch_code_modify_prepare(void) |
| 61 | { | 60 | { |
| 62 | set_all_modules_text_rw(); | ||
| 63 | return 0; | 61 | return 0; |
| 64 | } | 62 | } |
| 65 | 63 | ||
| 66 | int ftrace_arch_code_modify_post_process(void) | 64 | int ftrace_arch_code_modify_post_process(void) |
| 67 | { | 65 | { |
| 68 | set_all_modules_text_ro(); | ||
| 69 | /* Make sure any TLB misses during machine stop are cleared. */ | 66 | /* Make sure any TLB misses during machine stop are cleared. */ |
| 70 | flush_tlb_all(); | 67 | flush_tlb_all(); |
| 71 | return 0; | 68 | return 0; |
| @@ -97,10 +94,7 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old, · 25 unmodified lines | |||
| 97 | return -EINVAL; | 94 | return -EINVAL; |
| 98 | } | 95 | } |
| 99 | 96 | ||
| 100 | if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) | 97 | __patch_text((void *)pc, new); |
| 101 | return -EPERM; | ||
| 102 | |||
| 103 | flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); | ||
| 104 | 98 | ||
| 105 | return 0; | 99 | return 0; |
| 106 | } | 100 | } |
| @@ -89,18 +89,6 @@ int __init ftrace_dyn_arch_init(void) · 88 unmodified lines | |||
| 89 | return 0; | 89 | return 0; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | int ftrace_arch_code_modify_prepare(void) | ||
| 93 | { | ||
| 94 | set_all_modules_text_rw(); | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | int ftrace_arch_code_modify_post_process(void) | ||
| 99 | { | ||
| 100 | set_all_modules_text_ro(); | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static unsigned long gen_sethi_insn(unsigned long addr) | 92 | static unsigned long gen_sethi_insn(unsigned long addr) |
| 105 | { | 93 | { |
| 106 | unsigned long opcode = 0x46000000; | 94 | unsigned long opcode = 0x46000000; |
| @@ -14,6 +14,10 @@ · 13 unmodified lines | |||
| 14 | static DEFINE_PER_CPU(unsigned long, perf_nmi_tstamp); | 14 | static DEFINE_PER_CPU(unsigned long, perf_nmi_tstamp); |
| 15 | static unsigned long perf_nmi_window; | 15 | static unsigned long perf_nmi_window; |
| 16 | 16 | ||
| 17 | /* AMD Event 0xFFF: Merge. Used with Large Increment per Cycle events */ | ||
| 18 | #define AMD_MERGE_EVENT ((0xFULL << 32) | 0xFFULL) | ||
| 19 | #define AMD_MERGE_EVENT_ENABLE (AMD_MERGE_EVENT | ARCH_PERFMON_EVENTSEL_ENABLE) | ||
| 20 | |||
| 17 | static __initconst const u64 amd_hw_cache_event_ids | 21 | static __initconst const u64 amd_hw_cache_event_ids |
| 18 | [PERF_COUNT_HW_CACHE_MAX] | 22 | [PERF_COUNT_HW_CACHE_MAX] |
| 19 | [PERF_COUNT_HW_CACHE_OP_MAX] | 23 | [PERF_COUNT_HW_CACHE_OP_MAX] |
| @@ -301,6 +305,25 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel) · 281 unmodified lines | |||
| 301 | return offset; | 305 | return offset; |
| 302 | } | 306 | } |
| 303 | 307 | ||
| 308 | /* | ||
| 309 | * AMD64 events are detected based on their event codes. | ||
| 310 | */ | ||
| 311 | static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) | ||
| 312 | { | ||
| 313 | return ((hwc->config >> 24) & 0x0f00) | (hwc->config & 0x00ff); | ||
| 314 | } | ||
| 315 | |||
| 316 | static inline bool amd_is_pair_event_code(struct hw_perf_event *hwc) | ||
| 317 | { | ||
| 318 | if (!(x86_pmu.flags & PMU_FL_PAIR)) | ||
| 319 | return false; | ||
| 320 | |||
| 321 | switch (amd_get_event_code(hwc)) { | ||
| 322 | case 0x003: return true; /* Retired SSE/AVX FLOPs */ | ||
| 323 | default: return false; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 304 | static int amd_core_hw_config(struct perf_event *event) | 327 | static int amd_core_hw_config(struct perf_event *event) |
| 305 | { | 328 | { |
| 306 | if (event->attr.exclude_host && event->attr.exclude_guest) | 329 | if (event->attr.exclude_host && event->attr.exclude_guest) |
| @@ -316,15 +339,10 @@ static int amd_core_hw_config(struct perf_event *event) · 9 unmodified lines | |||
| 316 | else if (event->attr.exclude_guest) | 339 | else if (event->attr.exclude_guest) |
| 317 | event->hw.config |= AMD64_EVENTSEL_HOSTONLY; | 340 | event->hw.config |= AMD64_EVENTSEL_HOSTONLY; |
| 318 | 341 | ||
| 319 | return 0; | 342 | if ((x86_pmu.flags & PMU_FL_PAIR) && amd_is_pair_event_code(&event->hw)) |
| 320 | } | 343 | event->hw.flags |= PERF_X86_EVENT_PAIR; |
| 321 | 344 | ||
| 322 | /* | 345 | return 0; |
| 323 | * AMD64 events are detected based on their event codes. | ||
| 324 | */ | ||
| 325 | static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) | ||
| 326 | { | ||
| 327 | return ((hwc->config >> 24) & 0x0f00) | (hwc->config & 0x00ff); | ||
| 328 | } | 346 | } |
| 329 | 347 | ||
| 330 | static inline int amd_is_nb_event(struct hw_perf_event *hwc) | 348 | static inline int amd_is_nb_event(struct hw_perf_event *hwc) |
| @@ -855,6 +873,29 @@ amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, int idx, · 524 unmodified lines | |||
| 855 | } | 873 | } |
| 856 | } | 874 | } |
| 857 | 875 | ||
| 876 | static struct event_constraint pair_constraint; | ||
| 877 | |||
| 878 | static struct event_constraint * | ||
| 879 | amd_get_event_constraints_f17h(struct cpu_hw_events *cpuc, int idx, | ||
| 880 | struct perf_event *event) | ||
| 881 | { | ||
| 882 | struct hw_perf_event *hwc = &event->hw; | ||
| 883 | |||
| 884 | if (amd_is_pair_event_code(hwc)) | ||
| 885 | return &pair_constraint; | ||
| 886 | |||
| 887 | return &unconstrained; | ||
| 888 | } | ||
| 889 | |||
| 890 | static void amd_put_event_constraints_f17h(struct cpu_hw_events *cpuc, | ||
| 891 | struct perf_event *event) | ||
| 892 | { | ||
| 893 | struct hw_perf_event *hwc = &event->hw; | ||
| 894 | |||
| 895 | if (is_counter_pair(hwc)) | ||
| 896 | --cpuc->n_pair; | ||
| 897 | } | ||
| 898 | |||
| 858 | static ssize_t amd_event_sysfs_show(char *page, u64 config) | 899 | static ssize_t amd_event_sysfs_show(char *page, u64 config) |
| 859 | { | 900 | { |
| 860 | u64 event = (config & ARCH_PERFMON_EVENTSEL_EVENT) | | 901 | u64 event = (config & ARCH_PERFMON_EVENTSEL_EVENT) | |
| @@ -898,33 +939,15 @@ static __initconst const struct x86_pmu amd_pmu = { · 37 unmodified lines | |||
| 898 | 939 | ||
| 899 | static int __init amd_core_pmu_init(void) | 940 | static int __init amd_core_pmu_init(void) |
| 900 | { | 941 | { |
| 942 | u64 even_ctr_mask = 0ULL; | ||
| 943 | int i; | ||
| 944 | |||
| 901 | if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) | 945 | if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) |
| 902 | return 0; | 946 | return 0; |
| 903 | 947 | ||
| 904 | /* Avoid calulating the value each time in the NMI handler */ | 948 | /* Avoid calculating the value each time in the NMI handler */ |
| 905 | perf_nmi_window = msecs_to_jiffies(100); | 949 | perf_nmi_window = msecs_to_jiffies(100); |
| 906 | 950 | ||
| 907 | switch (boot_cpu_data.x86) { | ||
| 908 | case 0x15: | ||
| 909 | pr_cont("Fam15h "); | ||
| 910 | x86_pmu.get_event_constraints = amd_get_event_constraints_f15h; | ||
| 911 | break; | ||
| 912 | case 0x17: | ||
| 913 | pr_cont("Fam17h "); | ||
| 914 | /* | ||
| 915 | * In family 17h, there are no event constraints in the PMC hardware. | ||
| 916 | * We fallback to using default amd_get_event_constraints. | ||
| 917 | */ | ||
| 918 | break; | ||
| 919 | case 0x18: | ||
| 920 | pr_cont("Fam18h "); | ||
| 921 | /* Using default amd_get_event_constraints. */ | ||
| 922 | break; | ||
| 923 | default: | ||
| 924 | pr_err("core perfctr but no constraints; unknown hardware!\n"); | ||
| 925 | return -ENODEV; | ||
| 926 | } | ||
| 927 | |||
| 928 | /* | 951 | /* |
| 929 | * If core performance counter extensions exists, we must use | 952 | * If core performance counter extensions exists, we must use |
| 930 | * MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also | 953 | * MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also |
| @@ -939,6 +962,32 @@ static int __init amd_core_pmu_init(void) · 8 unmodified lines | |||
| 939 | */ | 962 | */ |
| 940 | x86_pmu.amd_nb_constraints = 0; | 963 | x86_pmu.amd_nb_constraints = 0; |
| 941 | 964 | ||
| 965 | if (boot_cpu_data.x86 == 0x15) { | ||
| 966 | pr_cont("Fam15h "); | ||
| 967 | x86_pmu.get_event_constraints = amd_get_event_constraints_f15h; | ||
| 968 | } | ||
| 969 | if (boot_cpu_data.x86 >= 0x17) { | ||
| 970 | pr_cont("Fam17h+ "); | ||
| 971 | /* | ||
| 972 | * Family 17h and compatibles have constraints for Large | ||
| 973 | * Increment per Cycle events: they may only be assigned an | ||
| 974 | * even numbered counter that has a consecutive adjacent odd | ||
| 975 | * numbered counter following it. | ||
| 976 | */ | ||
| 977 | for (i = 0; i < x86_pmu.num_counters - 1; i += 2) | ||
| 978 | even_ctr_mask |= 1 << i; | ||
| 979 | |||
| 980 | pair_constraint = (struct event_constraint) | ||
| 981 | __EVENT_CONSTRAINT(0, even_ctr_mask, 0, | ||
| 982 | x86_pmu.num_counters / 2, 0, | ||
| 983 | PERF_X86_EVENT_PAIR); | ||
| 984 | |||
| 985 | x86_pmu.get_event_constraints = amd_get_event_constraints_f17h; | ||
| 986 | x86_pmu.put_event_constraints = amd_put_event_constraints_f17h; | ||
| 987 | x86_pmu.perf_ctr_pair_en = AMD_MERGE_EVENT_ENABLE; | ||
| 988 | x86_pmu.flags |= PMU_FL_PAIR; | ||
| 989 | } | ||
| 990 | |||
| 942 | pr_cont("core perfctr, "); | 991 | pr_cont("core perfctr, "); |
| 943 | return 0; | 992 | return 0; |
| 944 | } | 993 | } |
| @@ -618,6 +618,7 @@ void x86_pmu_disable_all(void) · 617 unmodified lines | |||
| 618 | int idx; | 618 | int idx; |
| 619 | 619 | ||
| 620 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | 620 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { |
| 621 | struct hw_perf_event *hwc = &cpuc->events[idx]->hw; | ||
| 621 | u64 val; | 622 | u64 val; |
| 622 | 623 | ||
| 623 | if (!test_bit(idx, cpuc->active_mask)) | 624 | if (!test_bit(idx, cpuc->active_mask)) |
| @@ -627,6 +628,8 @@ void x86_pmu_disable_all(void) · 3 unmodified lines | |||
| 627 | continue; | 628 | continue; |
| 628 | val &= ~ARCH_PERFMON_EVENTSEL_ENABLE; | 629 | val &= ~ARCH_PERFMON_EVENTSEL_ENABLE; |
| 629 | wrmsrl(x86_pmu_config_addr(idx), val); | 630 | wrmsrl(x86_pmu_config_addr(idx), val); |
| 631 | if (is_counter_pair(hwc)) | ||
| 632 | wrmsrl(x86_pmu_config_addr(idx + 1), 0); | ||
| 630 | } | 633 | } |
| 631 | } | 634 | } |
| 632 | 635 | ||
| @@ -699,7 +702,7 @@ struct sched_state { · 66 unmodified lines | |||
| 699 | int counter; /* counter index */ | 702 | int counter; /* counter index */ |
| 700 | int unassigned; /* number of events to be assigned left */ | 703 | int unassigned; /* number of events to be assigned left */ |
| 701 | int nr_gp; /* number of GP counters used */ | 704 | int nr_gp; /* number of GP counters used */ |
| 702 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 705 | u64 used; |
| 703 | }; | 706 | }; |
| 704 | 707 | ||
| 705 | /* Total max is X86_PMC_IDX_MAX, but we are O(n!) limited */ | 708 | /* Total max is X86_PMC_IDX_MAX, but we are O(n!) limited */ |
| @@ -756,8 +759,12 @@ static bool perf_sched_restore_state(struct perf_sched *sched) · 50 unmodified lines | |||
| 756 | sched->saved_states--; | 759 | sched->saved_states--; |
| 757 | sched->state = sched->saved[sched->saved_states]; | 760 | sched->state = sched->saved[sched->saved_states]; |
| 758 | 761 | ||
| 759 | /* continue with next counter: */ | 762 | /* this assignment didn't work out */ |
| 760 | clear_bit(sched->state.counter++, sched->state.used); | 763 | /* XXX broken vs EVENT_PAIR */ |
| 764 | sched->state.used &= ~BIT_ULL(sched->state.counter); | ||
| 765 | |||
| 766 | /* try the next one */ | ||
| 767 | sched->state.counter++; | ||
| 761 | 768 | ||
| 762 | return true; | 769 | return true; |
| 763 | } | 770 | } |
| @@ -782,20 +789,32 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) · 18 unmodified lines | |||
| 782 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { | 789 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { |
| 783 | idx = INTEL_PMC_IDX_FIXED; | 790 | idx = INTEL_PMC_IDX_FIXED; |
| 784 | for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) { | 791 | for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) { |
| 785 | if (!__test_and_set_bit(idx, sched->state.used)) | 792 | u64 mask = BIT_ULL(idx); |
| 786 | goto done; | 793 | |
| 794 | if (sched->state.used & mask) | ||
| 795 | continue; | ||
| 796 | |||
| 797 | sched->state.used |= mask; | ||
| 798 | goto done; | ||
| 787 | } | 799 | } |
| 788 | } | 800 | } |
| 789 | 801 | ||
| 790 | /* Grab the first unused counter starting with idx */ | 802 | /* Grab the first unused counter starting with idx */ |
| 791 | idx = sched->state.counter; | 803 | idx = sched->state.counter; |
| 792 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { | 804 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { |
| 793 | if (!__test_and_set_bit(idx, sched->state.used)) { | 805 | u64 mask = BIT_ULL(idx); |
| 794 | if (sched->state.nr_gp++ >= sched->max_gp) | ||
| 795 | return false; | ||
| 796 | 806 | ||
| 797 | goto done; | 807 | if (c->flags & PERF_X86_EVENT_PAIR) |
| 798 | } | 808 | mask |= mask << 1; |
| 809 | |||
| 810 | if (sched->state.used & mask) | ||
| 811 | continue; | ||
| 812 | |||
| 813 | if (sched->state.nr_gp++ >= sched->max_gp) | ||
| 814 | return false; | ||
| 815 | |||
| 816 | sched->state.used |= mask; | ||
| 817 | goto done; | ||
| 799 | } | 818 | } |
| 800 | 819 | ||
| 801 | return false; | 820 | return false; |
| @@ -872,12 +891,10 @@ EXPORT_SYMBOL_GPL(perf_assign_events); · 70 unmodified lines | |||
| 872 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | 891 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) |
| 873 | { | 892 | { |
| 874 | struct event_constraint *c; | 893 | struct event_constraint *c; |
| 875 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
| 876 | struct perf_event *e; | 894 | struct perf_event *e; |
| 877 | int n0, i, wmin, wmax, unsched = 0; | 895 | int n0, i, wmin, wmax, unsched = 0; |
| 878 | struct hw_perf_event *hwc; | 896 | struct hw_perf_event *hwc; |
| 879 | 897 | u64 used_mask = 0; | |
| 880 | bitmap_zero(used_mask, X86_PMC_IDX_MAX); | ||
| 881 | 898 | ||
| 882 | /* | 899 | /* |
| 883 | * Compute the number of events already present; see x86_pmu_add(), | 900 | * Compute the number of events already present; see x86_pmu_add(), |
| @@ -920,6 +937,8 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) · 36 unmodified lines | |||
| 920 | * fastpath, try to reuse previous register | 937 | * fastpath, try to reuse previous register |
| 921 | */ | 938 | */ |
| 922 | for (i = 0; i < n; i++) { | 939 | for (i = 0; i < n; i++) { |
| 940 | u64 mask; | ||
| 941 | |||
| 923 | hwc = &cpuc->event_list[i]->hw; | 942 | hwc = &cpuc->event_list[i]->hw; |
| 924 | c = cpuc->event_constraint[i]; | 943 | c = cpuc->event_constraint[i]; |
| 925 | 944 | ||
| @@ -931,11 +950,16 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) · 5 unmodified lines | |||
| 931 | if (!test_bit(hwc->idx, c->idxmsk)) | 950 | if (!test_bit(hwc->idx, c->idxmsk)) |
| 932 | break; | 951 | break; |
| 933 | 952 | ||
| 953 | mask = BIT_ULL(hwc->idx); | ||
| 954 | if (is_counter_pair(hwc)) | ||
| 955 | mask |= mask << 1; | ||
| 956 | |||
| 934 | /* not already used */ | 957 | /* not already used */ |
| 935 | if (test_bit(hwc->idx, used_mask)) | 958 | if (used_mask & mask) |
| 936 | break; | 959 | break; |
| 937 | 960 | ||
| 938 | __set_bit(hwc->idx, used_mask); | 961 | used_mask |= mask; |
| 962 | |||
| 939 | if (assign) | 963 | if (assign) |
| 940 | assign[i] = hwc->idx; | 964 | assign[i] = hwc->idx; |
| 941 | } | 965 | } |
| @@ -958,6 +982,15 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) · 16 unmodified lines | |||
| 958 | READ_ONCE(cpuc->excl_cntrs->exclusive_present)) | 982 | READ_ONCE(cpuc->excl_cntrs->exclusive_present)) |
| 959 | gpmax /= 2; | 983 | gpmax /= 2; |
| 960 | 984 | ||
| 985 | /* | ||
| 986 | * Reduce the amount of available counters to allow fitting | ||
| 987 | * the extra Merge events needed by large increment events. | ||
| 988 | */ | ||
| 989 | if (x86_pmu.flags & PMU_FL_PAIR) { | ||
| 990 | gpmax = x86_pmu.num_counters - cpuc->n_pair; | ||
| 991 | WARN_ON(gpmax <= 0); | ||
| 992 | } | ||
| 993 | |||
| 961 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, | 994 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, |
| 962 | wmax, gpmax, assign); | 995 | wmax, gpmax, assign); |
| 963 | } | 996 | } |
| @@ -1038,6 +1071,8 @@ static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, · 74 unmodified lines | |||
| 1038 | return -EINVAL; | 1071 | return -EINVAL; |
| 1039 | cpuc->event_list[n] = leader; | 1072 | cpuc->event_list[n] = leader; |
| 1040 | n++; | 1073 | n++; |
| 1074 | if (is_counter_pair(&leader->hw)) | ||
| 1075 | cpuc->n_pair++; | ||
| 1041 | } | 1076 | } |
| 1042 | if (!dogrp) | 1077 | if (!dogrp) |
| 1043 | return n; | 1078 | return n; |
| @@ -1052,6 +1087,8 @@ static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, · 8 unmodified lines | |||
| 1052 | 1087 | ||
| 1053 | cpuc->event_list[n] = event; | 1088 | cpuc->event_list[n] = event; |
| 1054 | n++; | 1089 | n++; |
| 1090 | if (is_counter_pair(&event->hw)) | ||
| 1091 | cpuc->n_pair++; | ||
| 1055 | } | 1092 | } |
| 1056 | return n; | 1093 | return n; |
| 1057 | } | 1094 | } |
| @@ -1237,6 +1274,13 @@ int x86_perf_event_set_period(struct perf_event *event) · 179 unmodified lines | |||
| 1237 | 1274 | ||
| 1238 | wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask); | 1275 | wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask); |
| 1239 | 1276 | ||
| 1277 | /* | ||
| 1278 | * Clear the Merge event counter's upper 16 bits since | ||
| 1279 | * we currently declare a 48-bit counter width | ||
| 1280 | */ | ||
| 1281 | if (is_counter_pair(hwc)) | ||
| 1282 | wrmsrl(x86_pmu_event_addr(idx + 1), 0); | ||
| 1283 | |||
| 1240 | /* | 1284 | /* |
| 1241 | * Due to erratum on certan cpu we need | 1285 | * Due to erratum on certan cpu we need |
| 1242 | * a second write to be sure the register | 1286 | * a second write to be sure the register |
| @@ -741,6 +741,8 @@ static const struct x86_cpu_id rapl_model_match[] __initconst = { · 740 unmodified lines | |||
| 741 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_GOLDMONT_PLUS, model_hsw), | 741 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_GOLDMONT_PLUS, model_hsw), |
| 742 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ICELAKE_L, model_skl), | 742 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ICELAKE_L, model_skl), |
| 743 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ICELAKE, model_skl), | 743 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_ICELAKE, model_skl), |
| 744 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_COMETLAKE_L, model_skl), | ||
| 745 | X86_RAPL_MODEL_MATCH(INTEL_FAM6_COMETLAKE, model_skl), | ||
| 744 | {}, | 746 | {}, |
| 745 | }; | 747 | }; |
| 746 | 748 | ||
| @@ -77,6 +77,7 @@ static inline bool constraint_match(struct event_constraint *c, u64 ecode) · 76 unmodified lines | |||
| 77 | #define PERF_X86_EVENT_AUTO_RELOAD 0x0200 /* use PEBS auto-reload */ | 77 | #define PERF_X86_EVENT_AUTO_RELOAD 0x0200 /* use PEBS auto-reload */ |
| 78 | #define PERF_X86_EVENT_LARGE_PEBS 0x0400 /* use large PEBS */ | 78 | #define PERF_X86_EVENT_LARGE_PEBS 0x0400 /* use large PEBS */ |
| 79 | #define PERF_X86_EVENT_PEBS_VIA_PT 0x0800 /* use PT buffer for PEBS */ | 79 | #define PERF_X86_EVENT_PEBS_VIA_PT 0x0800 /* use PT buffer for PEBS */ |
| 80 | #define PERF_X86_EVENT_PAIR 0x1000 /* Large Increment per Cycle */ | ||
| 80 | 81 | ||
| 81 | struct amd_nb { | 82 | struct amd_nb { |
| 82 | int nb_id; /* NorthBridge id */ | 83 | int nb_id; /* NorthBridge id */ |
| @@ -272,6 +273,7 @@ struct cpu_hw_events { · 189 unmodified lines | |||
| 272 | struct amd_nb *amd_nb; | 273 | struct amd_nb *amd_nb; |
| 273 | /* Inverted mask of bits to clear in the perf_ctr ctrl registers */ | 274 | /* Inverted mask of bits to clear in the perf_ctr ctrl registers */ |
| 274 | u64 perf_ctr_virt_mask; | 275 | u64 perf_ctr_virt_mask; |
| 276 | int n_pair; /* Large increment events */ | ||
| 275 | 277 | ||
| 276 | void *kfree_on_online[X86_PERF_KFREE_MAX]; | 278 | void *kfree_on_online[X86_PERF_KFREE_MAX]; |
| 277 | }; | 279 | }; |
| @@ -694,6 +696,7 @@ struct x86_pmu { · 416 unmodified lines | |||
| 694 | * AMD bits | 696 | * AMD bits |
| 695 | */ | 697 | */ |
| 696 | unsigned int amd_nb_constraints : 1; | 698 | unsigned int amd_nb_constraints : 1; |
| 699 | u64 perf_ctr_pair_en; | ||
| 697 | 700 | ||
| 698 | /* | 701 | /* |
| 699 | * Extra registers for events | 702 | * Extra registers for events |
| @@ -743,6 +746,7 @@ do { \ · 43 unmodified lines | |||
| 743 | #define PMU_FL_EXCL_ENABLED 0x8 /* exclusive counter active */ | 746 | #define PMU_FL_EXCL_ENABLED 0x8 /* exclusive counter active */ |
| 744 | #define PMU_FL_PEBS_ALL 0x10 /* all events are valid PEBS events */ | 747 | #define PMU_FL_PEBS_ALL 0x10 /* all events are valid PEBS events */ |
| 745 | #define PMU_FL_TFA 0x20 /* deal with TSX force abort */ | 748 | #define PMU_FL_TFA 0x20 /* deal with TSX force abort */ |
| 749 | #define PMU_FL_PAIR 0x40 /* merge counters for large incr. events */ | ||
| 746 | 750 | ||
| 747 | #define EVENT_VAR(_id) event_attr_##_id | 751 | #define EVENT_VAR(_id) event_attr_##_id |
| 748 | #define EVENT_PTR(_id) &event_attr_##_id.attr.attr | 752 | #define EVENT_PTR(_id) &event_attr_##_id.attr.attr |
| @@ -838,6 +842,11 @@ int x86_pmu_hw_config(struct perf_event *event); · 89 unmodified lines | |||
| 838 | 842 | ||
| 839 | void x86_pmu_disable_all(void); | 843 | void x86_pmu_disable_all(void); |
| 840 | 844 | ||
| 845 | static inline bool is_counter_pair(struct hw_perf_event *hwc) | ||
| 846 | { | ||
| 847 | return hwc->flags & PERF_X86_EVENT_PAIR; | ||
| 848 | } | ||
| 849 | |||
| 841 | static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, | 850 | static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, |
| 842 | u64 enable_mask) | 851 | u64 enable_mask) |
| 843 | { | 852 | { |
| @@ -845,6 +854,14 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, · 1 unmodified line | |||
| 845 | 854 | ||
| 846 | if (hwc->extra_reg.reg) | 855 | if (hwc->extra_reg.reg) |
| 847 | wrmsrl(hwc->extra_reg.reg, hwc->extra_reg.config); | 856 | wrmsrl(hwc->extra_reg.reg, hwc->extra_reg.config); |
| 857 | |||
| 858 | /* | ||
| 859 | * Add enabled Merge event on next counter | ||
| 860 | * if large increment event being enabled on this counter | ||
| 861 | */ | ||
| 862 | if (is_counter_pair(hwc)) | ||
| 863 | wrmsrl(x86_pmu_config_addr(hwc->idx + 1), x86_pmu.perf_ctr_pair_en); | ||
| 864 | |||
| 848 | wrmsrl(hwc->config_base, (hwc->config | enable_mask) & ~disable_mask); | 865 | wrmsrl(hwc->config_base, (hwc->config | enable_mask) & ~disable_mask); |
| 849 | } | 866 | } |
| 850 | 867 | ||
| @@ -861,6 +878,9 @@ static inline void x86_pmu_disable_event(struct perf_event *event) · 10 unmodified lines | |||
| 861 | struct hw_perf_event *hwc = &event->hw; | 878 | struct hw_perf_event *hwc = &event->hw; |
| 862 | 879 | ||
| 863 | wrmsrl(hwc->config_base, hwc->config); | 880 | wrmsrl(hwc->config_base, hwc->config); |
| 881 | |||
| 882 | if (is_counter_pair(hwc)) | ||
| 883 | wrmsrl(x86_pmu_config_addr(hwc->idx + 1), 0); | ||
| 864 | } | 884 | } |
| 865 | 885 | ||
| 866 | void x86_pmu_enable_event(struct perf_event *event); | 886 | void x86_pmu_enable_event(struct perf_event *event); |
| @@ -47,8 +47,6 @@ struct dyn_arch_ftrace { · 46 unmodified lines | |||
| 47 | /* No extra data needed for x86 */ | 47 | /* No extra data needed for x86 */ |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | int ftrace_int3_handler(struct pt_regs *regs); | ||
| 51 | |||
| 52 | #define FTRACE_GRAPH_TRAMP_ADDR FTRACE_GRAPH_ADDR | 50 | #define FTRACE_GRAPH_TRAMP_ADDR FTRACE_GRAPH_ADDR |
| 53 | 51 | ||
| 54 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 52 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| @@ -11,12 +11,11 @@ · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | #include <asm-generic/kprobes.h> | 12 | #include <asm-generic/kprobes.h> |
| 13 | 13 | ||
| 14 | #define BREAKPOINT_INSTRUCTION 0xcc | ||
| 15 | |||
| 16 | #ifdef CONFIG_KPROBES | 14 | #ifdef CONFIG_KPROBES |
| 17 | #include <linux/types.h> | 15 | #include <linux/types.h> |
| 18 | #include <linux/ptrace.h> | 16 | #include <linux/ptrace.h> |
| 19 | #include <linux/percpu.h> | 17 | #include <linux/percpu.h> |
| 18 | #include <asm/text-patching.h> | ||
| 20 | #include <asm/insn.h> | 19 | #include <asm/insn.h> |
| 21 | 20 | ||
| 22 | #define __ARCH_WANT_KPROBES_INSN_SLOT | 21 | #define __ARCH_WANT_KPROBES_INSN_SLOT |
| @@ -25,10 +24,7 @@ struct pt_regs; · 2 unmodified lines | |||
| 25 | struct kprobe; | 24 | struct kprobe; |
| 26 | 25 | ||
| 27 | typedef u8 kprobe_opcode_t; | 26 | typedef u8 kprobe_opcode_t; |
| 28 | #define RELATIVEJUMP_OPCODE 0xe9 | 27 | |
| 29 | #define RELATIVEJUMP_SIZE 5 | ||
| 30 | #define RELATIVECALL_OPCODE 0xe8 | ||
| 31 | #define RELATIVE_ADDR_SIZE 4 | ||
| 32 | #define MAX_STACK_SIZE 64 | 28 | #define MAX_STACK_SIZE 64 |
| 33 | #define CUR_STACK_SIZE(ADDR) \ | 29 | #define CUR_STACK_SIZE(ADDR) \ |
| 34 | (current_top_of_stack() - (unsigned long)(ADDR)) | 30 | (current_top_of_stack() - (unsigned long)(ADDR)) |
| @@ -43,11 +39,11 @@ extern __visible kprobe_opcode_t optprobe_template_entry[]; · 8 unmodified lines | |||
| 43 | extern __visible kprobe_opcode_t optprobe_template_val[]; | 39 | extern __visible kprobe_opcode_t optprobe_template_val[]; |
| 44 | extern __visible kprobe_opcode_t optprobe_template_call[]; | 40 | extern __visible kprobe_opcode_t optprobe_template_call[]; |
| 45 | extern __visible kprobe_opcode_t optprobe_template_end[]; | 41 | extern __visible kprobe_opcode_t optprobe_template_end[]; |
| 46 | #define MAX_OPTIMIZED_LENGTH (MAX_INSN_SIZE + RELATIVE_ADDR_SIZE) | 42 | #define MAX_OPTIMIZED_LENGTH (MAX_INSN_SIZE + DISP32_SIZE) |
| 47 | #define MAX_OPTINSN_SIZE \ | 43 | #define MAX_OPTINSN_SIZE \ |
| 48 | (((unsigned long)optprobe_template_end - \ | 44 | (((unsigned long)optprobe_template_end - \ |
| 49 | (unsigned long)optprobe_template_entry) + \ | 45 | (unsigned long)optprobe_template_entry) + \ |
| 50 | MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE) | 46 | MAX_OPTIMIZED_LENGTH + JMP32_INSN_SIZE) |
| 51 | 47 | ||
| 52 | extern const int kretprobe_blacklist_size; | 48 | extern const int kretprobe_blacklist_size; |
| 53 | 49 | ||
| @@ -73,7 +69,7 @@ struct arch_specific_insn { · 19 unmodified lines | |||
| 73 | 69 | ||
| 74 | struct arch_optimized_insn { | 70 | struct arch_optimized_insn { |
| 75 | /* copy of the original instructions */ | 71 | /* copy of the original instructions */ |
| 76 | kprobe_opcode_t copied_insn[RELATIVE_ADDR_SIZE]; | 72 | kprobe_opcode_t copied_insn[DISP32_SIZE]; |
| 77 | /* detour code buffer */ | 73 | /* detour code buffer */ |
| 78 | kprobe_opcode_t *insn; | 74 | kprobe_opcode_t *insn; |
| 79 | /* the size of instructions copied to detour code buffer */ | 75 | /* the size of instructions copied to detour code buffer */ |
| @@ -81,8 +81,6 @@ int set_direct_map_invalid_noflush(struct page *page); · 80 unmodified lines | |||
| 81 | int set_direct_map_default_noflush(struct page *page); | 81 | int set_direct_map_default_noflush(struct page *page); |
| 82 | 82 | ||
| 83 | extern int kernel_set_to_readonly; | 83 | extern int kernel_set_to_readonly; |
| 84 | void set_kernel_text_rw(void); | ||
| 85 | void set_kernel_text_ro(void); | ||
| 86 | 84 | ||
| 87 | #ifdef CONFIG_X86_64 | 85 | #ifdef CONFIG_X86_64 |
| 88 | static inline int set_mce_nospec(unsigned long pfn) | 86 | static inline int set_mce_nospec(unsigned long pfn) |
| @@ -25,14 +25,6 @@ static inline void apply_paravirt(struct paravirt_patch_site *start, · 24 unmodified lines | |||
| 25 | */ | 25 | */ |
| 26 | #define POKE_MAX_OPCODE_SIZE 5 | 26 | #define POKE_MAX_OPCODE_SIZE 5 |
| 27 | 27 | ||
| 28 | struct text_poke_loc { | ||
| 29 | void *addr; | ||
| 30 | int len; | ||
| 31 | s32 rel32; | ||
| 32 | u8 opcode; | ||
| 33 | const u8 text[POKE_MAX_OPCODE_SIZE]; | ||
| 34 | }; | ||
| 35 | |||
| 36 | extern void text_poke_early(void *addr, const void *opcode, size_t len); | 28 | extern void text_poke_early(void *addr, const void *opcode, size_t len); |
| 37 | 29 | ||
| 38 | /* | 30 | /* |
| @@ -50,21 +42,13 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len); · 11 unmodified lines | |||
| 50 | * an inconsistent instruction while you patch. | 42 | * an inconsistent instruction while you patch. |
| 51 | */ | 43 | */ |
| 52 | extern void *text_poke(void *addr, const void *opcode, size_t len); | 44 | extern void *text_poke(void *addr, const void *opcode, size_t len); |
| 45 | extern void text_poke_sync(void); | ||
| 53 | extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len); | 46 | extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len); |
| 54 | extern int poke_int3_handler(struct pt_regs *regs); | 47 | extern int poke_int3_handler(struct pt_regs *regs); |
| 55 | extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate); | 48 | extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate); |
| 56 | extern void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries); | ||
| 57 | extern void text_poke_loc_init(struct text_poke_loc *tp, void *addr, | ||
| 58 | const void *opcode, size_t len, const void *emulate); | ||
| 59 | extern int after_bootmem; | ||
| 60 | extern __ro_after_init struct mm_struct *poking_mm; | ||
| 61 | extern __ro_after_init unsigned long poking_addr; | ||
| 62 | 49 | ||
| 63 | #ifndef CONFIG_UML_X86 | 50 | extern void text_poke_queue(void *addr, const void *opcode, size_t len, const void *emulate); |
| 64 | static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip) | 51 | extern void text_poke_finish(void); |
| 65 | { | ||
| 66 | regs->ip = ip; | ||
| 67 | } | ||
| 68 | 52 | ||
| 69 | #define INT3_INSN_SIZE 1 | 53 | #define INT3_INSN_SIZE 1 |
| 70 | #define INT3_INSN_OPCODE 0xCC | 54 | #define INT3_INSN_OPCODE 0xCC |
| @@ -78,6 +62,67 @@ static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip) · 7 unmodified lines | |||
| 78 | #define JMP8_INSN_SIZE 2 | 62 | #define JMP8_INSN_SIZE 2 |
| 79 | #define JMP8_INSN_OPCODE 0xEB | 63 | #define JMP8_INSN_OPCODE 0xEB |
| 80 | 64 | ||
| 65 | #define DISP32_SIZE 4 | ||
| 66 | |||
| 67 | static inline int text_opcode_size(u8 opcode) | ||
| 68 | { | ||
| 69 | int size = 0; | ||
| 70 | |||
| 71 | #define __CASE(insn) \ | ||
| 72 | case insn##_INSN_OPCODE: size = insn##_INSN_SIZE; break | ||
| 73 | |||
| 74 | switch(opcode) { | ||
| 75 | __CASE(INT3); | ||
| 76 | __CASE(CALL); | ||
| 77 | __CASE(JMP32); | ||
| 78 | __CASE(JMP8); | ||
| 79 | } | ||
| 80 | |||
| 81 | #undef __CASE | ||
| 82 | |||
| 83 | return size; | ||
| 84 | } | ||
| 85 | |||
| 86 | union text_poke_insn { | ||
| 87 | u8 text[POKE_MAX_OPCODE_SIZE]; | ||
| 88 | struct { | ||
| 89 | u8 opcode; | ||
| 90 | s32 disp; | ||
| 91 | } __attribute__((packed)); | ||
| 92 | }; | ||
| 93 | |||
| 94 | static __always_inline | ||
| 95 | void *text_gen_insn(u8 opcode, const void *addr, const void *dest) | ||
| 96 | { | ||
| 97 | static union text_poke_insn insn; /* per instance */ | ||
| 98 | int size = text_opcode_size(opcode); | ||
| 99 | |||
| 100 | insn.opcode = opcode; | ||
| 101 | |||
| 102 | if (size > 1) { | ||
| 103 | insn.disp = (long)dest - (long)(addr + size); | ||
| 104 | if (size == 2) { | ||
| 105 | /* | ||
| 106 | * Ensure that for JMP9 the displacement | ||
| 107 | * actually fits the signed byte. | ||
| 108 | */ | ||
| 109 | BUG_ON((insn.disp >> 31) != (insn.disp >> 7)); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | return &insn.text; | ||
| 114 | } | ||
| 115 | |||
| 116 | extern int after_bootmem; | ||
| 117 | extern __ro_after_init struct mm_struct *poking_mm; | ||
| 118 | extern __ro_after_init unsigned long poking_addr; | ||
| 119 | |||
| 120 | #ifndef CONFIG_UML_X86 | ||
| 121 | static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip) | ||
| 122 | { | ||
| 123 | regs->ip = ip; | ||
| 124 | } | ||
| 125 | |||
| 81 | static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val) | 126 | static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val) |
| 82 | { | 127 | { |
| 83 | /* | 128 | /* |
| @@ -85,6 +130,9 @@ static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val) · 1 unmodified line | |||
| 85 | * stack where the break point happened, and the saving of | 130 | * stack where the break point happened, and the saving of |
| 86 | * pt_regs. We can extend the original stack because of | 131 | * pt_regs. We can extend the original stack because of |
| 87 | * this gap. See the idtentry macro's create_gap option. | 132 | * this gap. See the idtentry macro's create_gap option. |
| 133 | * | ||
| 134 | * Similarly entry_32.S will have a gap on the stack for (any) hardware | ||
| 135 | * exception and pt_regs; see FIXUP_FRAME. | ||
| 88 | */ | 136 | */ |
| 89 | regs->sp -= sizeof(unsigned long); | 137 | regs->sp -= sizeof(unsigned long); |
| 90 | *(unsigned long *)regs->sp = val; | 138 | *(unsigned long *)regs->sp = val; |
| @@ -936,44 +936,81 @@ static void do_sync_core(void *info) · 935 unmodified lines | |||
| 936 | sync_core(); | 936 | sync_core(); |
| 937 | } | 937 | } |
| 938 | 938 | ||
| 939 | static struct bp_patching_desc { | 939 | void text_poke_sync(void) |
| 940 | { | ||
| 941 | on_each_cpu(do_sync_core, NULL, 1); | ||
| 942 | } | ||
| 943 | |||
| 944 | struct text_poke_loc { | ||
| 945 | s32 rel_addr; /* addr := _stext + rel_addr */ | ||
| 946 | s32 rel32; | ||
| 947 | u8 opcode; | ||
| 948 | const u8 text[POKE_MAX_OPCODE_SIZE]; | ||
| 949 | }; | ||
| 950 | |||
| 951 | struct bp_patching_desc { | ||
| 940 | struct text_poke_loc *vec; | 952 | struct text_poke_loc *vec; |
| 941 | int nr_entries; | 953 | int nr_entries; |
| 942 | } bp_patching; | 954 | atomic_t refs; |
| 955 | }; | ||
| 956 | |||
| 957 | static struct bp_patching_desc *bp_desc; | ||
| 958 | |||
| 959 | static inline struct bp_patching_desc *try_get_desc(struct bp_patching_desc **descp) | ||
| 960 | { | ||
| 961 | struct bp_patching_desc *desc = READ_ONCE(*descp); /* rcu_dereference */ | ||
| 962 | |||
| 963 | if (!desc || !atomic_inc_not_zero(&desc->refs)) | ||
| 964 | return NULL; | ||
| 965 | |||
| 966 | return desc; | ||
| 967 | } | ||
| 968 | |||
| 969 | static inline void put_desc(struct bp_patching_desc *desc) | ||
| 970 | { | ||
| 971 | smp_mb__before_atomic(); | ||
| 972 | atomic_dec(&desc->refs); | ||
| 973 | } | ||
| 943 | 974 | ||
| 944 | static int patch_cmp(const void *key, const void *elt) | 975 | static inline void *text_poke_addr(struct text_poke_loc *tp) |
| 976 | { | ||
| 977 | return _stext + tp->rel_addr; | ||
| 978 | } | ||
| 979 | |||
| 980 | static int notrace patch_cmp(const void *key, const void *elt) | ||
| 945 | { | 981 | { |
| 946 | struct text_poke_loc *tp = (struct text_poke_loc *) elt; | 982 | struct text_poke_loc *tp = (struct text_poke_loc *) elt; |
| 947 | 983 | ||
| 948 | if (key < tp->addr) | 984 | if (key < text_poke_addr(tp)) |
| 949 | return -1; | 985 | return -1; |
| 950 | if (key > tp->addr) | 986 | if (key > text_poke_addr(tp)) |
| 951 | return 1; | 987 | return 1; |
| 952 | return 0; | 988 | return 0; |
| 953 | } | 989 | } |
| 954 | NOKPROBE_SYMBOL(patch_cmp); | 990 | NOKPROBE_SYMBOL(patch_cmp); |
| 955 | 991 | ||
| 956 | int poke_int3_handler(struct pt_regs *regs) | 992 | int notrace poke_int3_handler(struct pt_regs *regs) |
| 957 | { | 993 | { |
| 994 | struct bp_patching_desc *desc; | ||
| 958 | struct text_poke_loc *tp; | 995 | struct text_poke_loc *tp; |
| 996 | int len, ret = 0; | ||
| 959 | void *ip; | 997 | void *ip; |
| 960 | 998 | ||
| 999 | if (user_mode(regs)) | ||
| 1000 | return 0; | ||
| 1001 | |||
| 961 | /* | 1002 | /* |
| 962 | * Having observed our INT3 instruction, we now must observe | 1003 | * Having observed our INT3 instruction, we now must observe |
| 963 | * bp_patching.nr_entries. | 1004 | * bp_desc: |
| 964 | * | 1005 | * |
| 965 | * nr_entries != 0 INT3 | 1006 | * bp_desc = desc INT3 |
| 966 | * WMB RMB | 1007 | * WMB RMB |
| 967 | * write INT3 if (nr_entries) | 1008 | * write INT3 if (desc) |
| 968 | * | ||
| 969 | * Idem for other elements in bp_patching. | ||
| 970 | */ | 1009 | */ |
| 971 | smp_rmb(); | 1010 | smp_rmb(); |
| 972 | 1011 | ||
| 973 | if (likely(!bp_patching.nr_entries)) | 1012 | desc = try_get_desc(&bp_desc); |
| 974 | return 0; | 1013 | if (!desc) |
| 975 | |||
| 976 | if (user_mode(regs)) | ||
| 977 | return 0; | 1014 | return 0; |
| 978 | 1015 | ||
| 979 | /* | 1016 | /* |
| @@ -984,19 +1021,20 @@ int poke_int3_handler(struct pt_regs *regs) · 4 unmodified lines | |||
| 984 | /* | 1021 | /* |
| 985 | * Skip the binary search if there is a single member in the vector. | 1022 | * Skip the binary search if there is a single member in the vector. |
| 986 | */ | 1023 | */ |
| 987 | if (unlikely(bp_patching.nr_entries > 1)) { | 1024 | if (unlikely(desc->nr_entries > 1)) { |
| 988 | tp = bsearch(ip, bp_patching.vec, bp_patching.nr_entries, | 1025 | tp = bsearch(ip, desc->vec, desc->nr_entries, |
| 989 | sizeof(struct text_poke_loc), | 1026 | sizeof(struct text_poke_loc), |
| 990 | patch_cmp); | 1027 | patch_cmp); |
| 991 | if (!tp) | 1028 | if (!tp) |
| 992 | return 0; | 1029 | goto out_put; |
| 993 | } else { | 1030 | } else { |
| 994 | tp = bp_patching.vec; | 1031 | tp = desc->vec; |
| 995 | if (tp->addr != ip) | 1032 | if (text_poke_addr(tp) != ip) |
| 996 | return 0; | 1033 | goto out_put; |
| 997 | } | 1034 | } |
| 998 | 1035 | ||
| 999 | ip += tp->len; | 1036 | len = text_opcode_size(tp->opcode); |
| 1037 | ip += len; | ||
| 1000 | 1038 | ||
| 1001 | switch (tp->opcode) { | 1039 | switch (tp->opcode) { |
| 1002 | case INT3_INSN_OPCODE: | 1040 | case INT3_INSN_OPCODE: |
| @@ -1004,7 +1042,7 @@ int poke_int3_handler(struct pt_regs *regs) · 1 unmodified line | |||
| 1004 | * Someone poked an explicit INT3, they'll want to handle it, | 1042 | * Someone poked an explicit INT3, they'll want to handle it, |
| 1005 | * do not consume. | 1043 | * do not consume. |
| 1006 | */ | 1044 | */ |
| 1007 | return 0; | 1045 | goto out_put; |
| 1008 | 1046 | ||
| 1009 | case CALL_INSN_OPCODE: | 1047 | case CALL_INSN_OPCODE: |
| 1010 | int3_emulate_call(regs, (long)ip + tp->rel32); | 1048 | int3_emulate_call(regs, (long)ip + tp->rel32); |
| @@ -1019,10 +1057,18 @@ int poke_int3_handler(struct pt_regs *regs) · 8 unmodified lines | |||
| 1019 | BUG(); | 1057 | BUG(); |
| 1020 | } | 1058 | } |
| 1021 | 1059 | ||
| 1022 | return 1; | 1060 | ret = 1; |
| 1061 | |||
| 1062 | out_put: | ||
| 1063 | put_desc(desc); | ||
| 1064 | return ret; | ||
| 1023 | } | 1065 | } |
| 1024 | NOKPROBE_SYMBOL(poke_int3_handler); | 1066 | NOKPROBE_SYMBOL(poke_int3_handler); |
| 1025 | 1067 | ||
| 1068 | #define TP_VEC_MAX (PAGE_SIZE / sizeof(struct text_poke_loc)) | ||
| 1069 | static struct text_poke_loc tp_vec[TP_VEC_MAX]; | ||
| 1070 | static int tp_vec_nr; | ||
| 1071 | |||
| 1026 | /** | 1072 | /** |
| 1027 | * text_poke_bp_batch() -- update instructions on live kernel on SMP | 1073 | * text_poke_bp_batch() -- update instructions on live kernel on SMP |
| 1028 | * @tp: vector of instructions to patch | 1074 | * @tp: vector of instructions to patch |
| @@ -1044,16 +1090,20 @@ NOKPROBE_SYMBOL(poke_int3_handler); · 15 unmodified lines | |||
| 1044 | * replacing opcode | 1090 | * replacing opcode |
| 1045 | * - sync cores | 1091 | * - sync cores |
| 1046 | */ | 1092 | */ |
| 1047 | void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries) | 1093 | static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries) |
| 1048 | { | 1094 | { |
| 1095 | struct bp_patching_desc desc = { | ||
| 1096 | .vec = tp, | ||
| 1097 | .nr_entries = nr_entries, | ||
| 1098 | .refs = ATOMIC_INIT(1), | ||
| 1099 | }; | ||
| 1049 | unsigned char int3 = INT3_INSN_OPCODE; | 1100 | unsigned char int3 = INT3_INSN_OPCODE; |
| 1050 | unsigned int i; | 1101 | unsigned int i; |
| 1051 | int do_sync; | 1102 | int do_sync; |
| 1052 | 1103 | ||
| 1053 | lockdep_assert_held(&text_mutex); | 1104 | lockdep_assert_held(&text_mutex); |
| 1054 | 1105 | ||
| 1055 | bp_patching.vec = tp; | 1106 | smp_store_release(&bp_desc, &desc); /* rcu_assign_pointer */ |
| 1056 | bp_patching.nr_entries = nr_entries; | ||
| 1057 | 1107 | ||
| 1058 | /* | 1108 | /* |
| 1059 | * Corresponding read barrier in int3 notifier for making sure the | 1109 | * Corresponding read barrier in int3 notifier for making sure the |
| @@ -1065,18 +1115,20 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries) · 5 unmodified lines | |||
| 1065 | * First step: add a int3 trap to the address that will be patched. | 1115 | * First step: add a int3 trap to the address that will be patched. |
| 1066 | */ | 1116 | */ |
| 1067 | for (i = 0; i < nr_entries; i++) | 1117 | for (i = 0; i < nr_entries; i++) |
| 1068 | text_poke(tp[i].addr, &int3, sizeof(int3)); | 1118 | text_poke(text_poke_addr(&tp[i]), &int3, INT3_INSN_SIZE); |
| 1069 | 1119 | ||
| 1070 | on_each_cpu(do_sync_core, NULL, 1); | 1120 | text_poke_sync(); |
| 1071 | 1121 | ||
| 1072 | /* | 1122 | /* |
| 1073 | * Second step: update all but the first byte of the patched range. | 1123 | * Second step: update all but the first byte of the patched range. |
| 1074 | */ | 1124 | */ |
| 1075 | for (do_sync = 0, i = 0; i < nr_entries; i++) { | 1125 | for (do_sync = 0, i = 0; i < nr_entries; i++) { |
| 1076 | if (tp[i].len - sizeof(int3) > 0) { | 1126 | int len = text_opcode_size(tp[i].opcode); |
| 1077 | text_poke((char *)tp[i].addr + sizeof(int3), | 1127 | |
| 1078 | (const char *)tp[i].text + sizeof(int3), | 1128 | if (len - INT3_INSN_SIZE > 0) { |
| 1079 | tp[i].len - sizeof(int3)); | 1129 | text_poke(text_poke_addr(&tp[i]) + INT3_INSN_SIZE, |
| 1130 | (const char *)tp[i].text + INT3_INSN_SIZE, | ||
| 1131 | len - INT3_INSN_SIZE); | ||
| 1080 | do_sync++; | 1132 | do_sync++; |
| 1081 | } | 1133 | } |
| 1082 | } | 1134 | } |
| @@ -1087,7 +1139,7 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries) · 4 unmodified lines | |||
| 1087 | * not necessary and we'd be safe even without it. But | 1139 | * not necessary and we'd be safe even without it. But |
| 1088 | * better safe than sorry (plus there's not only Intel). | 1140 | * better safe than sorry (plus there's not only Intel). |
| 1089 | */ | 1141 | */ |
| 1090 | on_each_cpu(do_sync_core, NULL, 1); | 1142 | text_poke_sync(); |
| 1091 | } | 1143 | } |
| 1092 | 1144 | ||
| 1093 | /* | 1145 | /* |
| @@ -1098,19 +1150,20 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries) · 4 unmodified lines | |||
| 1098 | if (tp[i].text[0] == INT3_INSN_OPCODE) | 1150 | if (tp[i].text[0] == INT3_INSN_OPCODE) |
| 1099 | continue; | 1151 | continue; |
| 1100 | 1152 | ||
| 1101 | text_poke(tp[i].addr, tp[i].text, sizeof(int3)); | 1153 | text_poke(text_poke_addr(&tp[i]), tp[i].text, INT3_INSN_SIZE); |
| 1102 | do_sync++; | 1154 | do_sync++; |
| 1103 | } | 1155 | } |
| 1104 | 1156 | ||
| 1105 | if (do_sync) | 1157 | if (do_sync) |
| 1106 | on_each_cpu(do_sync_core, NULL, 1); | 1158 | text_poke_sync(); |
| 1107 | 1159 | ||
| 1108 | /* | 1160 | /* |
| 1109 | * sync_core() implies an smp_mb() and orders this store against | 1161 | * Remove and synchronize_rcu(), except we have a very primitive |
| 1110 | * the writing of the new instruction. | 1162 | * refcount based completion. |
| 1111 | */ | 1163 | */ |
| 1112 | bp_patching.vec = NULL; | 1164 | WRITE_ONCE(bp_desc, NULL); /* RCU_INIT_POINTER */ |
| 1113 | bp_patching.nr_entries = 0; | 1165 | if (!atomic_dec_and_test(&desc.refs)) |
| 1166 | atomic_cond_read_acquire(&desc.refs, !VAL); | ||
| 1114 | } | 1167 | } |
| 1115 | 1168 | ||
| 1116 | void text_poke_loc_init(struct text_poke_loc *tp, void *addr, | 1169 | void text_poke_loc_init(struct text_poke_loc *tp, void *addr, |
| @@ -1118,11 +1171,7 @@ void text_poke_loc_init(struct text_poke_loc *tp, void *addr, · 1 unmodified line | |||
| 1118 | { | 1171 | { |
| 1119 | struct insn insn; | 1172 | struct insn insn; |
| 1120 | 1173 | ||
| 1121 | if (!opcode) | 1174 | memcpy((void *)tp->text, opcode, len); |
| 1122 | opcode = (void *)tp->text; | ||
| 1123 | else | ||
| 1124 | memcpy((void *)tp->text, opcode, len); | ||
| 1125 | |||
| 1126 | if (!emulate) | 1175 | if (!emulate) |
| 1127 | emulate = opcode; | 1176 | emulate = opcode; |
| 1128 | 1177 | ||
| @@ -1132,8 +1181,7 @@ void text_poke_loc_init(struct text_poke_loc *tp, void *addr, · 3 unmodified lines | |||
| 1132 | BUG_ON(!insn_complete(&insn)); | 1181 | BUG_ON(!insn_complete(&insn)); |
| 1133 | BUG_ON(len != insn.length); | 1182 | BUG_ON(len != insn.length); |
| 1134 | 1183 | ||
| 1135 | tp->addr = addr; | 1184 | tp->rel_addr = addr - (void *)_stext; |
| 1136 | tp->len = len; | ||
| 1137 | tp->opcode = insn.opcode.bytes[0]; | 1185 | tp->opcode = insn.opcode.bytes[0]; |
| 1138 | 1186 | ||
| 1139 | switch (tp->opcode) { | 1187 | switch (tp->opcode) { |
| @@ -1167,6 +1215,55 @@ void text_poke_loc_init(struct text_poke_loc *tp, void *addr, · 27 unmodified lines | |||
| 1167 | } | 1215 | } |
| 1168 | } | 1216 | } |
| 1169 | 1217 | ||
| 1218 | /* | ||
| 1219 | * We hard rely on the tp_vec being ordered; ensure this is so by flushing | ||
| 1220 | * early if needed. | ||
| 1221 | */ | ||
| 1222 | static bool tp_order_fail(void *addr) | ||
| 1223 | { | ||
| 1224 | struct text_poke_loc *tp; | ||
| 1225 | |||
| 1226 | if (!tp_vec_nr) | ||
| 1227 | return false; | ||
| 1228 | |||
| 1229 | if (!addr) /* force */ | ||
| 1230 | return true; | ||
| 1231 | |||
| 1232 | tp = &tp_vec[tp_vec_nr - 1]; | ||
| 1233 | if ((unsigned long)text_poke_addr(tp) > (unsigned long)addr) | ||
| 1234 | return true; | ||
| 1235 | |||
| 1236 | return false; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | static void text_poke_flush(void *addr) | ||
| 1240 | { | ||
| 1241 | if (tp_vec_nr == TP_VEC_MAX || tp_order_fail(addr)) { | ||
| 1242 | text_poke_bp_batch(tp_vec, tp_vec_nr); | ||
| 1243 | tp_vec_nr = 0; | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | void text_poke_finish(void) | ||
| 1248 | { | ||
| 1249 | text_poke_flush(NULL); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | void __ref text_poke_queue(void *addr, const void *opcode, size_t len, const void *emulate) | ||
| 1253 | { | ||
| 1254 | struct text_poke_loc *tp; | ||
| 1255 | |||
| 1256 | if (unlikely(system_state == SYSTEM_BOOTING)) { | ||
| 1257 | text_poke_early(addr, opcode, len); | ||
| 1258 | return; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | text_poke_flush(addr); | ||
| 1262 | |||
| 1263 | tp = &tp_vec[tp_vec_nr++]; | ||
| 1264 | text_poke_loc_init(tp, addr, opcode, len, emulate); | ||
| 1265 | } | ||
| 1266 | |||
| 1170 | /** | 1267 | /** |
| 1171 | * text_poke_bp() -- update instructions on live kernel on SMP | 1268 | * text_poke_bp() -- update instructions on live kernel on SMP |
| 1172 | * @addr: address to patch | 1269 | * @addr: address to patch |
| @@ -1178,10 +1275,15 @@ void text_poke_loc_init(struct text_poke_loc *tp, void *addr, · 5 unmodified lines | |||
| 1178 | * dynamically allocated memory. This function should be used when it is | 1275 | * dynamically allocated memory. This function should be used when it is |
| 1179 | * not possible to allocate memory. | 1276 | * not possible to allocate memory. |
| 1180 | */ | 1277 | */ |
| 1181 | void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate) | 1278 | void __ref text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate) |
| 1182 | { | 1279 | { |
| 1183 | struct text_poke_loc tp; | 1280 | struct text_poke_loc tp; |
| 1184 | 1281 | ||
| 1282 | if (unlikely(system_state == SYSTEM_BOOTING)) { | ||
| 1283 | text_poke_early(addr, opcode, len); | ||
| 1284 | return; | ||
| 1285 | } | ||
| 1286 | |||
| 1185 | text_poke_loc_init(&tp, addr, opcode, len, emulate); | 1287 | text_poke_loc_init(&tp, addr, opcode, len, emulate); |
| 1186 | text_poke_bp_batch(&tp, 1); | 1288 | text_poke_bp_batch(&tp, 1); |
| 1187 | } | 1289 | } |
| @@ -35,6 +35,8 @@ · 34 unmodified lines | |||
| 35 | 35 | ||
| 36 | #ifdef CONFIG_DYNAMIC_FTRACE | 36 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 37 | 37 | ||
| 38 | static int ftrace_poke_late = 0; | ||
| 39 | |||
| 38 | int ftrace_arch_code_modify_prepare(void) | 40 | int ftrace_arch_code_modify_prepare(void) |
| 39 | __acquires(&text_mutex) | 41 | __acquires(&text_mutex) |
| 40 | { | 42 | { |
| @@ -44,84 +46,37 @@ int ftrace_arch_code_modify_prepare(void) · 3 unmodified lines | |||
| 44 | * ftrace has it set to "read/write". | 46 | * ftrace has it set to "read/write". |
| 45 | */ | 47 | */ |
| 46 | mutex_lock(&text_mutex); | 48 | mutex_lock(&text_mutex); |
| 47 | set_kernel_text_rw(); | 49 | ftrace_poke_late = 1; |
| 48 | set_all_modules_text_rw(); | ||
| 49 | return 0; | 50 | return 0; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | int ftrace_arch_code_modify_post_process(void) | 53 | int ftrace_arch_code_modify_post_process(void) |
| 53 | __releases(&text_mutex) | 54 | __releases(&text_mutex) |
| 54 | { | 55 | { |
| 55 | set_all_modules_text_ro(); | 56 | /* |
| 56 | set_kernel_text_ro(); | 57 | * ftrace_make_{call,nop}() may be called during |
| 58 | * module load, and we need to finish the text_poke_queue() | ||
| 59 | * that they do, here. | ||
| 60 | */ | ||
| 61 | text_poke_finish(); | ||
| 62 | ftrace_poke_late = 0; | ||
| 57 | mutex_unlock(&text_mutex); | 63 | mutex_unlock(&text_mutex); |
| 58 | return 0; | 64 | return 0; |
| 59 | } | 65 | } |
| 60 | 66 | ||
| 61 | union ftrace_code_union { | 67 | static const char *ftrace_nop_replace(void) |
| 62 | char code[MCOUNT_INSN_SIZE]; | ||
| 63 | struct { | ||
| 64 | unsigned char op; | ||
| 65 | int offset; | ||
| 66 | } __attribute__((packed)); | ||
| 67 | }; | ||
| 68 | |||
| 69 | static int ftrace_calc_offset(long ip, long addr) | ||
| 70 | { | ||
| 71 | return (int)(addr - ip); | ||
| 72 | } | ||
| 73 | |||
| 74 | static unsigned char * | ||
| 75 | ftrace_text_replace(unsigned char op, unsigned long ip, unsigned long addr) | ||
| 76 | { | 68 | { |
| 77 | static union ftrace_code_union calc; | 69 | return ideal_nops[NOP_ATOMIC5]; |
| 78 | |||
| 79 | calc.op = op; | ||
| 80 | calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr); | ||
| 81 | |||
| 82 | return calc.code; | ||
| 83 | } | ||
| 84 | |||
| 85 | static unsigned char * | ||
| 86 | ftrace_call_replace(unsigned long ip, unsigned long addr) | ||
| 87 | { | ||
| 88 | return ftrace_text_replace(0xe8, ip, addr); | ||
| 89 | } | ||
| 90 | |||
| 91 | static inline int | ||
| 92 | within(unsigned long addr, unsigned long start, unsigned long end) | ||
| 93 | { | ||
| 94 | return addr >= start && addr < end; | ||
| 95 | } | 70 | } |
| 96 | 71 | ||
| 97 | static unsigned long text_ip_addr(unsigned long ip) | 72 | static const char *ftrace_call_replace(unsigned long ip, unsigned long addr) |
| 98 | { | 73 | { |
| 99 | /* | 74 | return text_gen_insn(CALL_INSN_OPCODE, (void *)ip, (void *)addr); |
| 100 | * On x86_64, kernel text mappings are mapped read-only, so we use | ||
| 101 | * the kernel identity mapping instead of the kernel text mapping | ||
| 102 | * to modify the kernel text. | ||
| 103 | * | ||
| 104 | * For 32bit kernels, these mappings are same and we can use | ||
| 105 | * kernel identity mapping to modify code. | ||
| 106 | */ | ||
| 107 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | ||
| 108 | ip = (unsigned long)__va(__pa_symbol(ip)); | ||
| 109 | |||
| 110 | return ip; | ||
| 111 | } | 75 | } |
| 112 | 76 | ||
| 113 | static const unsigned char *ftrace_nop_replace(void) | 77 | static int ftrace_verify_code(unsigned long ip, const char *old_code) |
| 114 | { | 78 | { |
| 115 | return ideal_nops[NOP_ATOMIC5]; | 79 | char cur_code[MCOUNT_INSN_SIZE]; |
| 116 | } | ||
| 117 | |||
| 118 | static int | ||
| 119 | ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, | ||
| 120 | unsigned const char *new_code) | ||
| 121 | { | ||
| 122 | unsigned char replaced[MCOUNT_INSN_SIZE]; | ||
| 123 | |||
| 124 | ftrace_expected = old_code; | ||
| 125 | 80 | ||
| 126 | /* | 81 | /* |
| 127 | * Note: | 82 | * Note: |
| @@ -130,31 +85,46 @@ ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, · 2 unmodified lines | |||
| 130 | * Carefully read and modify the code with probe_kernel_*(), and make | 85 | * Carefully read and modify the code with probe_kernel_*(), and make |
| 131 | * sure what we read is what we expected it to be before modifying it. | 86 | * sure what we read is what we expected it to be before modifying it. |
| 132 | */ | 87 | */ |
| 133 | |||
| 134 | /* read the text we want to modify */ | 88 | /* read the text we want to modify */ |
| 135 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | 89 | if (probe_kernel_read(cur_code, (void *)ip, MCOUNT_INSN_SIZE)) { |
| 90 | WARN_ON(1); | ||
| 136 | return -EFAULT; | 91 | return -EFAULT; |
| 92 | } | ||
| 137 | 93 | ||
| 138 | /* Make sure it is what we expect it to be */ | 94 | /* Make sure it is what we expect it to be */ |
| 139 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) | 95 | if (memcmp(cur_code, old_code, MCOUNT_INSN_SIZE) != 0) { |
| 96 | WARN_ON(1); | ||
| 140 | return -EINVAL; | 97 | return -EINVAL; |
| 98 | } | ||
| 141 | 99 | ||
| 142 | ip = text_ip_addr(ip); | 100 | return 0; |
| 143 | 101 | } | |
| 144 | /* replace the text with the new text */ | ||
| 145 | if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) | ||
| 146 | return -EPERM; | ||
| 147 | 102 | ||
| 148 | sync_core(); | 103 | /* |
| 104 | * Marked __ref because it calls text_poke_early() which is .init.text. That is | ||
| 105 | * ok because that call will happen early, during boot, when .init sections are | ||
| 106 | * still present. | ||
| 107 | */ | ||
| 108 | static int __ref | ||
| 109 | ftrace_modify_code_direct(unsigned long ip, const char *old_code, | ||
| 110 | const char *new_code) | ||
| 111 | { | ||
| 112 | int ret = ftrace_verify_code(ip, old_code); | ||
| 113 | if (ret) | ||
| 114 | return ret; | ||
| 149 | 115 | ||
| 116 | /* replace the text with the new text */ | ||
| 117 | if (ftrace_poke_late) | ||
| 118 | text_poke_queue((void *)ip, new_code, MCOUNT_INSN_SIZE, NULL); | ||
| 119 | else | ||
| 120 | text_poke_early((void *)ip, new_code, MCOUNT_INSN_SIZE); | ||
| 150 | return 0; | 121 | return 0; |
| 151 | } | 122 | } |
| 152 | 123 | ||
| 153 | int ftrace_make_nop(struct module *mod, | 124 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) |
| 154 | struct dyn_ftrace *rec, unsigned long addr) | ||
| 155 | { | 125 | { |
| 156 | unsigned const char *new, *old; | ||
| 157 | unsigned long ip = rec->ip; | 126 | unsigned long ip = rec->ip; |
| 127 | const char *new, *old; | ||
| 158 | 128 | ||
| 159 | old = ftrace_call_replace(ip, addr); | 129 | old = ftrace_call_replace(ip, addr); |
| 160 | new = ftrace_nop_replace(); | 130 | new = ftrace_nop_replace(); |
| @@ -168,19 +138,20 @@ int ftrace_make_nop(struct module *mod, · 7 unmodified lines | |||
| 168 | * just modify the code directly. | 138 | * just modify the code directly. |
| 169 | */ | 139 | */ |
| 170 | if (addr == MCOUNT_ADDR) | 140 | if (addr == MCOUNT_ADDR) |
| 171 | return ftrace_modify_code_direct(rec->ip, old, new); | 141 | return ftrace_modify_code_direct(ip, old, new); |
| 172 | 142 | ||
| 173 | ftrace_expected = NULL; | 143 | /* |
| 174 | 144 | * x86 overrides ftrace_replace_code -- this function will never be used | |
| 175 | /* Normal cases use add_brk_on_nop */ | 145 | * in this case. |
| 146 | */ | ||
| 176 | WARN_ONCE(1, "invalid use of ftrace_make_nop"); | 147 | WARN_ONCE(1, "invalid use of ftrace_make_nop"); |
| 177 | return -EINVAL; | 148 | return -EINVAL; |
| 178 | } | 149 | } |
| 179 | 150 | ||
| 180 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 151 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
| 181 | { | 152 | { |
| 182 | unsigned const char *new, *old; | ||
| 183 | unsigned long ip = rec->ip; | 153 | unsigned long ip = rec->ip; |
| 154 | const char *new, *old; | ||
| 184 | 155 | ||
| 185 | old = ftrace_nop_replace(); | 156 | old = ftrace_nop_replace(); |
| 186 | new = ftrace_call_replace(ip, addr); | 157 | new = ftrace_call_replace(ip, addr); |
| @@ -189,43 +160,6 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) · 2 unmodified lines | |||
| 189 | return ftrace_modify_code_direct(rec->ip, old, new); | 160 | return ftrace_modify_code_direct(rec->ip, old, new); |
| 190 | } | 161 | } |
| 191 | 162 | ||
| 192 | /* | ||
| 193 | * The modifying_ftrace_code is used to tell the breakpoint | ||
| 194 | * handler to call ftrace_int3_handler(). If it fails to | ||
| 195 | * call this handler for a breakpoint added by ftrace, then | ||
| 196 | * the kernel may crash. | ||
| 197 | * | ||
| 198 | * As atomic_writes on x86 do not need a barrier, we do not | ||
| 199 | * need to add smp_mb()s for this to work. It is also considered | ||
| 200 | * that we can not read the modifying_ftrace_code before | ||
| 201 | * executing the breakpoint. That would be quite remarkable if | ||
| 202 | * it could do that. Here's the flow that is required: | ||
| 203 | * | ||
| 204 | * CPU-0 CPU-1 | ||
| 205 | * | ||
| 206 | * atomic_inc(mfc); | ||
| 207 | * write int3s | ||
| 208 | * <trap-int3> // implicit (r)mb | ||
| 209 | * if (atomic_read(mfc)) | ||
| 210 | * call ftrace_int3_handler() | ||
| 211 | * | ||
| 212 | * Then when we are finished: | ||
| 213 | * | ||
| 214 | * atomic_dec(mfc); | ||
| 215 | * | ||
| 216 | * If we hit a breakpoint that was not set by ftrace, it does not | ||
| 217 | * matter if ftrace_int3_handler() is called or not. It will | ||
| 218 | * simply be ignored. But it is crucial that a ftrace nop/caller | ||
| 219 | * breakpoint is handled. No other user should ever place a | ||
| 220 | * breakpoint on an ftrace nop/caller location. It must only | ||
| 221 | * be done by this code. | ||
| 222 | */ | ||
| 223 | atomic_t modifying_ftrace_code __read_mostly; | ||
| 224 | |||
| 225 | static int | ||
| 226 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | ||
| 227 | unsigned const char *new_code); | ||
| 228 | |||
| 229 | /* | 163 | /* |
| 230 | * Should never be called: | 164 | * Should never be called: |
| 231 | * As it is only called by __ftrace_replace_code() which is called by | 165 | * As it is only called by __ftrace_replace_code() which is called by |
| @@ -238,452 +172,84 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, · 6 unmodified lines | |||
| 238 | unsigned long addr) | 172 | unsigned long addr) |
| 239 | { | 173 | { |
| 240 | WARN_ON(1); | 174 | WARN_ON(1); |
| 241 | ftrace_expected = NULL; | ||
| 242 | return -EINVAL; | 175 | return -EINVAL; |
| 243 | } | 176 | } |
| 244 | 177 | ||
| 245 | static unsigned long ftrace_update_func; | ||
| 246 | static unsigned long ftrace_update_func_call; | ||
| 247 | |||
| 248 | static int update_ftrace_func(unsigned long ip, void *new) | ||
| 249 | { | ||
| 250 | unsigned char old[MCOUNT_INSN_SIZE]; | ||
| 251 | int ret; | ||
| 252 | |||
| 253 | memcpy(old, (void *)ip, MCOUNT_INSN_SIZE); | ||
| 254 | |||
| 255 | ftrace_update_func = ip; | ||
| 256 | /* Make sure the breakpoints see the ftrace_update_func update */ | ||
| 257 | smp_wmb(); | ||
| 258 | |||
| 259 | /* See comment above by declaration of modifying_ftrace_code */ | ||
| 260 | atomic_inc(&modifying_ftrace_code); | ||
| 261 | |||
| 262 | ret = ftrace_modify_code(ip, old, new); | ||
| 263 | |||
| 264 | atomic_dec(&modifying_ftrace_code); | ||
| 265 | |||
| 266 | return ret; | ||
| 267 | } | ||
| 268 | |||
| 269 | int ftrace_update_ftrace_func(ftrace_func_t func) | 178 | int ftrace_update_ftrace_func(ftrace_func_t func) |
| 270 | { | ||
| 271 | unsigned long ip = (unsigned long)(&ftrace_call); | ||
| 272 | unsigned char *new; | ||
| 273 | int ret; | ||
| 274 | |||
| 275 | ftrace_update_func_call = (unsigned long)func; | ||
| 276 | |||
| 277 | new = ftrace_call_replace(ip, (unsigned long)func); | ||
| 278 | ret = update_ftrace_func(ip, new); | ||
| 279 | |||
| 280 | /* Also update the regs callback function */ | ||
| 281 | if (!ret) { | ||
| 282 | ip = (unsigned long)(&ftrace_regs_call); | ||
| 283 | new = ftrace_call_replace(ip, (unsigned long)func); | ||
| 284 | ret = update_ftrace_func(ip, new); | ||
| 285 | } | ||
| 286 | |||
| 287 | return ret; | ||
| 288 | } | ||
| 289 | |||
| 290 | static nokprobe_inline int is_ftrace_caller(unsigned long ip) | ||
| 291 | { | ||
| 292 | if (ip == ftrace_update_func) | ||
| 293 | return 1; | ||
| 294 | |||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | |||
| 298 | /* | ||
| 299 | * A breakpoint was added to the code address we are about to | ||
| 300 | * modify, and this is the handle that will just skip over it. | ||
| 301 | * We are either changing a nop into a trace call, or a trace | ||
| 302 | * call to a nop. While the change is taking place, we treat | ||
| 303 | * it just like it was a nop. | ||
| 304 | */ | ||
| 305 | int ftrace_int3_handler(struct pt_regs *regs) | ||
| 306 | { | 179 | { |
| 307 | unsigned long ip; | 180 | unsigned long ip; |
| 181 | const char *new; | ||
| 308 | 182 | ||
| 309 | if (WARN_ON_ONCE(!regs)) | 183 | ip = (unsigned long)(&ftrace_call); |
| 310 | return 0; | 184 | new = ftrace_call_replace(ip, (unsigned long)func); |
| 311 | 185 | text_poke_bp((void *)ip, new, MCOUNT_INSN_SIZE, NULL); | |
| 312 | ip = regs->ip - INT3_INSN_SIZE; | ||
| 313 | |||
| 314 | if (ftrace_location(ip)) { | ||
| 315 | int3_emulate_call(regs, (unsigned long)ftrace_regs_caller); | ||
| 316 | return 1; | ||
| 317 | } else if (is_ftrace_caller(ip)) { | ||
| 318 | if (!ftrace_update_func_call) { | ||
| 319 | int3_emulate_jmp(regs, ip + CALL_INSN_SIZE); | ||
| 320 | return 1; | ||
| 321 | } | ||
| 322 | int3_emulate_call(regs, ftrace_update_func_call); | ||
| 323 | return 1; | ||
| 324 | } | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | NOKPROBE_SYMBOL(ftrace_int3_handler); | ||
| 329 | |||
| 330 | static int ftrace_write(unsigned long ip, const char *val, int size) | ||
| 331 | { | ||
| 332 | ip = text_ip_addr(ip); | ||
| 333 | |||
| 334 | if (probe_kernel_write((void *)ip, val, size)) | ||
| 335 | return -EPERM; | ||
| 336 | |||
| 337 | return 0; | ||
| 338 | } | ||
| 339 | |||
| 340 | static int add_break(unsigned long ip, const char *old) | ||
| 341 | { | ||
| 342 | unsigned char replaced[MCOUNT_INSN_SIZE]; | ||
| 343 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
| 344 | |||
| 345 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | ||
| 346 | return -EFAULT; | ||
| 347 | |||
| 348 | ftrace_expected = old; | ||
| 349 | |||
| 350 | /* Make sure it is what we expect it to be */ | ||
| 351 | if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0) | ||
| 352 | return -EINVAL; | ||
| 353 | |||
| 354 | return ftrace_write(ip, &brk, 1); | ||
| 355 | } | ||
| 356 | |||
| 357 | static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 358 | { | ||
| 359 | unsigned const char *old; | ||
| 360 | unsigned long ip = rec->ip; | ||
| 361 | |||
| 362 | old = ftrace_call_replace(ip, addr); | ||
| 363 | |||
| 364 | return add_break(rec->ip, old); | ||
| 365 | } | ||
| 366 | |||
| 367 | |||
| 368 | static int add_brk_on_nop(struct dyn_ftrace *rec) | ||
| 369 | { | ||
| 370 | unsigned const char *old; | ||
| 371 | |||
| 372 | old = ftrace_nop_replace(); | ||
| 373 | |||
| 374 | return add_break(rec->ip, old); | ||
| 375 | } | ||
| 376 | |||
| 377 | static int add_breakpoints(struct dyn_ftrace *rec, bool enable) | ||
| 378 | { | ||
| 379 | unsigned long ftrace_addr; | ||
| 380 | int ret; | ||
| 381 | |||
| 382 | ftrace_addr = ftrace_get_addr_curr(rec); | ||
| 383 | |||
| 384 | ret = ftrace_test_record(rec, enable); | ||
| 385 | |||
| 386 | switch (ret) { | ||
| 387 | case FTRACE_UPDATE_IGNORE: | ||
| 388 | return 0; | ||
| 389 | |||
| 390 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 391 | /* converting nop to call */ | ||
| 392 | return add_brk_on_nop(rec); | ||
| 393 | |||
| 394 | case FTRACE_UPDATE_MODIFY_CALL: | ||
| 395 | case FTRACE_UPDATE_MAKE_NOP: | ||
| 396 | /* converting a call to a nop */ | ||
| 397 | return add_brk_on_call(rec, ftrace_addr); | ||
| 398 | } | ||
| 399 | return 0; | ||
| 400 | } | ||
| 401 | |||
| 402 | /* | ||
| 403 | * On error, we need to remove breakpoints. This needs to | ||
| 404 | * be done caefully. If the address does not currently have a | ||
| 405 | * breakpoint, we know we are done. Otherwise, we look at the | ||
| 406 | * remaining 4 bytes of the instruction. If it matches a nop | ||
| 407 | * we replace the breakpoint with the nop. Otherwise we replace | ||
| 408 | * it with the call instruction. | ||
| 409 | */ | ||
| 410 | static int remove_breakpoint(struct dyn_ftrace *rec) | ||
| 411 | { | ||
| 412 | unsigned char ins[MCOUNT_INSN_SIZE]; | ||
| 413 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
| 414 | const unsigned char *nop; | ||
| 415 | unsigned long ftrace_addr; | ||
| 416 | unsigned long ip = rec->ip; | ||
| 417 | |||
| 418 | /* If we fail the read, just give up */ | ||
| 419 | if (probe_kernel_read(ins, (void *)ip, MCOUNT_INSN_SIZE)) | ||
| 420 | return -EFAULT; | ||
| 421 | |||
| 422 | /* If this does not have a breakpoint, we are done */ | ||
| 423 | if (ins[0] != brk) | ||
| 424 | return 0; | ||
| 425 | |||
| 426 | nop = ftrace_nop_replace(); | ||
| 427 | |||
| 428 | /* | ||
| 429 | * If the last 4 bytes of the instruction do not match | ||
| 430 | * a nop, then we assume that this is a call to ftrace_addr. | ||
| 431 | */ | ||
| 432 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) { | ||
| 433 | /* | ||
| 434 | * For extra paranoidism, we check if the breakpoint is on | ||
| 435 | * a call that would actually jump to the ftrace_addr. | ||
| 436 | * If not, don't touch the breakpoint, we make just create | ||
| 437 | * a disaster. | ||
| 438 | */ | ||
| 439 | ftrace_addr = ftrace_get_addr_new(rec); | ||
| 440 | nop = ftrace_call_replace(ip, ftrace_addr); | ||
| 441 | |||
| 442 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) == 0) | ||
| 443 | goto update; | ||
| 444 | |||
| 445 | /* Check both ftrace_addr and ftrace_old_addr */ | ||
| 446 | ftrace_addr = ftrace_get_addr_curr(rec); | ||
| 447 | nop = ftrace_call_replace(ip, ftrace_addr); | ||
| 448 | |||
| 449 | ftrace_expected = nop; | ||
| 450 | |||
| 451 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) | ||
| 452 | return -EINVAL; | ||
| 453 | } | ||
| 454 | |||
| 455 | update: | ||
| 456 | return ftrace_write(ip, nop, 1); | ||
| 457 | } | ||
| 458 | |||
| 459 | static int add_update_code(unsigned long ip, unsigned const char *new) | ||
| 460 | { | ||
| 461 | /* skip breakpoint */ | ||
| 462 | ip++; | ||
| 463 | new++; | ||
| 464 | return ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1); | ||
| 465 | } | ||
| 466 | |||
| 467 | static int add_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 468 | { | ||
| 469 | unsigned long ip = rec->ip; | ||
| 470 | unsigned const char *new; | ||
| 471 | |||
| 472 | new = ftrace_call_replace(ip, addr); | ||
| 473 | return add_update_code(ip, new); | ||
| 474 | } | ||
| 475 | |||
| 476 | static int add_update_nop(struct dyn_ftrace *rec) | ||
| 477 | { | ||
| 478 | unsigned long ip = rec->ip; | ||
| 479 | unsigned const char *new; | ||
| 480 | |||
| 481 | new = ftrace_nop_replace(); | ||
| 482 | return add_update_code(ip, new); | ||
| 483 | } | ||
| 484 | |||
| 485 | static int add_update(struct dyn_ftrace *rec, bool enable) | ||
| 486 | { | ||
| 487 | unsigned long ftrace_addr; | ||
| 488 | int ret; | ||
| 489 | |||
| 490 | ret = ftrace_test_record(rec, enable); | ||
| 491 | |||
| 492 | ftrace_addr = ftrace_get_addr_new(rec); | ||
| 493 | |||
| 494 | switch (ret) { | ||
| 495 | case FTRACE_UPDATE_IGNORE: | ||
| 496 | return 0; | ||
| 497 | |||
| 498 | case FTRACE_UPDATE_MODIFY_CALL: | ||
| 499 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 500 | /* converting nop to call */ | ||
| 501 | return add_update_call(rec, ftrace_addr); | ||
| 502 | |||
| 503 | case FTRACE_UPDATE_MAKE_NOP: | ||
| 504 | /* converting a call to a nop */ | ||
| 505 | return add_update_nop(rec); | ||
| 506 | } | ||
| 507 | |||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 512 | { | ||
| 513 | unsigned long ip = rec->ip; | ||
| 514 | unsigned const char *new; | ||
| 515 | |||
| 516 | new = ftrace_call_replace(ip, addr); | ||
| 517 | |||
| 518 | return ftrace_write(ip, new, 1); | ||
| 519 | } | ||
| 520 | |||
| 521 | static int finish_update_nop(struct dyn_ftrace *rec) | ||
| 522 | { | ||
| 523 | unsigned long ip = rec->ip; | ||
| 524 | unsigned const char *new; | ||
| 525 | |||
| 526 | new = ftrace_nop_replace(); | ||
| 527 | |||
| 528 | return ftrace_write(ip, new, 1); | ||
| 529 | } | ||
| 530 | |||
| 531 | static int finish_update(struct dyn_ftrace *rec, bool enable) | ||
| 532 | { | ||
| 533 | unsigned long ftrace_addr; | ||
| 534 | int ret; | ||
| 535 | |||
| 536 | ret = ftrace_update_record(rec, enable); | ||
| 537 | |||
| 538 | ftrace_addr = ftrace_get_addr_new(rec); | ||
| 539 | |||
| 540 | switch (ret) { | ||
| 541 | case FTRACE_UPDATE_IGNORE: | ||
| 542 | return 0; | ||
| 543 | |||
| 544 | case FTRACE_UPDATE_MODIFY_CALL: | ||
| 545 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 546 | /* converting nop to call */ | ||
| 547 | return finish_update_call(rec, ftrace_addr); | ||
| 548 | 186 | ||
| 549 | case FTRACE_UPDATE_MAKE_NOP: | 187 | ip = (unsigned long)(&ftrace_regs_call); |
| 550 | /* converting a call to a nop */ | 188 | new = ftrace_call_replace(ip, (unsigned long)func); |
| 551 | return finish_update_nop(rec); | 189 | text_poke_bp((void *)ip, new, MCOUNT_INSN_SIZE, NULL); |
| 552 | } | ||
| 553 | 190 | ||
| 554 | return 0; | 191 | return 0; |
| 555 | } | 192 | } |
| 556 | 193 | ||
| 557 | static void do_sync_core(void *data) | ||
| 558 | { | ||
| 559 | sync_core(); | ||
| 560 | } | ||
| 561 | |||
| 562 | static void run_sync(void) | ||
| 563 | { | ||
| 564 | int enable_irqs; | ||
| 565 | |||
| 566 | /* No need to sync if there's only one CPU */ | ||
| 567 | if (num_online_cpus() == 1) | ||
| 568 | return; | ||
| 569 | |||
| 570 | enable_irqs = irqs_disabled(); | ||
| 571 | |||
| 572 | /* We may be called with interrupts disabled (on bootup). */ | ||
| 573 | if (enable_irqs) | ||
| 574 | local_irq_enable(); | ||
| 575 | on_each_cpu(do_sync_core, NULL, 1); | ||
| 576 | if (enable_irqs) | ||
| 577 | local_irq_disable(); | ||
| 578 | } | ||
| 579 | |||
| 580 | void ftrace_replace_code(int enable) | 194 | void ftrace_replace_code(int enable) |
| 581 | { | 195 | { |
| 582 | struct ftrace_rec_iter *iter; | 196 | struct ftrace_rec_iter *iter; |
| 583 | struct dyn_ftrace *rec; | 197 | struct dyn_ftrace *rec; |
| 584 | const char *report = "adding breakpoints"; | 198 | const char *new, *old; |
| 585 | int count = 0; | ||
| 586 | int ret; | 199 | int ret; |
| 587 | 200 | ||
| 588 | for_ftrace_rec_iter(iter) { | 201 | for_ftrace_rec_iter(iter) { |
| 589 | rec = ftrace_rec_iter_record(iter); | 202 | rec = ftrace_rec_iter_record(iter); |
| 590 | 203 | ||
| 591 | ret = add_breakpoints(rec, enable); | 204 | switch (ftrace_test_record(rec, enable)) { |
| 592 | if (ret) | 205 | case FTRACE_UPDATE_IGNORE: |
| 593 | goto remove_breakpoints; | 206 | default: |
| 594 | count++; | 207 | continue; |
| 595 | } | ||
| 596 | |||
| 597 | run_sync(); | ||
| 598 | 208 | ||
| 599 | report = "updating code"; | 209 | case FTRACE_UPDATE_MAKE_CALL: |
| 600 | count = 0; | 210 | old = ftrace_nop_replace(); |
| 211 | break; | ||
| 601 | 212 | ||
| 602 | for_ftrace_rec_iter(iter) { | 213 | case FTRACE_UPDATE_MODIFY_CALL: |
| 603 | rec = ftrace_rec_iter_record(iter); | 214 | case FTRACE_UPDATE_MAKE_NOP: |
| 215 | old = ftrace_call_replace(rec->ip, ftrace_get_addr_curr(rec)); | ||
| 216 | break; | ||
| 217 | } | ||
| 604 | 218 | ||
| 605 | ret = add_update(rec, enable); | 219 | ret = ftrace_verify_code(rec->ip, old); |
| 606 | if (ret) | 220 | if (ret) { |
| 607 | goto remove_breakpoints; | 221 | ftrace_bug(ret, rec); |
| 608 | count++; | 222 | return; |
| 223 | } | ||
| 609 | } | 224 | } |
| 610 | 225 | ||
| 611 | run_sync(); | ||
| 612 | |||
| 613 | report = "removing breakpoints"; | ||
| 614 | count = 0; | ||
| 615 | |||
| 616 | for_ftrace_rec_iter(iter) { | 226 | for_ftrace_rec_iter(iter) { |
| 617 | rec = ftrace_rec_iter_record(iter); | 227 | rec = ftrace_rec_iter_record(iter); |
| 618 | 228 | ||
| 619 | ret = finish_update(rec, enable); | 229 | switch (ftrace_test_record(rec, enable)) { |
| 620 | if (ret) | 230 | case FTRACE_UPDATE_IGNORE: |
| 621 | goto remove_breakpoints; | 231 | default: |
| 622 | count++; | 232 | continue; |
| 623 | } | ||
| 624 | 233 | ||
| 625 | run_sync(); | 234 | case FTRACE_UPDATE_MAKE_CALL: |
| 235 | case FTRACE_UPDATE_MODIFY_CALL: | ||
| 236 | new = ftrace_call_replace(rec->ip, ftrace_get_addr_new(rec)); | ||
| 237 | break; | ||
| 626 | 238 | ||
| 627 | return; | 239 | case FTRACE_UPDATE_MAKE_NOP: |
| 240 | new = ftrace_nop_replace(); | ||
| 241 | break; | ||
| 242 | } | ||
| 628 | 243 | ||
| 629 | remove_breakpoints: | 244 | text_poke_queue((void *)rec->ip, new, MCOUNT_INSN_SIZE, NULL); |
| 630 | pr_warn("Failed on %s (%d):\n", report, count); | 245 | ftrace_update_record(rec, enable); |
| 631 | ftrace_bug(ret, rec); | ||
| 632 | for_ftrace_rec_iter(iter) { | ||
| 633 | rec = ftrace_rec_iter_record(iter); | ||
| 634 | /* | ||
| 635 | * Breakpoints are handled only when this function is in | ||
| 636 | * progress. The system could not work with them. | ||
| 637 | */ | ||
| 638 | if (remove_breakpoint(rec)) | ||
| 639 | BUG(); | ||
| 640 | } | 246 | } |
| 641 | run_sync(); | 247 | text_poke_finish(); |
| 642 | } | ||
| 643 | |||
| 644 | static int | ||
| 645 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | ||
| 646 | unsigned const char *new_code) | ||
| 647 | { | ||
| 648 | int ret; | ||
| 649 | |||
| 650 | ret = add_break(ip, old_code); | ||
| 651 | if (ret) | ||
| 652 | goto out; | ||
| 653 | |||
| 654 | run_sync(); | ||
| 655 | |||
| 656 | ret = add_update_code(ip, new_code); | ||
| 657 | if (ret) | ||
| 658 | goto fail_update; | ||
| 659 | |||
| 660 | run_sync(); | ||
| 661 | |||
| 662 | ret = ftrace_write(ip, new_code, 1); | ||
| 663 | /* | ||
| 664 | * The breakpoint is handled only when this function is in progress. | ||
| 665 | * The system could not work if we could not remove it. | ||
| 666 | */ | ||
| 667 | BUG_ON(ret); | ||
| 668 | out: | ||
| 669 | run_sync(); | ||
| 670 | return ret; | ||
| 671 | |||
| 672 | fail_update: | ||
| 673 | /* Also here the system could not work with the breakpoint */ | ||
| 674 | if (ftrace_write(ip, old_code, 1)) | ||
| 675 | BUG(); | ||
| 676 | goto out; | ||
| 677 | } | 248 | } |
| 678 | 249 | ||
| 679 | void arch_ftrace_update_code(int command) | 250 | void arch_ftrace_update_code(int command) |
| 680 | { | 251 | { |
| 681 | /* See comment above by declaration of modifying_ftrace_code */ | ||
| 682 | atomic_inc(&modifying_ftrace_code); | ||
| 683 | |||
| 684 | ftrace_modify_all_code(command); | 252 | ftrace_modify_all_code(command); |
| 685 | |||
| 686 | atomic_dec(&modifying_ftrace_code); | ||
| 687 | } | 253 | } |
| 688 | 254 | ||
| 689 | int __init ftrace_dyn_arch_init(void) | 255 | int __init ftrace_dyn_arch_init(void) |
| @@ -748,6 +314,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) · 58 unmodified lines | |||
| 748 | unsigned long start_offset; | 314 | unsigned long start_offset; |
| 749 | unsigned long end_offset; | 315 | unsigned long end_offset; |
| 750 | unsigned long op_offset; | 316 | unsigned long op_offset; |
| 317 | unsigned long call_offset; | ||
| 751 | unsigned long offset; | 318 | unsigned long offset; |
| 752 | unsigned long npages; | 319 | unsigned long npages; |
| 753 | unsigned long size; | 320 | unsigned long size; |
| @@ -764,10 +331,12 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) · 10 unmodified lines | |||
| 764 | start_offset = (unsigned long)ftrace_regs_caller; | 331 | start_offset = (unsigned long)ftrace_regs_caller; |
| 765 | end_offset = (unsigned long)ftrace_regs_caller_end; | 332 | end_offset = (unsigned long)ftrace_regs_caller_end; |
| 766 | op_offset = (unsigned long)ftrace_regs_caller_op_ptr; | 333 | op_offset = (unsigned long)ftrace_regs_caller_op_ptr; |
| 334 | call_offset = (unsigned long)ftrace_regs_call; | ||
| 767 | } else { | 335 | } else { |
| 768 | start_offset = (unsigned long)ftrace_caller; | 336 | start_offset = (unsigned long)ftrace_caller; |
| 769 | end_offset = (unsigned long)ftrace_epilogue; | 337 | end_offset = (unsigned long)ftrace_epilogue; |
| 770 | op_offset = (unsigned long)ftrace_caller_op_ptr; | 338 | op_offset = (unsigned long)ftrace_caller_op_ptr; |
| 339 | call_offset = (unsigned long)ftrace_call; | ||
| 771 | } | 340 | } |
| 772 | 341 | ||
| 773 | size = end_offset - start_offset; | 342 | size = end_offset - start_offset; |
| @@ -824,16 +393,21 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) · 50 unmodified lines | |||
| 824 | /* put in the new offset to the ftrace_ops */ | 393 | /* put in the new offset to the ftrace_ops */ |
| 825 | memcpy(trampoline + op_offset, &op_ptr, OP_REF_SIZE); | 394 | memcpy(trampoline + op_offset, &op_ptr, OP_REF_SIZE); |
| 826 | 395 | ||
| 396 | /* put in the call to the function */ | ||
| 397 | mutex_lock(&text_mutex); | ||
| 398 | call_offset -= start_offset; | ||
| 399 | memcpy(trampoline + call_offset, | ||
| 400 | text_gen_insn(CALL_INSN_OPCODE, | ||
| 401 | trampoline + call_offset, | ||
| 402 | ftrace_ops_get_func(ops)), CALL_INSN_SIZE); | ||
| 403 | mutex_unlock(&text_mutex); | ||
| 404 | |||
| 827 | /* ALLOC_TRAMP flags lets us know we created it */ | 405 | /* ALLOC_TRAMP flags lets us know we created it */ |
| 828 | ops->flags |= FTRACE_OPS_FL_ALLOC_TRAMP; | 406 | ops->flags |= FTRACE_OPS_FL_ALLOC_TRAMP; |
| 829 | 407 | ||
| 830 | set_vm_flush_reset_perms(trampoline); | 408 | set_vm_flush_reset_perms(trampoline); |
| 831 | 409 | ||
| 832 | /* | 410 | set_memory_ro((unsigned long)trampoline, npages); |
| 833 | * Module allocation needs to be completed by making the page | ||
| 834 | * executable. The page is still writable, which is a security hazard, | ||
| 835 | * but anyhow ftrace breaks W^X completely. | ||
| 836 | */ | ||
| 837 | set_memory_x((unsigned long)trampoline, npages); | 411 | set_memory_x((unsigned long)trampoline, npages); |
| 838 | return (unsigned long)trampoline; | 412 | return (unsigned long)trampoline; |
| 839 | fail: | 413 | fail: |
| @@ -860,62 +434,54 @@ static unsigned long calc_trampoline_call_offset(bool save_regs) · 20 unmodified lines | |||
| 860 | void arch_ftrace_update_trampoline(struct ftrace_ops *ops) | 434 | void arch_ftrace_update_trampoline(struct ftrace_ops *ops) |
| 861 | { | 435 | { |
| 862 | ftrace_func_t func; | 436 | ftrace_func_t func; |
| 863 | unsigned char *new; | ||
| 864 | unsigned long offset; | 437 | unsigned long offset; |
| 865 | unsigned long ip; | 438 | unsigned long ip; |
| 866 | unsigned int size; | 439 | unsigned int size; |
| 867 | int ret, npages; | 440 | const char *new; |
| 868 | 441 | ||
| 869 | if (ops->trampoline) { | 442 | if (!ops->trampoline) { |
| 870 | /* | ||
| 871 | * The ftrace_ops caller may set up its own trampoline. | ||
| 872 | * In such a case, this code must not modify it. | ||
| 873 | */ | ||
| 874 | if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP)) | ||
| 875 | return; | ||
| 876 | npages = PAGE_ALIGN(ops->trampoline_size) >> PAGE_SHIFT; | ||
| 877 | set_memory_rw(ops->trampoline, npages); | ||
| 878 | } else { | ||
| 879 | ops->trampoline = create_trampoline(ops, &size); | 443 | ops->trampoline = create_trampoline(ops, &size); |
| 880 | if (!ops->trampoline) | 444 | if (!ops->trampoline) |
| 881 | return; | 445 | return; |
| 882 | ops->trampoline_size = size; | 446 | ops->trampoline_size = size; |
| 883 | npages = PAGE_ALIGN(size) >> PAGE_SHIFT; | 447 | return; |
| 884 | } | 448 | } |
| 885 | 449 | ||
| 450 | /* | ||
| 451 | * The ftrace_ops caller may set up its own trampoline. | ||
| 452 | * In such a case, this code must not modify it. | ||
| 453 | */ | ||
| 454 | if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP)) | ||
| 455 | return; | ||
| 456 | |||
| 886 | offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS); | 457 | offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS); |
| 887 | ip = ops->trampoline + offset; | 458 | ip = ops->trampoline + offset; |
| 888 | |||
| 889 | func = ftrace_ops_get_func(ops); | 459 | func = ftrace_ops_get_func(ops); |
| 890 | 460 | ||
| 891 | ftrace_update_func_call = (unsigned long)func; | 461 | mutex_lock(&text_mutex); |
| 892 | |||
| 893 | /* Do a safe modify in case the trampoline is executing */ | 462 | /* Do a safe modify in case the trampoline is executing */ |
| 894 | new = ftrace_call_replace(ip, (unsigned long)func); | 463 | new = ftrace_call_replace(ip, (unsigned long)func); |
| 895 | ret = update_ftrace_func(ip, new); | 464 | text_poke_bp((void *)ip, new, MCOUNT_INSN_SIZE, NULL); |
| 896 | set_memory_ro(ops->trampoline, npages); | 465 | mutex_unlock(&text_mutex); |
| 897 | |||
| 898 | /* The update should never fail */ | ||
| 899 | WARN_ON(ret); | ||
| 900 | } | 466 | } |
| 901 | 467 | ||
| 902 | /* Return the address of the function the trampoline calls */ | 468 | /* Return the address of the function the trampoline calls */ |
| 903 | static void *addr_from_call(void *ptr) | 469 | static void *addr_from_call(void *ptr) |
| 904 | { | 470 | { |
| 905 | union ftrace_code_union calc; | 471 | union text_poke_insn call; |
| 906 | int ret; | 472 | int ret; |
| 907 | 473 | ||
| 908 | ret = probe_kernel_read(&calc, ptr, MCOUNT_INSN_SIZE); | 474 | ret = probe_kernel_read(&call, ptr, CALL_INSN_SIZE); |
| 909 | if (WARN_ON_ONCE(ret < 0)) | 475 | if (WARN_ON_ONCE(ret < 0)) |
| 910 | return NULL; | 476 | return NULL; |
| 911 | 477 | ||
| 912 | /* Make sure this is a call */ | 478 | /* Make sure this is a call */ |
| 913 | if (WARN_ON_ONCE(calc.op != 0xe8)) { | 479 | if (WARN_ON_ONCE(call.opcode != CALL_INSN_OPCODE)) { |
| 914 | pr_warn("Expected e8, got %x\n", calc.op); | 480 | pr_warn("Expected E8, got %x\n", call.opcode); |
| 915 | return NULL; | 481 | return NULL; |
| 916 | } | 482 | } |
| 917 | 483 | ||
| 918 | return ptr + MCOUNT_INSN_SIZE + calc.offset; | 484 | return ptr + CALL_INSN_SIZE + call.disp; |
| 919 | } | 485 | } |
| 920 | 486 | ||
| 921 | void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, | 487 | void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, |
| @@ -982,19 +548,18 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops) · 60 unmodified lines | |||
| 982 | #ifdef CONFIG_DYNAMIC_FTRACE | 548 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 983 | extern void ftrace_graph_call(void); | 549 | extern void ftrace_graph_call(void); |
| 984 | 550 | ||
| 985 | static unsigned char *ftrace_jmp_replace(unsigned long ip, unsigned long addr) | 551 | static const char *ftrace_jmp_replace(unsigned long ip, unsigned long addr) |
| 986 | { | 552 | { |
| 987 | return ftrace_text_replace(0xe9, ip, addr); | 553 | return text_gen_insn(JMP32_INSN_OPCODE, (void *)ip, (void *)addr); |
| 988 | } | 554 | } |
| 989 | 555 | ||
| 990 | static int ftrace_mod_jmp(unsigned long ip, void *func) | 556 | static int ftrace_mod_jmp(unsigned long ip, void *func) |
| 991 | { | 557 | { |
| 992 | unsigned char *new; | 558 | const char *new; |
| 993 | 559 | ||
| 994 | ftrace_update_func_call = 0UL; | ||
| 995 | new = ftrace_jmp_replace(ip, (unsigned long)func); | 560 | new = ftrace_jmp_replace(ip, (unsigned long)func); |
| 996 | 561 | text_poke_bp((void *)ip, new, MCOUNT_INSN_SIZE, NULL); | |
| 997 | return update_ftrace_func(ip, new); | 562 | return 0; |
| 998 | } | 563 | } |
| 999 | 564 | ||
| 1000 | int ftrace_enable_ftrace_graph_caller(void) | 565 | int ftrace_enable_ftrace_graph_caller(void) |
| @@ -1020,10 +585,9 @@ int ftrace_disable_ftrace_graph_caller(void) · 19 unmodified lines | |||
| 1020 | void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, | 585 | void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, |
| 1021 | unsigned long frame_pointer) | 586 | unsigned long frame_pointer) |
| 1022 | { | 587 | { |
| 588 | unsigned long return_hooker = (unsigned long)&return_to_handler; | ||
| 1023 | unsigned long old; | 589 | unsigned long old; |
| 1024 | int faulted; | 590 | int faulted; |
| 1025 | unsigned long return_hooker = (unsigned long) | ||
| 1026 | &return_to_handler; | ||
| 1027 | 591 | ||
| 1028 | /* | 592 | /* |
| 1029 | * When resuming from suspend-to-ram, this function can be indirectly | 593 | * When resuming from suspend-to-ram, this function can be indirectly |
| @@ -16,15 +16,7 @@ · 15 unmodified lines | |||
| 16 | #include <asm/alternative.h> | 16 | #include <asm/alternative.h> |
| 17 | #include <asm/text-patching.h> | 17 | #include <asm/text-patching.h> |
| 18 | 18 | ||
| 19 | union jump_code_union { | 19 | static void bug_at(const void *ip, int line) |
| 20 | char code[JUMP_LABEL_NOP_SIZE]; | ||
| 21 | struct { | ||
| 22 | char jump; | ||
| 23 | int offset; | ||
| 24 | } __attribute__((packed)); | ||
| 25 | }; | ||
| 26 | |||
| 27 | static void bug_at(unsigned char *ip, int line) | ||
| 28 | { | 20 | { |
| 29 | /* | 21 | /* |
| 30 | * The location is not an op that we were expecting. | 22 | * The location is not an op that we were expecting. |
| @@ -35,42 +27,42 @@ static void bug_at(unsigned char *ip, int line) · 4 unmodified lines | |||
| 35 | BUG(); | 27 | BUG(); |
| 36 | } | 28 | } |
| 37 | 29 | ||
| 38 | static void __jump_label_set_jump_code(struct jump_entry *entry, | 30 | static const void * |
| 39 | enum jump_label_type type, | 31 | __jump_label_set_jump_code(struct jump_entry *entry, enum jump_label_type type, int init) |
| 40 | union jump_code_union *code, | ||
| 41 | int init) | ||
| 42 | { | 32 | { |
| 43 | const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; | 33 | const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; |
| 44 | const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5]; | 34 | const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5]; |
| 45 | const void *expect; | 35 | const void *expect, *code; |
| 36 | const void *addr, *dest; | ||
| 46 | int line; | 37 | int line; |
| 47 | 38 | ||
| 48 | code->jump = 0xe9; | 39 | addr = (void *)jump_entry_code(entry); |
| 49 | code->offset = jump_entry_target(entry) - | 40 | dest = (void *)jump_entry_target(entry); |
| 50 | (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE); | 41 | |
| 42 | code = text_gen_insn(JMP32_INSN_OPCODE, addr, dest); | ||
| 51 | 43 | ||
| 52 | if (init) { | 44 | if (init) { |
| 53 | expect = default_nop; line = __LINE__; | 45 | expect = default_nop; line = __LINE__; |
| 54 | } else if (type == JUMP_LABEL_JMP) { | 46 | } else if (type == JUMP_LABEL_JMP) { |
| 55 | expect = ideal_nop; line = __LINE__; | 47 | expect = ideal_nop; line = __LINE__; |
| 56 | } else { | 48 | } else { |
| 57 | expect = code->code; line = __LINE__; | 49 | expect = code; line = __LINE__; |
| 58 | } | 50 | } |
| 59 | 51 | ||
| 60 | if (memcmp((void *)jump_entry_code(entry), expect, JUMP_LABEL_NOP_SIZE)) | 52 | if (memcmp(addr, expect, JUMP_LABEL_NOP_SIZE)) |
| 61 | bug_at((void *)jump_entry_code(entry), line); | 53 | bug_at(addr, line); |
| 62 | 54 | ||
| 63 | if (type == JUMP_LABEL_NOP) | 55 | if (type == JUMP_LABEL_NOP) |
| 64 | memcpy(code, ideal_nop, JUMP_LABEL_NOP_SIZE); | 56 | code = ideal_nop; |
| 57 | |||
| 58 | return code; | ||
| 65 | } | 59 | } |
| 66 | 60 | ||
| 67 | static void __ref __jump_label_transform(struct jump_entry *entry, | 61 | static void inline __jump_label_transform(struct jump_entry *entry, |
| 68 | enum jump_label_type type, | 62 | enum jump_label_type type, |
| 69 | int init) | 63 | int init) |
| 70 | { | 64 | { |
| 71 | union jump_code_union code; | 65 | const void *opcode = __jump_label_set_jump_code(entry, type, init); |
| 72 | |||
| 73 | __jump_label_set_jump_code(entry, type, &code, init); | ||
| 74 | 66 | ||
| 75 | /* | 67 | /* |
| 76 | * As long as only a single processor is running and the code is still | 68 | * As long as only a single processor is running and the code is still |
| @@ -84,31 +76,33 @@ static void __ref __jump_label_transform(struct jump_entry *entry, · 7 unmodified lines | |||
| 84 | * always nop being the 'currently valid' instruction | 76 | * always nop being the 'currently valid' instruction |
| 85 | */ | 77 | */ |
| 86 | if (init || system_state == SYSTEM_BOOTING) { | 78 | if (init || system_state == SYSTEM_BOOTING) { |
| 87 | text_poke_early((void *)jump_entry_code(entry), &code, | 79 | text_poke_early((void *)jump_entry_code(entry), opcode, |
| 88 | JUMP_LABEL_NOP_SIZE); | 80 | JUMP_LABEL_NOP_SIZE); |
| 89 | return; | 81 | return; |
| 90 | } | 82 | } |
| 91 | 83 | ||
| 92 | text_poke_bp((void *)jump_entry_code(entry), &code, JUMP_LABEL_NOP_SIZE, NULL); | 84 | text_poke_bp((void *)jump_entry_code(entry), opcode, JUMP_LABEL_NOP_SIZE, NULL); |
| 93 | } | 85 | } |
| 94 | 86 | ||
| 95 | void arch_jump_label_transform(struct jump_entry *entry, | 87 | static void __ref jump_label_transform(struct jump_entry *entry, |
| 96 | enum jump_label_type type) | 88 | enum jump_label_type type, |
| 89 | int init) | ||
| 97 | { | 90 | { |
| 98 | mutex_lock(&text_mutex); | 91 | mutex_lock(&text_mutex); |
| 99 | __jump_label_transform(entry, type, 0); | 92 | __jump_label_transform(entry, type, init); |
| 100 | mutex_unlock(&text_mutex); | 93 | mutex_unlock(&text_mutex); |
| 101 | } | 94 | } |
| 102 | 95 | ||
| 103 | #define TP_VEC_MAX (PAGE_SIZE / sizeof(struct text_poke_loc)) | 96 | void arch_jump_label_transform(struct jump_entry *entry, |
| 104 | static struct text_poke_loc tp_vec[TP_VEC_MAX]; | 97 | enum jump_label_type type) |
| 105 | static int tp_vec_nr; | 98 | { |
| 99 | jump_label_transform(entry, type, 0); | ||
| 100 | } | ||
| 106 | 101 | ||
| 107 | bool arch_jump_label_transform_queue(struct jump_entry *entry, | 102 | bool arch_jump_label_transform_queue(struct jump_entry *entry, |
| 108 | enum jump_label_type type) | 103 | enum jump_label_type type) |
| 109 | { | 104 | { |
| 110 | struct text_poke_loc *tp; | 105 | const void *opcode; |
| 111 | void *entry_code; | ||
| 112 | 106 | ||
| 113 | if (system_state == SYSTEM_BOOTING) { | 107 | if (system_state == SYSTEM_BOOTING) { |
| 114 | /* | 108 | /* |
| @@ -118,53 +112,19 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry, · 3 unmodified lines | |||
| 118 | return true; | 112 | return true; |
| 119 | } | 113 | } |
| 120 | 114 | ||
| 121 | /* | 115 | mutex_lock(&text_mutex); |
| 122 | * No more space in the vector, tell upper layer to apply | 116 | opcode = __jump_label_set_jump_code(entry, type, 0); |
| 123 | * the queue before continuing. | 117 | text_poke_queue((void *)jump_entry_code(entry), |
| 124 | */ | 118 | opcode, JUMP_LABEL_NOP_SIZE, NULL); |
| 125 | if (tp_vec_nr == TP_VEC_MAX) | 119 | mutex_unlock(&text_mutex); |
| 126 | return false; | ||
| 127 | |||
| 128 | tp = &tp_vec[tp_vec_nr]; | ||
| 129 | |||
| 130 | entry_code = (void *)jump_entry_code(entry); | ||
| 131 | |||
| 132 | /* | ||
| 133 | * The INT3 handler will do a bsearch in the queue, so we need entries | ||
| 134 | * to be sorted. We can survive an unsorted list by rejecting the entry, | ||
| 135 | * forcing the generic jump_label code to apply the queue. Warning once, | ||
| 136 | * to raise the attention to the case of an unsorted entry that is | ||
| 137 | * better not happen, because, in the worst case we will perform in the | ||
| 138 | * same way as we do without batching - with some more overhead. | ||
| 139 | */ | ||
| 140 | if (tp_vec_nr > 0) { | ||
| 141 | int prev = tp_vec_nr - 1; | ||
| 142 | struct text_poke_loc *prev_tp = &tp_vec[prev]; | ||
| 143 | |||
| 144 | if (WARN_ON_ONCE(prev_tp->addr > entry_code)) | ||
| 145 | return false; | ||
| 146 | } | ||
| 147 | |||
| 148 | __jump_label_set_jump_code(entry, type, | ||
| 149 | (union jump_code_union *)&tp->text, 0); | ||
| 150 | |||
| 151 | text_poke_loc_init(tp, entry_code, NULL, JUMP_LABEL_NOP_SIZE, NULL); | ||
| 152 | |||
| 153 | tp_vec_nr++; | ||
| 154 | |||
| 155 | return true; | 120 | return true; |
| 156 | } | 121 | } |
| 157 | 122 | ||
| 158 | void arch_jump_label_transform_apply(void) | 123 | void arch_jump_label_transform_apply(void) |
| 159 | { | 124 | { |
| 160 | if (!tp_vec_nr) | ||
| 161 | return; | ||
| 162 | |||
| 163 | mutex_lock(&text_mutex); | 125 | mutex_lock(&text_mutex); |
| 164 | text_poke_bp_batch(tp_vec, tp_vec_nr); | 126 | text_poke_finish(); |
| 165 | mutex_unlock(&text_mutex); | 127 | mutex_unlock(&text_mutex); |
| 166 | |||
| 167 | tp_vec_nr = 0; | ||
| 168 | } | 128 | } |
| 169 | 129 | ||
| 170 | static enum { | 130 | static enum { |
| @@ -193,5 +153,5 @@ __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry, · 22 unmodified lines | |||
| 193 | jlstate = JL_STATE_NO_UPDATE; | 153 | jlstate = JL_STATE_NO_UPDATE; |
| 194 | } | 154 | } |
| 195 | if (jlstate == JL_STATE_UPDATE) | 155 | if (jlstate == JL_STATE_UPDATE) |
| 196 | __jump_label_transform(entry, type, 1); | 156 | jump_label_transform(entry, type, 1); |
| 197 | } | 157 | } |
| @@ -120,14 +120,14 @@ __synthesize_relative_insn(void *dest, void *from, void *to, u8 op) · 119 unmodified lines | |||
| 120 | /* Insert a jump instruction at address 'from', which jumps to address 'to'.*/ | 120 | /* Insert a jump instruction at address 'from', which jumps to address 'to'.*/ |
| 121 | void synthesize_reljump(void *dest, void *from, void *to) | 121 | void synthesize_reljump(void *dest, void *from, void *to) |
| 122 | { | 122 | { |
| 123 | __synthesize_relative_insn(dest, from, to, RELATIVEJUMP_OPCODE); | 123 | __synthesize_relative_insn(dest, from, to, JMP32_INSN_OPCODE); |
| 124 | } | 124 | } |
| 125 | NOKPROBE_SYMBOL(synthesize_reljump); | 125 | NOKPROBE_SYMBOL(synthesize_reljump); |
| 126 | 126 | ||
| 127 | /* Insert a call instruction at address 'from', which calls address 'to'.*/ | 127 | /* Insert a call instruction at address 'from', which calls address 'to'.*/ |
| 128 | void synthesize_relcall(void *dest, void *from, void *to) | 128 | void synthesize_relcall(void *dest, void *from, void *to) |
| 129 | { | 129 | { |
| 130 | __synthesize_relative_insn(dest, from, to, RELATIVECALL_OPCODE); | 130 | __synthesize_relative_insn(dest, from, to, CALL_INSN_OPCODE); |
| 131 | } | 131 | } |
| 132 | NOKPROBE_SYMBOL(synthesize_relcall); | 132 | NOKPROBE_SYMBOL(synthesize_relcall); |
| 133 | 133 | ||
| @@ -302,7 +302,7 @@ static int can_probe(unsigned long paddr) · 168 unmodified lines | |||
| 302 | * Another debugging subsystem might insert this breakpoint. | 302 | * Another debugging subsystem might insert this breakpoint. |
| 303 | * In that case, we can't recover it. | 303 | * In that case, we can't recover it. |
| 304 | */ | 304 | */ |
| 305 | if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) | 305 | if (insn.opcode.bytes[0] == INT3_INSN_OPCODE) |
| 306 | return 0; | 306 | return 0; |
| 307 | addr += insn.length; | 307 | addr += insn.length; |
| 308 | } | 308 | } |
| @@ -357,7 +357,7 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn) · 48 unmodified lines | |||
| 357 | return 0; | 357 | return 0; |
| 358 | 358 | ||
| 359 | /* Another subsystem puts a breakpoint, failed to recover */ | 359 | /* Another subsystem puts a breakpoint, failed to recover */ |
| 360 | if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION) | 360 | if (insn->opcode.bytes[0] == INT3_INSN_OPCODE) |
| 361 | return 0; | 361 | return 0; |
| 362 | 362 | ||
| 363 | /* We should not singlestep on the exception masking instructions */ | 363 | /* We should not singlestep on the exception masking instructions */ |
| @@ -401,14 +401,14 @@ static int prepare_boost(kprobe_opcode_t *buf, struct kprobe *p, · 37 unmodified lines | |||
| 401 | int len = insn->length; | 401 | int len = insn->length; |
| 402 | 402 | ||
| 403 | if (can_boost(insn, p->addr) && | 403 | if (can_boost(insn, p->addr) && |
| 404 | MAX_INSN_SIZE - len >= RELATIVEJUMP_SIZE) { | 404 | MAX_INSN_SIZE - len >= JMP32_INSN_SIZE) { |
| 405 | /* | 405 | /* |
| 406 | * These instructions can be executed directly if it | 406 | * These instructions can be executed directly if it |
| 407 | * jumps back to correct address. | 407 | * jumps back to correct address. |
| 408 | */ | 408 | */ |
| 409 | synthesize_reljump(buf + len, p->ainsn.insn + len, | 409 | synthesize_reljump(buf + len, p->ainsn.insn + len, |
| 410 | p->addr + insn->length); | 410 | p->addr + insn->length); |
| 411 | len += RELATIVEJUMP_SIZE; | 411 | len += JMP32_INSN_SIZE; |
| 412 | p->ainsn.boostable = true; | 412 | p->ainsn.boostable = true; |
| 413 | } else { | 413 | } else { |
| 414 | p->ainsn.boostable = false; | 414 | p->ainsn.boostable = false; |
| @@ -502,12 +502,14 @@ int arch_prepare_kprobe(struct kprobe *p) · 87 unmodified lines | |||
| 502 | 502 | ||
| 503 | void arch_arm_kprobe(struct kprobe *p) | 503 | void arch_arm_kprobe(struct kprobe *p) |
| 504 | { | 504 | { |
| 505 | text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1); | 505 | text_poke(p->addr, ((unsigned char []){INT3_INSN_OPCODE}), 1); |
| 506 | text_poke_sync(); | ||
| 506 | } | 507 | } |
| 507 | 508 | ||
| 508 | void arch_disarm_kprobe(struct kprobe *p) | 509 | void arch_disarm_kprobe(struct kprobe *p) |
| 509 | { | 510 | { |
| 510 | text_poke(p->addr, &p->opcode, 1); | 511 | text_poke(p->addr, &p->opcode, 1); |
| 512 | text_poke_sync(); | ||
| 511 | } | 513 | } |
| 512 | 514 | ||
| 513 | void arch_remove_kprobe(struct kprobe *p) | 515 | void arch_remove_kprobe(struct kprobe *p) |
| @@ -610,7 +612,7 @@ static void setup_singlestep(struct kprobe *p, struct pt_regs *regs, · 96 unmodified lines | |||
| 610 | regs->flags |= X86_EFLAGS_TF; | 612 | regs->flags |= X86_EFLAGS_TF; |
| 611 | regs->flags &= ~X86_EFLAGS_IF; | 613 | regs->flags &= ~X86_EFLAGS_IF; |
| 612 | /* single step inline if the instruction is an int3 */ | 614 | /* single step inline if the instruction is an int3 */ |
| 613 | if (p->opcode == BREAKPOINT_INSTRUCTION) | 615 | if (p->opcode == INT3_INSN_OPCODE) |
| 614 | regs->ip = (unsigned long)p->addr; | 616 | regs->ip = (unsigned long)p->addr; |
| 615 | else | 617 | else |
| 616 | regs->ip = (unsigned long)p->ainsn.insn; | 618 | regs->ip = (unsigned long)p->ainsn.insn; |
| @@ -696,7 +698,7 @@ int kprobe_int3_handler(struct pt_regs *regs) · 79 unmodified lines | |||
| 696 | reset_current_kprobe(); | 698 | reset_current_kprobe(); |
| 697 | return 1; | 699 | return 1; |
| 698 | } | 700 | } |
| 699 | } else if (*addr != BREAKPOINT_INSTRUCTION) { | 701 | } else if (*addr != INT3_INSN_OPCODE) { |
| 700 | /* | 702 | /* |
| 701 | * The breakpoint instruction was removed right | 703 | * The breakpoint instruction was removed right |
| 702 | * after we hit it. Another cpu has removed | 704 | * after we hit it. Another cpu has removed |
| @@ -38,7 +38,7 @@ unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsigned long addr) · 37 unmodified lines | |||
| 38 | long offs; | 38 | long offs; |
| 39 | int i; | 39 | int i; |
| 40 | 40 | ||
| 41 | for (i = 0; i < RELATIVEJUMP_SIZE; i++) { | 41 | for (i = 0; i < JMP32_INSN_SIZE; i++) { |
| 42 | kp = get_kprobe((void *)addr - i); | 42 | kp = get_kprobe((void *)addr - i); |
| 43 | /* This function only handles jump-optimized kprobe */ | 43 | /* This function only handles jump-optimized kprobe */ |
| 44 | if (kp && kprobe_optimized(kp)) { | 44 | if (kp && kprobe_optimized(kp)) { |
| @@ -62,10 +62,10 @@ found: · 17 unmodified lines | |||
| 62 | 62 | ||
| 63 | if (addr == (unsigned long)kp->addr) { | 63 | if (addr == (unsigned long)kp->addr) { |
| 64 | buf[0] = kp->opcode; | 64 | buf[0] = kp->opcode; |
| 65 | memcpy(buf + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE); | 65 | memcpy(buf + 1, op->optinsn.copied_insn, DISP32_SIZE); |
| 66 | } else { | 66 | } else { |
| 67 | offs = addr - (unsigned long)kp->addr - 1; | 67 | offs = addr - (unsigned long)kp->addr - 1; |
| 68 | memcpy(buf, op->optinsn.copied_insn + offs, RELATIVE_ADDR_SIZE - offs); | 68 | memcpy(buf, op->optinsn.copied_insn + offs, DISP32_SIZE - offs); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | return (unsigned long)buf; | 71 | return (unsigned long)buf; |
| @@ -141,8 +141,6 @@ STACK_FRAME_NON_STANDARD(optprobe_template_func); · 69 unmodified lines | |||
| 141 | #define TMPL_END_IDX \ | 141 | #define TMPL_END_IDX \ |
| 142 | ((long)optprobe_template_end - (long)optprobe_template_entry) | 142 | ((long)optprobe_template_end - (long)optprobe_template_entry) |
| 143 | 143 | ||
| 144 | #define INT3_SIZE sizeof(kprobe_opcode_t) | ||
| 145 | |||
| 146 | /* Optimized kprobe call back function: called from optinsn */ | 144 | /* Optimized kprobe call back function: called from optinsn */ |
| 147 | static void | 145 | static void |
| 148 | optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) | 146 | optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) |
| @@ -162,7 +160,7 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) · 13 unmodified lines | |||
| 162 | regs->cs |= get_kernel_rpl(); | 160 | regs->cs |= get_kernel_rpl(); |
| 163 | regs->gs = 0; | 161 | regs->gs = 0; |
| 164 | #endif | 162 | #endif |
| 165 | regs->ip = (unsigned long)op->kp.addr + INT3_SIZE; | 163 | regs->ip = (unsigned long)op->kp.addr + INT3_INSN_SIZE; |
| 166 | regs->orig_ax = ~0UL; | 164 | regs->orig_ax = ~0UL; |
| 167 | 165 | ||
| 168 | __this_cpu_write(current_kprobe, &op->kp); | 166 | __this_cpu_write(current_kprobe, &op->kp); |
| @@ -179,7 +177,7 @@ static int copy_optimized_instructions(u8 *dest, u8 *src, u8 *real) · 10 unmodified lines | |||
| 179 | struct insn insn; | 177 | struct insn insn; |
| 180 | int len = 0, ret; | 178 | int len = 0, ret; |
| 181 | 179 | ||
| 182 | while (len < RELATIVEJUMP_SIZE) { | 180 | while (len < JMP32_INSN_SIZE) { |
| 183 | ret = __copy_instruction(dest + len, src + len, real + len, &insn); | 181 | ret = __copy_instruction(dest + len, src + len, real + len, &insn); |
| 184 | if (!ret || !can_boost(&insn, src + len)) | 182 | if (!ret || !can_boost(&insn, src + len)) |
| 185 | return -EINVAL; | 183 | return -EINVAL; |
| @@ -271,7 +269,7 @@ static int can_optimize(unsigned long paddr) · 85 unmodified lines | |||
| 271 | return 0; | 269 | return 0; |
| 272 | 270 | ||
| 273 | /* Check there is enough space for a relative jump. */ | 271 | /* Check there is enough space for a relative jump. */ |
| 274 | if (size - offset < RELATIVEJUMP_SIZE) | 272 | if (size - offset < JMP32_INSN_SIZE) |
| 275 | return 0; | 273 | return 0; |
| 276 | 274 | ||
| 277 | /* Decode instructions */ | 275 | /* Decode instructions */ |
| @@ -290,15 +288,15 @@ static int can_optimize(unsigned long paddr) · 12 unmodified lines | |||
| 290 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); | 288 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); |
| 291 | insn_get_length(&insn); | 289 | insn_get_length(&insn); |
| 292 | /* Another subsystem puts a breakpoint */ | 290 | /* Another subsystem puts a breakpoint */ |
| 293 | if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) | 291 | if (insn.opcode.bytes[0] == INT3_INSN_OPCODE) |
| 294 | return 0; | 292 | return 0; |
| 295 | /* Recover address */ | 293 | /* Recover address */ |
| 296 | insn.kaddr = (void *)addr; | 294 | insn.kaddr = (void *)addr; |
| 297 | insn.next_byte = (void *)(addr + insn.length); | 295 | insn.next_byte = (void *)(addr + insn.length); |
| 298 | /* Check any instructions don't jump into target */ | 296 | /* Check any instructions don't jump into target */ |
| 299 | if (insn_is_indirect_jump(&insn) || | 297 | if (insn_is_indirect_jump(&insn) || |
| 300 | insn_jump_into_range(&insn, paddr + INT3_SIZE, | 298 | insn_jump_into_range(&insn, paddr + INT3_INSN_SIZE, |
| 301 | RELATIVE_ADDR_SIZE)) | 299 | DISP32_SIZE)) |
| 302 | return 0; | 300 | return 0; |
| 303 | addr += insn.length; | 301 | addr += insn.length; |
| 304 | } | 302 | } |
| @@ -374,7 +372,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, · 69 unmodified lines | |||
| 374 | * Verify if the address gap is in 2GB range, because this uses | 372 | * Verify if the address gap is in 2GB range, because this uses |
| 375 | * a relative jump. | 373 | * a relative jump. |
| 376 | */ | 374 | */ |
| 377 | rel = (long)slot - (long)op->kp.addr + RELATIVEJUMP_SIZE; | 375 | rel = (long)slot - (long)op->kp.addr + JMP32_INSN_SIZE; |
| 378 | if (abs(rel) > 0x7fffffff) { | 376 | if (abs(rel) > 0x7fffffff) { |
| 379 | ret = -ERANGE; | 377 | ret = -ERANGE; |
| 380 | goto err; | 378 | goto err; |
| @@ -401,7 +399,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, · 20 unmodified lines | |||
| 401 | /* Set returning jmp instruction at the tail of out-of-line buffer */ | 399 | /* Set returning jmp instruction at the tail of out-of-line buffer */ |
| 402 | synthesize_reljump(buf + len, slot + len, | 400 | synthesize_reljump(buf + len, slot + len, |
| 403 | (u8 *)op->kp.addr + op->optinsn.size); | 401 | (u8 *)op->kp.addr + op->optinsn.size); |
| 404 | len += RELATIVEJUMP_SIZE; | 402 | len += JMP32_INSN_SIZE; |
| 405 | 403 | ||
| 406 | /* We have to use text_poke() for instruction buffer because it is RO */ | 404 | /* We have to use text_poke() for instruction buffer because it is RO */ |
| 407 | text_poke(slot, buf, len); | 405 | text_poke(slot, buf, len); |
| @@ -416,49 +414,50 @@ err: · 8 unmodified lines | |||
| 416 | } | 414 | } |
| 417 | 415 | ||
| 418 | /* | 416 | /* |
| 419 | * Replace breakpoints (int3) with relative jumps. | 417 | * Replace breakpoints (INT3) with relative jumps (JMP.d32). |
| 420 | * Caller must call with locking kprobe_mutex and text_mutex. | 418 | * Caller must call with locking kprobe_mutex and text_mutex. |
| 419 | * | ||
| 420 | * The caller will have installed a regular kprobe and after that issued | ||
| 421 | * syncrhonize_rcu_tasks(), this ensures that the instruction(s) that live in | ||
| 422 | * the 4 bytes after the INT3 are unused and can now be overwritten. | ||
| 421 | */ | 423 | */ |
| 422 | void arch_optimize_kprobes(struct list_head *oplist) | 424 | void arch_optimize_kprobes(struct list_head *oplist) |
| 423 | { | 425 | { |
| 424 | struct optimized_kprobe *op, *tmp; | 426 | struct optimized_kprobe *op, *tmp; |
| 425 | u8 insn_buff[RELATIVEJUMP_SIZE]; | 427 | u8 insn_buff[JMP32_INSN_SIZE]; |
| 426 | 428 | ||
| 427 | list_for_each_entry_safe(op, tmp, oplist, list) { | 429 | list_for_each_entry_safe(op, tmp, oplist, list) { |
| 428 | s32 rel = (s32)((long)op->optinsn.insn - | 430 | s32 rel = (s32)((long)op->optinsn.insn - |
| 429 | ((long)op->kp.addr + RELATIVEJUMP_SIZE)); | 431 | ((long)op->kp.addr + JMP32_INSN_SIZE)); |
| 430 | 432 | ||
| 431 | WARN_ON(kprobe_disabled(&op->kp)); | 433 | WARN_ON(kprobe_disabled(&op->kp)); |
| 432 | 434 | ||
| 433 | /* Backup instructions which will be replaced by jump address */ | 435 | /* Backup instructions which will be replaced by jump address */ |
| 434 | memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE, | 436 | memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_INSN_SIZE, |
| 435 | RELATIVE_ADDR_SIZE); | 437 | DISP32_SIZE); |
| 436 | 438 | ||
| 437 | insn_buff[0] = RELATIVEJUMP_OPCODE; | 439 | insn_buff[0] = JMP32_INSN_OPCODE; |
| 438 | *(s32 *)(&insn_buff[1]) = rel; | 440 | *(s32 *)(&insn_buff[1]) = rel; |
| 439 | 441 | ||
| 440 | text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE, NULL); | 442 | text_poke_bp(op->kp.addr, insn_buff, JMP32_INSN_SIZE, NULL); |
| 441 | 443 | ||
| 442 | list_del_init(&op->list); | 444 | list_del_init(&op->list); |
| 443 | } | 445 | } |
| 444 | } | 446 | } |
| 445 | 447 | ||
| 446 | /* Replace a relative jump with a breakpoint (int3). */ | 448 | /* |
| 449 | * Replace a relative jump (JMP.d32) with a breakpoint (INT3). | ||
| 450 | * | ||
| 451 | * After that, we can restore the 4 bytes after the INT3 to undo what | ||
| 452 | * arch_optimize_kprobes() scribbled. This is safe since those bytes will be | ||
| 453 | * unused once the INT3 lands. | ||
| 454 | */ | ||
| 447 | void arch_unoptimize_kprobe(struct optimized_kprobe *op) | 455 | void arch_unoptimize_kprobe(struct optimized_kprobe *op) |
| 448 | { | 456 | { |
| 449 | u8 insn_buff[RELATIVEJUMP_SIZE]; | 457 | arch_arm_kprobe(&op->kp); |
| 450 | u8 emulate_buff[RELATIVEJUMP_SIZE]; | 458 | text_poke(op->kp.addr + INT3_INSN_SIZE, |
| 451 | 459 | op->optinsn.copied_insn, DISP32_SIZE); | |
| 452 | /* Set int3 to first byte for kprobes */ | 460 | text_poke_sync(); |
| 453 | insn_buff[0] = BREAKPOINT_INSTRUCTION; | ||
| 454 | memcpy(insn_buff + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE); | ||
| 455 | |||
| 456 | emulate_buff[0] = RELATIVEJUMP_OPCODE; | ||
| 457 | *(s32 *)(&emulate_buff[1]) = (s32)((long)op->optinsn.insn - | ||
| 458 | ((long)op->kp.addr + RELATIVEJUMP_SIZE)); | ||
| 459 | |||
| 460 | text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE, | ||
| 461 | emulate_buff); | ||
| 462 | } | 461 | } |
| 463 | 462 | ||
| 464 | /* | 463 | /* |
| @@ -572,15 +572,6 @@ NOKPROBE_SYMBOL(do_general_protection); · 571 unmodified lines | |||
| 572 | 572 | ||
| 573 | dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) | 573 | dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) |
| 574 | { | 574 | { |
| 575 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 576 | /* | ||
| 577 | * ftrace must be first, everything else may cause a recursive crash. | ||
| 578 | * See note by declaration of modifying_ftrace_code in ftrace.c | ||
| 579 | */ | ||
| 580 | if (unlikely(atomic_read(&modifying_ftrace_code)) && | ||
| 581 | ftrace_int3_handler(regs)) | ||
| 582 | return; | ||
| 583 | #endif | ||
| 584 | if (poke_int3_handler(regs)) | 575 | if (poke_int3_handler(regs)) |
| 585 | return; | 576 | return; |
| 586 | 577 | ||
| @@ -873,34 +873,6 @@ void arch_remove_memory(int nid, u64 start, u64 size, · 872 unmodified lines | |||
| 873 | 873 | ||
| 874 | int kernel_set_to_readonly __read_mostly; | 874 | int kernel_set_to_readonly __read_mostly; |
| 875 | 875 | ||
| 876 | void set_kernel_text_rw(void) | ||
| 877 | { | ||
| 878 | unsigned long start = PFN_ALIGN(_text); | ||
| 879 | unsigned long size = PFN_ALIGN(_etext) - start; | ||
| 880 | |||
| 881 | if (!kernel_set_to_readonly) | ||
| 882 | return; | ||
| 883 | |||
| 884 | pr_debug("Set kernel text: %lx - %lx for read write\n", | ||
| 885 | start, start+size); | ||
| 886 | |||
| 887 | set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT); | ||
| 888 | } | ||
| 889 | |||
| 890 | void set_kernel_text_ro(void) | ||
| 891 | { | ||
| 892 | unsigned long start = PFN_ALIGN(_text); | ||
| 893 | unsigned long size = PFN_ALIGN(_etext) - start; | ||
| 894 | |||
| 895 | if (!kernel_set_to_readonly) | ||
| 896 | return; | ||
| 897 | |||
| 898 | pr_debug("Set kernel text: %lx - %lx for read only\n", | ||
| 899 | start, start+size); | ||
| 900 | |||
| 901 | set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); | ||
| 902 | } | ||
| 903 | |||
| 904 | static void mark_nxdata_nx(void) | 876 | static void mark_nxdata_nx(void) |
| 905 | { | 877 | { |
| 906 | /* | 878 | /* |
| @@ -1258,42 +1258,6 @@ void __init mem_init(void) · 1257 unmodified lines | |||
| 1258 | 1258 | ||
| 1259 | int kernel_set_to_readonly; | 1259 | int kernel_set_to_readonly; |
| 1260 | 1260 | ||
| 1261 | void set_kernel_text_rw(void) | ||
| 1262 | { | ||
| 1263 | unsigned long start = PFN_ALIGN(_text); | ||
| 1264 | unsigned long end = PFN_ALIGN(_etext); | ||
| 1265 | |||
| 1266 | if (!kernel_set_to_readonly) | ||
| 1267 | return; | ||
| 1268 | |||
| 1269 | pr_debug("Set kernel text: %lx - %lx for read write\n", | ||
| 1270 | start, end); | ||
| 1271 | |||
| 1272 | /* | ||
| 1273 | * Make the kernel identity mapping for text RW. Kernel text | ||
| 1274 | * mapping will always be RO. Refer to the comment in | ||
| 1275 | * static_protections() in pageattr.c | ||
| 1276 | */ | ||
| 1277 | set_memory_rw(start, (end - start) >> PAGE_SHIFT); | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | void set_kernel_text_ro(void) | ||
| 1281 | { | ||
| 1282 | unsigned long start = PFN_ALIGN(_text); | ||
| 1283 | unsigned long end = PFN_ALIGN(_etext); | ||
| 1284 | |||
| 1285 | if (!kernel_set_to_readonly) | ||
| 1286 | return; | ||
| 1287 | |||
| 1288 | pr_debug("Set kernel text: %lx - %lx for read only\n", | ||
| 1289 | start, end); | ||
| 1290 | |||
| 1291 | /* | ||
| 1292 | * Set the kernel identity mapping for text RO. | ||
| 1293 | */ | ||
| 1294 | set_memory_ro(start, (end - start) >> PAGE_SHIFT); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | void mark_rodata_ro(void) | 1261 | void mark_rodata_ro(void) |
| 1298 | { | 1262 | { |
| 1299 | unsigned long start = PFN_ALIGN(_text); | 1263 | unsigned long start = PFN_ALIGN(_text); |
| @@ -138,10 +138,10 @@ TRACE_EVENT(/* put_tid */ · 137 unmodified lines | |||
| 138 | TP_ARGS(dd, index, type, pa, order), | 138 | TP_ARGS(dd, index, type, pa, order), |
| 139 | TP_STRUCT__entry(/* entry */ | 139 | TP_STRUCT__entry(/* entry */ |
| 140 | DD_DEV_ENTRY(dd) | 140 | DD_DEV_ENTRY(dd) |
| 141 | __field(unsigned long, pa); | 141 | __field(unsigned long, pa) |
| 142 | __field(u32, index); | 142 | __field(u32, index) |
| 143 | __field(u32, type); | 143 | __field(u32, type) |
| 144 | __field(u16, order); | 144 | __field(u16, order) |
| 145 | ), | 145 | ), |
| 146 | TP_fast_assign(/* assign */ | 146 | TP_fast_assign(/* assign */ |
| 147 | DD_DEV_ASSIGN(dd); | 147 | DD_DEV_ASSIGN(dd); |
| @@ -588,7 +588,7 @@ TRACE_EVENT(hfi1_sdma_user_reqinfo, · 587 unmodified lines | |||
| 588 | TP_PROTO(struct hfi1_devdata *dd, u16 ctxt, u8 subctxt, u16 *i), | 588 | TP_PROTO(struct hfi1_devdata *dd, u16 ctxt, u8 subctxt, u16 *i), |
| 589 | TP_ARGS(dd, ctxt, subctxt, i), | 589 | TP_ARGS(dd, ctxt, subctxt, i), |
| 590 | TP_STRUCT__entry( | 590 | TP_STRUCT__entry( |
| 591 | DD_DEV_ENTRY(dd); | 591 | DD_DEV_ENTRY(dd) |
| 592 | __field(u16, ctxt) | 592 | __field(u16, ctxt) |
| 593 | __field(u8, subctxt) | 593 | __field(u8, subctxt) |
| 594 | __field(u8, ver_opcode) | 594 | __field(u8, ver_opcode) |
| @@ -46,7 +46,7 @@ TRACE_EVENT(pblk_chunk_reset, · 45 unmodified lines | |||
| 46 | TP_STRUCT__entry( | 46 | TP_STRUCT__entry( |
| 47 | __string(name, name) | 47 | __string(name, name) |
| 48 | __field(u64, ppa) | 48 | __field(u64, ppa) |
| 49 | __field(int, state); | 49 | __field(int, state) |
| 50 | ), | 50 | ), |
| 51 | 51 | ||
| 52 | TP_fast_assign( | 52 | TP_fast_assign( |
| @@ -72,7 +72,7 @@ TRACE_EVENT(pblk_chunk_state, · 19 unmodified lines | |||
| 72 | TP_STRUCT__entry( | 72 | TP_STRUCT__entry( |
| 73 | __string(name, name) | 73 | __string(name, name) |
| 74 | __field(u64, ppa) | 74 | __field(u64, ppa) |
| 75 | __field(int, state); | 75 | __field(int, state) |
| 76 | ), | 76 | ), |
| 77 | 77 | ||
| 78 | TP_fast_assign( | 78 | TP_fast_assign( |
| @@ -98,7 +98,7 @@ TRACE_EVENT(pblk_line_state, · 19 unmodified lines | |||
| 98 | TP_STRUCT__entry( | 98 | TP_STRUCT__entry( |
| 99 | __string(name, name) | 99 | __string(name, name) |
| 100 | __field(int, line) | 100 | __field(int, line) |
| 101 | __field(int, state); | 101 | __field(int, state) |
| 102 | ), | 102 | ), |
| 103 | 103 | ||
| 104 | TP_fast_assign( | 104 | TP_fast_assign( |
| @@ -121,7 +121,7 @@ TRACE_EVENT(pblk_state, · 16 unmodified lines | |||
| 121 | 121 | ||
| 122 | TP_STRUCT__entry( | 122 | TP_STRUCT__entry( |
| 123 | __string(name, name) | 123 | __string(name, name) |
| 124 | __field(int, state); | 124 | __field(int, state) |
| 125 | ), | 125 | ), |
| 126 | 126 | ||
| 127 | TP_fast_assign( | 127 | TP_fast_assign( |
| @@ -28,7 +28,7 @@ TRACE_EVENT(fjes_hw_issue_request_command, · 27 unmodified lines | |||
| 28 | __field(u8, cs_busy) | 28 | __field(u8, cs_busy) |
| 29 | __field(u8, cs_complete) | 29 | __field(u8, cs_complete) |
| 30 | __field(int, timeout) | 30 | __field(int, timeout) |
| 31 | __field(int, ret); | 31 | __field(int, ret) |
| 32 | ), | 32 | ), |
| 33 | TP_fast_assign( | 33 | TP_fast_assign( |
| 34 | __entry->cr_req = cr->bits.req_code; | 34 | __entry->cr_req = cr->bits.req_code; |
| @@ -239,7 +239,7 @@ TRACE_EVENT(ath10k_wmi_dbglog, · 238 unmodified lines | |||
| 239 | TP_STRUCT__entry( | 239 | TP_STRUCT__entry( |
| 240 | __string(device, dev_name(ar->dev)) | 240 | __string(device, dev_name(ar->dev)) |
| 241 | __string(driver, dev_driver_string(ar->dev)) | 241 | __string(driver, dev_driver_string(ar->dev)) |
| 242 | __field(u8, hw_type); | 242 | __field(u8, hw_type) |
| 243 | __field(size_t, buf_len) | 243 | __field(size_t, buf_len) |
| 244 | __dynamic_array(u8, buf, buf_len) | 244 | __dynamic_array(u8, buf, buf_len) |
| 245 | ), | 245 | ), |
| @@ -269,7 +269,7 @@ TRACE_EVENT(ath10k_htt_pktlog, · 23 unmodified lines | |||
| 269 | TP_STRUCT__entry( | 269 | TP_STRUCT__entry( |
| 270 | __string(device, dev_name(ar->dev)) | 270 | __string(device, dev_name(ar->dev)) |
| 271 | __string(driver, dev_driver_string(ar->dev)) | 271 | __string(driver, dev_driver_string(ar->dev)) |
| 272 | __field(u8, hw_type); | 272 | __field(u8, hw_type) |
| 273 | __field(u16, buf_len) | 273 | __field(u16, buf_len) |
| 274 | __dynamic_array(u8, pktlog, buf_len) | 274 | __dynamic_array(u8, pktlog, buf_len) |
| 275 | ), | 275 | ), |
| @@ -435,7 +435,7 @@ TRACE_EVENT(ath10k_htt_rx_desc, · 159 unmodified lines | |||
| 435 | TP_STRUCT__entry( | 435 | TP_STRUCT__entry( |
| 436 | __string(device, dev_name(ar->dev)) | 436 | __string(device, dev_name(ar->dev)) |
| 437 | __string(driver, dev_driver_string(ar->dev)) | 437 | __string(driver, dev_driver_string(ar->dev)) |
| 438 | __field(u8, hw_type); | 438 | __field(u8, hw_type) |
| 439 | __field(u16, len) | 439 | __field(u16, len) |
| 440 | __dynamic_array(u8, rxdesc, len) | 440 | __dynamic_array(u8, rxdesc, len) |
| 441 | ), | 441 | ), |
| @@ -329,7 +329,7 @@ TRACE_EVENT(xchk_btree_op_error, · 328 unmodified lines | |||
| 329 | __field(int, level) | 329 | __field(int, level) |
| 330 | __field(xfs_agnumber_t, agno) | 330 | __field(xfs_agnumber_t, agno) |
| 331 | __field(xfs_agblock_t, bno) | 331 | __field(xfs_agblock_t, bno) |
| 332 | __field(int, ptr); | 332 | __field(int, ptr) |
| 333 | __field(int, error) | 333 | __field(int, error) |
| 334 | __field(void *, ret_ip) | 334 | __field(void *, ret_ip) |
| 335 | ), | 335 | ), |
| @@ -414,7 +414,7 @@ TRACE_EVENT(xchk_btree_error, · 78 unmodified lines | |||
| 414 | __field(int, level) | 414 | __field(int, level) |
| 415 | __field(xfs_agnumber_t, agno) | 415 | __field(xfs_agnumber_t, agno) |
| 416 | __field(xfs_agblock_t, bno) | 416 | __field(xfs_agblock_t, bno) |
| 417 | __field(int, ptr); | 417 | __field(int, ptr) |
| 418 | __field(void *, ret_ip) | 418 | __field(void *, ret_ip) |
| 419 | ), | 419 | ), |
| 420 | TP_fast_assign( | 420 | TP_fast_assign( |
| @@ -452,7 +452,7 @@ TRACE_EVENT(xchk_ifork_btree_error, · 31 unmodified lines | |||
| 452 | __field(int, level) | 452 | __field(int, level) |
| 453 | __field(xfs_agnumber_t, agno) | 453 | __field(xfs_agnumber_t, agno) |
| 454 | __field(xfs_agblock_t, bno) | 454 | __field(xfs_agblock_t, bno) |
| 455 | __field(int, ptr); | 455 | __field(int, ptr) |
| 456 | __field(void *, ret_ip) | 456 | __field(void *, ret_ip) |
| 457 | ), | 457 | ), |
| 458 | TP_fast_assign( | 458 | TP_fast_assign( |
| @@ -218,8 +218,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class, · 217 unmodified lines | |||
| 218 | TP_STRUCT__entry( | 218 | TP_STRUCT__entry( |
| 219 | __field(dev_t, dev) | 219 | __field(dev_t, dev) |
| 220 | __field(xfs_ino_t, ino) | 220 | __field(xfs_ino_t, ino) |
| 221 | __field(void *, leaf); | 221 | __field(void *, leaf) |
| 222 | __field(int, pos); | 222 | __field(int, pos) |
| 223 | __field(xfs_fileoff_t, startoff) | 223 | __field(xfs_fileoff_t, startoff) |
| 224 | __field(xfs_fsblock_t, startblock) | 224 | __field(xfs_fsblock_t, startblock) |
| 225 | __field(xfs_filblks_t, blockcount) | 225 | __field(xfs_filblks_t, blockcount) |
| @@ -849,13 +849,9 @@ extern int module_sysfs_initialized; · 848 unmodified lines | |||
| 849 | #define __MODULE_STRING(x) __stringify(x) | 849 | #define __MODULE_STRING(x) __stringify(x) |
| 850 | 850 | ||
| 851 | #ifdef CONFIG_STRICT_MODULE_RWX | 851 | #ifdef CONFIG_STRICT_MODULE_RWX |
| 852 | extern void set_all_modules_text_rw(void); | ||
| 853 | extern void set_all_modules_text_ro(void); | ||
| 854 | extern void module_enable_ro(const struct module *mod, bool after_init); | 852 | extern void module_enable_ro(const struct module *mod, bool after_init); |
| 855 | extern void module_disable_ro(const struct module *mod); | 853 | extern void module_disable_ro(const struct module *mod); |
| 856 | #else | 854 | #else |
| 857 | static inline void set_all_modules_text_rw(void) { } | ||
| 858 | static inline void set_all_modules_text_ro(void) { } | ||
| 859 | static inline void module_enable_ro(const struct module *mod, bool after_init) { } | 855 | static inline void module_enable_ro(const struct module *mod, bool after_init) { } |
| 860 | static inline void module_disable_ro(const struct module *mod) { } | 856 | static inline void module_disable_ro(const struct module *mod) { } |
| 861 | #endif | 857 | #endif |
| @@ -192,6 +192,22 @@ enum trace_reg { · 191 unmodified lines | |||
| 192 | 192 | ||
| 193 | struct trace_event_call; | 193 | struct trace_event_call; |
| 194 | 194 | ||
| 195 | #define TRACE_FUNCTION_TYPE ((const char *)~0UL) | ||
| 196 | |||
| 197 | struct trace_event_fields { | ||
| 198 | const char *type; | ||
| 199 | union { | ||
| 200 | struct { | ||
| 201 | const char *name; | ||
| 202 | const int size; | ||
| 203 | const int align; | ||
| 204 | const int is_signed; | ||
| 205 | const int filter_type; | ||
| 206 | }; | ||
| 207 | int (*define_fields)(struct trace_event_call *); | ||
| 208 | }; | ||
| 209 | }; | ||
| 210 | |||
| 195 | struct trace_event_class { | 211 | struct trace_event_class { |
| 196 | const char *system; | 212 | const char *system; |
| 197 | void *probe; | 213 | void *probe; |
| @@ -200,7 +216,7 @@ struct trace_event_class { · 2 unmodified lines | |||
| 200 | #endif | 216 | #endif |
| 201 | int (*reg)(struct trace_event_call *event, | 217 | int (*reg)(struct trace_event_call *event, |
| 202 | enum trace_reg type, void *data); | 218 | enum trace_reg type, void *data); |
| 203 | int (*define_fields)(struct trace_event_call *); | 219 | struct trace_event_fields *fields_array; |
| 204 | struct list_head *(*get_fields)(struct trace_event_call *); | 220 | struct list_head *(*get_fields)(struct trace_event_call *); |
| 205 | struct list_head fields; | 221 | struct list_head fields; |
| 206 | int (*raw_init)(struct trace_event_call *); | 222 | int (*raw_init)(struct trace_event_call *); |
| @@ -85,7 +85,7 @@ TRACE_EVENT(file_check_and_advance_wb_err, · 84 unmodified lines | |||
| 85 | TP_ARGS(file, old), | 85 | TP_ARGS(file, old), |
| 86 | 86 | ||
| 87 | TP_STRUCT__entry( | 87 | TP_STRUCT__entry( |
| 88 | __field(struct file *, file); | 88 | __field(struct file *, file) |
| 89 | __field(unsigned long, i_ino) | 89 | __field(unsigned long, i_ino) |
| 90 | __field(dev_t, s_dev) | 90 | __field(dev_t, s_dev) |
| 91 | __field(errseq_t, old) | 91 | __field(errseq_t, old) |
| @@ -400,22 +400,16 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \ · 399 unmodified lines | |||
| 400 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) | 400 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) |
| 401 | 401 | ||
| 402 | #undef __field_ext | 402 | #undef __field_ext |
| 403 | #define __field_ext(type, item, filter_type) \ | 403 | #define __field_ext(_type, _item, _filter_type) { \ |
| 404 | ret = trace_define_field(event_call, #type, #item, \ | 404 | .type = #_type, .name = #_item, \ |
| 405 | offsetof(typeof(field), item), \ | 405 | .size = sizeof(_type), .align = __alignof__(_type), \ |
| 406 | sizeof(field.item), \ | 406 | .is_signed = is_signed_type(_type), .filter_type = _filter_type }, |
| 407 | is_signed_type(type), filter_type); \ | ||
| 408 | if (ret) \ | ||
| 409 | return ret; | ||
| 410 | 407 | ||
| 411 | #undef __field_struct_ext | 408 | #undef __field_struct_ext |
| 412 | #define __field_struct_ext(type, item, filter_type) \ | 409 | #define __field_struct_ext(_type, _item, _filter_type) { \ |
| 413 | ret = trace_define_field(event_call, #type, #item, \ | 410 | .type = #_type, .name = #_item, \ |
| 414 | offsetof(typeof(field), item), \ | 411 | .size = sizeof(_type), .align = __alignof__(_type), \ |
| 415 | sizeof(field.item), \ | 412 | 0, .filter_type = _filter_type }, |
| 416 | 0, filter_type); \ | ||
| 417 | if (ret) \ | ||
| 418 | return ret; | ||
| 419 | 413 | ||
| 420 | #undef __field | 414 | #undef __field |
| 421 | #define __field(type, item) __field_ext(type, item, FILTER_OTHER) | 415 | #define __field(type, item) __field_ext(type, item, FILTER_OTHER) |
| @@ -424,25 +418,16 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \ · 2 unmodified lines | |||
| 424 | #define __field_struct(type, item) __field_struct_ext(type, item, FILTER_OTHER) | 418 | #define __field_struct(type, item) __field_struct_ext(type, item, FILTER_OTHER) |
| 425 | 419 | ||
| 426 | #undef __array | 420 | #undef __array |
| 427 | #define __array(type, item, len) \ | 421 | #define __array(_type, _item, _len) { \ |
| 428 | do { \ | 422 | .type = #_type"["__stringify(_len)"]", .name = #_item, \ |
| 429 | char *type_str = #type"["__stringify(len)"]"; \ | 423 | .size = sizeof(_type[_len]), .align = __alignof__(_type), \ |
| 430 | BUILD_BUG_ON(len > MAX_FILTER_STR_VAL); \ | 424 | .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER }, |
| 431 | BUILD_BUG_ON(len <= 0); \ | ||
| 432 | ret = trace_define_field(event_call, type_str, #item, \ | ||
| 433 | offsetof(typeof(field), item), \ | ||
| 434 | sizeof(field.item), \ | ||
| 435 | is_signed_type(type), FILTER_OTHER); \ | ||
| 436 | if (ret) \ | ||
| 437 | return ret; \ | ||
| 438 | } while (0); | ||
| 439 | 425 | ||
| 440 | #undef __dynamic_array | 426 | #undef __dynamic_array |
| 441 | #define __dynamic_array(type, item, len) \ | 427 | #define __dynamic_array(_type, _item, _len) { \ |
| 442 | ret = trace_define_field(event_call, "__data_loc " #type "[]", #item, \ | 428 | .type = "__data_loc " #_type "[]", .name = #_item, \ |
| 443 | offsetof(typeof(field), __data_loc_##item), \ | 429 | .size = 4, .align = 4, \ |
| 444 | sizeof(field.__data_loc_##item), \ | 430 | .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER }, |
| 445 | is_signed_type(type), FILTER_OTHER); | ||
| 446 | 431 | ||
| 447 | #undef __string | 432 | #undef __string |
| 448 | #define __string(item, src) __dynamic_array(char, item, -1) | 433 | #define __string(item, src) __dynamic_array(char, item, -1) |
| @@ -452,16 +437,9 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \ · 3 unmodified lines | |||
| 452 | 437 | ||
| 453 | #undef DECLARE_EVENT_CLASS | 438 | #undef DECLARE_EVENT_CLASS |
| 454 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \ | 439 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \ |
| 455 | static int notrace __init \ | 440 | static struct trace_event_fields trace_event_fields_##call[] = { \ |
| 456 | trace_event_define_fields_##call(struct trace_event_call *event_call) \ | 441 | tstruct \ |
| 457 | { \ | 442 | {} }; |
| 458 | struct trace_event_raw_##call field; \ | ||
| 459 | int ret; \ | ||
| 460 | \ | ||
| 461 | tstruct; \ | ||
| 462 | \ | ||
| 463 | return ret; \ | ||
| 464 | } | ||
| 465 | 443 | ||
| 466 | #undef DEFINE_EVENT | 444 | #undef DEFINE_EVENT |
| 467 | #define DEFINE_EVENT(template, name, proto, args) | 445 | #define DEFINE_EVENT(template, name, proto, args) |
| @@ -619,7 +597,7 @@ static inline notrace int trace_event_get_offsets_##call( \ · 151 unmodified lines | |||
| 619 | * | 597 | * |
| 620 | * static struct trace_event_class __used event_class_<template> = { | 598 | * static struct trace_event_class __used event_class_<template> = { |
| 621 | * .system = "<system>", | 599 | * .system = "<system>", |
| 622 | * .define_fields = trace_event_define_fields_<call>, | 600 | * .fields_array = trace_event_fields_<call>, |
| 623 | * .fields = LIST_HEAD_INIT(event_class_##call.fields), | 601 | * .fields = LIST_HEAD_INIT(event_class_##call.fields), |
| 624 | * .raw_init = trace_event_raw_init, | 602 | * .raw_init = trace_event_raw_init, |
| 625 | * .probe = trace_event_raw_event_##call, | 603 | * .probe = trace_event_raw_event_##call, |
| @@ -768,7 +746,7 @@ _TRACE_PERF_PROTO(call, PARAMS(proto)); \ · 142 unmodified lines | |||
| 768 | static char print_fmt_##call[] = print; \ | 746 | static char print_fmt_##call[] = print; \ |
| 769 | static struct trace_event_class __used __refdata event_class_##call = { \ | 747 | static struct trace_event_class __used __refdata event_class_##call = { \ |
| 770 | .system = TRACE_SYSTEM_STRING, \ | 748 | .system = TRACE_SYSTEM_STRING, \ |
| 771 | .define_fields = trace_event_define_fields_##call, \ | 749 | .fields_array = trace_event_fields_##call, \ |
| 772 | .fields = LIST_HEAD_INIT(event_class_##call.fields),\ | 750 | .fields = LIST_HEAD_INIT(event_class_##call.fields),\ |
| 773 | .raw_init = trace_event_raw_init, \ | 751 | .raw_init = trace_event_raw_init, \ |
| 774 | .probe = trace_event_raw_event_##call, \ | 752 | .probe = trace_event_raw_event_##call, \ |
| @@ -510,6 +510,8 @@ static void do_unoptimize_kprobes(void) · 509 unmodified lines | |||
| 510 | arch_unoptimize_kprobes(&unoptimizing_list, &freeing_list); | 510 | arch_unoptimize_kprobes(&unoptimizing_list, &freeing_list); |
| 511 | /* Loop free_list for disarming */ | 511 | /* Loop free_list for disarming */ |
| 512 | list_for_each_entry_safe(op, tmp, &freeing_list, list) { | 512 | list_for_each_entry_safe(op, tmp, &freeing_list, list) { |
| 513 | /* Switching from detour code to origin */ | ||
| 514 | op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; | ||
| 513 | /* Disarm probes if marked disabled */ | 515 | /* Disarm probes if marked disabled */ |
| 514 | if (kprobe_disabled(&op->kp)) | 516 | if (kprobe_disabled(&op->kp)) |
| 515 | arch_disarm_kprobe(&op->kp); | 517 | arch_disarm_kprobe(&op->kp); |
| @@ -610,6 +612,18 @@ void wait_for_kprobe_optimizer(void) · 94 unmodified lines | |||
| 610 | mutex_unlock(&kprobe_mutex); | 612 | mutex_unlock(&kprobe_mutex); |
| 611 | } | 613 | } |
| 612 | 614 | ||
| 615 | static bool optprobe_queued_unopt(struct optimized_kprobe *op) | ||
| 616 | { | ||
| 617 | struct optimized_kprobe *_op; | ||
| 618 | |||
| 619 | list_for_each_entry(_op, &unoptimizing_list, list) { | ||
| 620 | if (op == _op) | ||
| 621 | return true; | ||
| 622 | } | ||
| 623 | |||
| 624 | return false; | ||
| 625 | } | ||
| 626 | |||
| 613 | /* Optimize kprobe if p is ready to be optimized */ | 627 | /* Optimize kprobe if p is ready to be optimized */ |
| 614 | static void optimize_kprobe(struct kprobe *p) | 628 | static void optimize_kprobe(struct kprobe *p) |
| 615 | { | 629 | { |
| @@ -631,17 +645,21 @@ static void optimize_kprobe(struct kprobe *p) · 15 unmodified lines | |||
| 631 | return; | 645 | return; |
| 632 | 646 | ||
| 633 | /* Check if it is already optimized. */ | 647 | /* Check if it is already optimized. */ |
| 634 | if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) | 648 | if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) { |
| 649 | if (optprobe_queued_unopt(op)) { | ||
| 650 | /* This is under unoptimizing. Just dequeue the probe */ | ||
| 651 | list_del_init(&op->list); | ||
| 652 | } | ||
| 635 | return; | 653 | return; |
| 654 | } | ||
| 636 | op->kp.flags |= KPROBE_FLAG_OPTIMIZED; | 655 | op->kp.flags |= KPROBE_FLAG_OPTIMIZED; |
| 637 | 656 | ||
| 638 | if (!list_empty(&op->list)) | 657 | /* On unoptimizing/optimizing_list, op must have OPTIMIZED flag */ |
| 639 | /* This is under unoptimizing. Just dequeue the probe */ | 658 | if (WARN_ON_ONCE(!list_empty(&op->list))) |
| 640 | list_del_init(&op->list); | 659 | return; |
| 641 | else { | 660 | |
| 642 | list_add(&op->list, &optimizing_list); | 661 | list_add(&op->list, &optimizing_list); |
| 643 | kick_kprobe_optimizer(); | 662 | kick_kprobe_optimizer(); |
| 644 | } | ||
| 645 | } | 663 | } |
| 646 | 664 | ||
| 647 | /* Short cut to direct unoptimizing */ | 665 | /* Short cut to direct unoptimizing */ |
| @@ -649,6 +667,7 @@ static void force_unoptimize_kprobe(struct optimized_kprobe *op) · 1 unmodified line | |||
| 649 | { | 667 | { |
| 650 | lockdep_assert_cpus_held(); | 668 | lockdep_assert_cpus_held(); |
| 651 | arch_unoptimize_kprobe(op); | 669 | arch_unoptimize_kprobe(op); |
| 670 | op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; | ||
| 652 | if (kprobe_disabled(&op->kp)) | 671 | if (kprobe_disabled(&op->kp)) |
| 653 | arch_disarm_kprobe(&op->kp); | 672 | arch_disarm_kprobe(&op->kp); |
| 654 | } | 673 | } |
| @@ -662,31 +681,33 @@ static void unoptimize_kprobe(struct kprobe *p, bool force) · 7 unmodified lines | |||
| 662 | return; /* This is not an optprobe nor optimized */ | 681 | return; /* This is not an optprobe nor optimized */ |
| 663 | 682 | ||
| 664 | op = container_of(p, struct optimized_kprobe, kp); | 683 | op = container_of(p, struct optimized_kprobe, kp); |
| 665 | if (!kprobe_optimized(p)) { | 684 | if (!kprobe_optimized(p)) |
| 666 | /* Unoptimized or unoptimizing case */ | ||
| 667 | if (force && !list_empty(&op->list)) { | ||
| 668 | /* | ||
| 669 | * Only if this is unoptimizing kprobe and forced, | ||
| 670 | * forcibly unoptimize it. (No need to unoptimize | ||
| 671 | * unoptimized kprobe again :) | ||
| 672 | */ | ||
| 673 | list_del_init(&op->list); | ||
| 674 | force_unoptimize_kprobe(op); | ||
| 675 | } | ||
| 676 | return; | 685 | return; |
| 677 | } | ||
| 678 | 686 | ||
| 679 | op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; | ||
| 680 | if (!list_empty(&op->list)) { | 687 | if (!list_empty(&op->list)) { |
| 681 | /* Dequeue from the optimization queue */ | 688 | if (optprobe_queued_unopt(op)) { |
| 682 | list_del_init(&op->list); | 689 | /* Queued in unoptimizing queue */ |
| 690 | if (force) { | ||
| 691 | /* | ||
| 692 | * Forcibly unoptimize the kprobe here, and queue it | ||
| 693 | * in the freeing list for release afterwards. | ||
| 694 | */ | ||
| 695 | force_unoptimize_kprobe(op); | ||
| 696 | list_move(&op->list, &freeing_list); | ||
| 697 | } | ||
| 698 | } else { | ||
| 699 | /* Dequeue from the optimizing queue */ | ||
| 700 | list_del_init(&op->list); | ||
| 701 | op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; | ||
| 702 | } | ||
| 683 | return; | 703 | return; |
| 684 | } | 704 | } |
| 705 | |||
| 685 | /* Optimized kprobe case */ | 706 | /* Optimized kprobe case */ |
| 686 | if (force) | 707 | if (force) { |
| 687 | /* Forcibly update the code: this is a special case */ | 708 | /* Forcibly update the code: this is a special case */ |
| 688 | force_unoptimize_kprobe(op); | 709 | force_unoptimize_kprobe(op); |
| 689 | else { | 710 | } else { |
| 690 | list_add(&op->list, &unoptimizing_list); | 711 | list_add(&op->list, &unoptimizing_list); |
| 691 | kick_kprobe_optimizer(); | 712 | kick_kprobe_optimizer(); |
| 692 | } | 713 | } |
| @@ -2031,49 +2031,6 @@ static void module_enable_nx(const struct module *mod) · 2030 unmodified lines | |||
| 2031 | frob_writable_data(&mod->init_layout, set_memory_nx); | 2031 | frob_writable_data(&mod->init_layout, set_memory_nx); |
| 2032 | } | 2032 | } |
| 2033 | 2033 | ||
| 2034 | /* Iterate through all modules and set each module's text as RW */ | ||
| 2035 | void set_all_modules_text_rw(void) | ||
| 2036 | { | ||
| 2037 | struct module *mod; | ||
| 2038 | |||
| 2039 | if (!rodata_enabled) | ||
| 2040 | return; | ||
| 2041 | |||
| 2042 | mutex_lock(&module_mutex); | ||
| 2043 | list_for_each_entry_rcu(mod, &modules, list) { | ||
| 2044 | if (mod->state == MODULE_STATE_UNFORMED) | ||
| 2045 | continue; | ||
| 2046 | |||
| 2047 | frob_text(&mod->core_layout, set_memory_rw); | ||
| 2048 | frob_text(&mod->init_layout, set_memory_rw); | ||
| 2049 | } | ||
| 2050 | mutex_unlock(&module_mutex); | ||
| 2051 | } | ||
| 2052 | |||
| 2053 | /* Iterate through all modules and set each module's text as RO */ | ||
| 2054 | void set_all_modules_text_ro(void) | ||
| 2055 | { | ||
| 2056 | struct module *mod; | ||
| 2057 | |||
| 2058 | if (!rodata_enabled) | ||
| 2059 | return; | ||
| 2060 | |||
| 2061 | mutex_lock(&module_mutex); | ||
| 2062 | list_for_each_entry_rcu(mod, &modules, list) { | ||
| 2063 | /* | ||
| 2064 | * Ignore going modules since it's possible that ro | ||
| 2065 | * protection has already been disabled, otherwise we'll | ||
| 2066 | * run into protection faults at module deallocation. | ||
| 2067 | */ | ||
| 2068 | if (mod->state == MODULE_STATE_UNFORMED || | ||
| 2069 | mod->state == MODULE_STATE_GOING) | ||
| 2070 | continue; | ||
| 2071 | |||
| 2072 | frob_text(&mod->core_layout, set_memory_ro); | ||
| 2073 | frob_text(&mod->init_layout, set_memory_ro); | ||
| 2074 | } | ||
| 2075 | mutex_unlock(&module_mutex); | ||
| 2076 | } | ||
| 2077 | #else /* !CONFIG_STRICT_MODULE_RWX */ | 2034 | #else /* !CONFIG_STRICT_MODULE_RWX */ |
| 2078 | static void module_enable_nx(const struct module *mod) { } | 2035 | static void module_enable_nx(const struct module *mod) { } |
| 2079 | #endif /* CONFIG_STRICT_MODULE_RWX */ | 2036 | #endif /* CONFIG_STRICT_MODULE_RWX */ |
| @@ -52,6 +52,9 @@ enum trace_type { · 51 unmodified lines | |||
| 52 | #undef __field | 52 | #undef __field |
| 53 | #define __field(type, item) type item; | 53 | #define __field(type, item) type item; |
| 54 | 54 | ||
| 55 | #undef __field_fn | ||
| 56 | #define __field_fn(type, item) type item; | ||
| 57 | |||
| 55 | #undef __field_struct | 58 | #undef __field_struct |
| 56 | #define __field_struct(type, item) __field(type, item) | 59 | #define __field_struct(type, item) __field(type, item) |
| 57 | 60 | ||
| @@ -71,26 +74,22 @@ enum trace_type { · 13 unmodified lines | |||
| 71 | #define F_STRUCT(args...) args | 74 | #define F_STRUCT(args...) args |
| 72 | 75 | ||
| 73 | #undef FTRACE_ENTRY | 76 | #undef FTRACE_ENTRY |
| 74 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ | 77 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ |
| 75 | struct struct_name { \ | 78 | struct struct_name { \ |
| 76 | struct trace_entry ent; \ | 79 | struct trace_entry ent; \ |
| 77 | tstruct \ | 80 | tstruct \ |
| 78 | } | 81 | } |
| 79 | 82 | ||
| 80 | #undef FTRACE_ENTRY_DUP | 83 | #undef FTRACE_ENTRY_DUP |
| 81 | #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk, filter) | 84 | #define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk) |
| 82 | 85 | ||
| 83 | #undef FTRACE_ENTRY_REG | 86 | #undef FTRACE_ENTRY_REG |
| 84 | #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ | 87 | #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, regfn) \ |
| 85 | filter, regfn) \ | 88 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
| 86 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | ||
| 87 | filter) | ||
| 88 | 89 | ||
| 89 | #undef FTRACE_ENTRY_PACKED | 90 | #undef FTRACE_ENTRY_PACKED |
| 90 | #define FTRACE_ENTRY_PACKED(name, struct_name, id, tstruct, print, \ | 91 | #define FTRACE_ENTRY_PACKED(name, struct_name, id, tstruct, print) \ |
| 91 | filter) \ | 92 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) __packed |
| 92 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | ||
| 93 | filter) __packed | ||
| 94 | 93 | ||
| 95 | #include "trace_entries.h" | 94 | #include "trace_entries.h" |
| 96 | 95 | ||
| @@ -1917,17 +1916,15 @@ extern void tracing_log_err(struct trace_array *tr, · 1820 unmodified lines | |||
| 1917 | #define internal_trace_puts(str) __trace_puts(_THIS_IP_, str, strlen(str)) | 1916 | #define internal_trace_puts(str) __trace_puts(_THIS_IP_, str, strlen(str)) |
| 1918 | 1917 | ||
| 1919 | #undef FTRACE_ENTRY | 1918 | #undef FTRACE_ENTRY |
| 1920 | #define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \ | 1919 | #define FTRACE_ENTRY(call, struct_name, id, tstruct, print) \ |
| 1921 | extern struct trace_event_call \ | 1920 | extern struct trace_event_call \ |
| 1922 | __aligned(4) event_##call; | 1921 | __aligned(4) event_##call; |
| 1923 | #undef FTRACE_ENTRY_DUP | 1922 | #undef FTRACE_ENTRY_DUP |
| 1924 | #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print, filter) \ | 1923 | #define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print) \ |
| 1925 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | 1924 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
| 1926 | filter) | ||
| 1927 | #undef FTRACE_ENTRY_PACKED | 1925 | #undef FTRACE_ENTRY_PACKED |
| 1928 | #define FTRACE_ENTRY_PACKED(call, struct_name, id, tstruct, print, filter) \ | 1926 | #define FTRACE_ENTRY_PACKED(call, struct_name, id, tstruct, print) \ |
| 1929 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | 1927 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
| 1930 | filter) | ||
| 1931 | 1928 | ||
| 1932 | #include "trace_entries.h" | 1929 | #include "trace_entries.h" |
| 1933 | 1930 | ||
| @@ -61,15 +61,13 @@ FTRACE_ENTRY_REG(function, ftrace_entry, · 60 unmodified lines | |||
| 61 | TRACE_FN, | 61 | TRACE_FN, |
| 62 | 62 | ||
| 63 | F_STRUCT( | 63 | F_STRUCT( |
| 64 | __field( unsigned long, ip ) | 64 | __field_fn( unsigned long, ip ) |
| 65 | __field( unsigned long, parent_ip ) | 65 | __field_fn( unsigned long, parent_ip ) |
| 66 | ), | 66 | ), |
| 67 | 67 | ||
| 68 | F_printk(" %ps <-- %ps", | 68 | F_printk(" %ps <-- %ps", |
| 69 | (void *)__entry->ip, (void *)__entry->parent_ip), | 69 | (void *)__entry->ip, (void *)__entry->parent_ip), |
| 70 | 70 | ||
| 71 | FILTER_TRACE_FN, | ||
| 72 | |||
| 73 | perf_ftrace_event_register | 71 | perf_ftrace_event_register |
| 74 | ); | 72 | ); |
| 75 | 73 | ||
| @@ -84,9 +82,7 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry, · 8 unmodified lines | |||
| 84 | __field_desc( int, graph_ent, depth ) | 82 | __field_desc( int, graph_ent, depth ) |
| 85 | ), | 83 | ), |
| 86 | 84 | ||
| 87 | F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth), | 85 | F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth) |
| 88 | |||
| 89 | FILTER_OTHER | ||
| 90 | ); | 86 | ); |
| 91 | 87 | ||
| 92 | /* Function return entry */ | 88 | /* Function return entry */ |
| @@ -97,18 +93,16 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry, · 4 unmodified lines | |||
| 97 | F_STRUCT( | 93 | F_STRUCT( |
| 98 | __field_struct( struct ftrace_graph_ret, ret ) | 94 | __field_struct( struct ftrace_graph_ret, ret ) |
| 99 | __field_desc( unsigned long, ret, func ) | 95 | __field_desc( unsigned long, ret, func ) |
| 96 | __field_desc( unsigned long, ret, overrun ) | ||
| 100 | __field_desc( unsigned long long, ret, calltime) | 97 | __field_desc( unsigned long long, ret, calltime) |
| 101 | __field_desc( unsigned long long, ret, rettime ) | 98 | __field_desc( unsigned long long, ret, rettime ) |
| 102 | __field_desc( unsigned long, ret, overrun ) | ||
| 103 | __field_desc( int, ret, depth ) | 99 | __field_desc( int, ret, depth ) |
| 104 | ), | 100 | ), |
| 105 | 101 | ||
| 106 | F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d", | 102 | F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d", |
| 107 | (void *)__entry->func, __entry->depth, | 103 | (void *)__entry->func, __entry->depth, |
| 108 | __entry->calltime, __entry->rettime, | 104 | __entry->calltime, __entry->rettime, |
| 109 | __entry->depth), | 105 | __entry->depth) |
| 110 | |||
| 111 | FILTER_OTHER | ||
| 112 | ); | 106 | ); |
| 113 | 107 | ||
| 114 | /* | 108 | /* |
| @@ -137,9 +131,7 @@ FTRACE_ENTRY(context_switch, ctx_switch_entry, · 22 unmodified lines | |||
| 137 | F_printk("%u:%u:%u ==> %u:%u:%u [%03u]", | 131 | F_printk("%u:%u:%u ==> %u:%u:%u [%03u]", |
| 138 | __entry->prev_pid, __entry->prev_prio, __entry->prev_state, | 132 | __entry->prev_pid, __entry->prev_prio, __entry->prev_state, |
| 139 | __entry->next_pid, __entry->next_prio, __entry->next_state, | 133 | __entry->next_pid, __entry->next_prio, __entry->next_state, |
| 140 | __entry->next_cpu), | 134 | __entry->next_cpu) |
| 141 | |||
| 142 | FILTER_OTHER | ||
| 143 | ); | 135 | ); |
| 144 | 136 | ||
| 145 | /* | 137 | /* |
| @@ -157,9 +149,7 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry, · 11 unmodified lines | |||
| 157 | F_printk("%u:%u:%u ==+ %u:%u:%u [%03u]", | 149 | F_printk("%u:%u:%u ==+ %u:%u:%u [%03u]", |
| 158 | __entry->prev_pid, __entry->prev_prio, __entry->prev_state, | 150 | __entry->prev_pid, __entry->prev_prio, __entry->prev_state, |
| 159 | __entry->next_pid, __entry->next_prio, __entry->next_state, | 151 | __entry->next_pid, __entry->next_prio, __entry->next_state, |
| 160 | __entry->next_cpu), | 152 | __entry->next_cpu) |
| 161 | |||
| 162 | FILTER_OTHER | ||
| 163 | ); | 153 | ); |
| 164 | 154 | ||
| 165 | /* | 155 | /* |
| @@ -183,9 +173,7 @@ FTRACE_ENTRY(kernel_stack, stack_entry, · 17 unmodified lines | |||
| 183 | (void *)__entry->caller[0], (void *)__entry->caller[1], | 173 | (void *)__entry->caller[0], (void *)__entry->caller[1], |
| 184 | (void *)__entry->caller[2], (void *)__entry->caller[3], | 174 | (void *)__entry->caller[2], (void *)__entry->caller[3], |
| 185 | (void *)__entry->caller[4], (void *)__entry->caller[5], | 175 | (void *)__entry->caller[4], (void *)__entry->caller[5], |
| 186 | (void *)__entry->caller[6], (void *)__entry->caller[7]), | 176 | (void *)__entry->caller[6], (void *)__entry->caller[7]) |
| 187 | |||
| 188 | FILTER_OTHER | ||
| 189 | ); | 177 | ); |
| 190 | 178 | ||
| 191 | FTRACE_ENTRY(user_stack, userstack_entry, | 179 | FTRACE_ENTRY(user_stack, userstack_entry, |
| @@ -203,9 +191,7 @@ FTRACE_ENTRY(user_stack, userstack_entry, · 11 unmodified lines | |||
| 203 | (void *)__entry->caller[0], (void *)__entry->caller[1], | 191 | (void *)__entry->caller[0], (void *)__entry->caller[1], |
| 204 | (void *)__entry->caller[2], (void *)__entry->caller[3], | 192 | (void *)__entry->caller[2], (void *)__entry->caller[3], |
| 205 | (void *)__entry->caller[4], (void *)__entry->caller[5], | 193 | (void *)__entry->caller[4], (void *)__entry->caller[5], |
| 206 | (void *)__entry->caller[6], (void *)__entry->caller[7]), | 194 | (void *)__entry->caller[6], (void *)__entry->caller[7]) |
| 207 | |||
| 208 | FILTER_OTHER | ||
| 209 | ); | 195 | ); |
| 210 | 196 | ||
| 211 | /* | 197 | /* |
| @@ -222,9 +208,7 @@ FTRACE_ENTRY(bprint, bprint_entry, · 10 unmodified lines | |||
| 222 | ), | 208 | ), |
| 223 | 209 | ||
| 224 | F_printk("%ps: %s", | 210 | F_printk("%ps: %s", |
| 225 | (void *)__entry->ip, __entry->fmt), | 211 | (void *)__entry->ip, __entry->fmt) |
| 226 | |||
| 227 | FILTER_OTHER | ||
| 228 | ); | 212 | ); |
| 229 | 213 | ||
| 230 | FTRACE_ENTRY_REG(print, print_entry, | 214 | FTRACE_ENTRY_REG(print, print_entry, |
| @@ -239,8 +223,6 @@ FTRACE_ENTRY_REG(print, print_entry, · 8 unmodified lines | |||
| 239 | F_printk("%ps: %s", | 223 | F_printk("%ps: %s", |
| 240 | (void *)__entry->ip, __entry->buf), | 224 | (void *)__entry->ip, __entry->buf), |
| 241 | 225 | ||
| 242 | FILTER_OTHER, | ||
| 243 | |||
| 244 | ftrace_event_register | 226 | ftrace_event_register |
| 245 | ); | 227 | ); |
| 246 | 228 | ||
| @@ -254,9 +236,7 @@ FTRACE_ENTRY(raw_data, raw_data_entry, · 7 unmodified lines | |||
| 254 | ), | 236 | ), |
| 255 | 237 | ||
| 256 | F_printk("id:%04x %08x", | 238 | F_printk("id:%04x %08x", |
| 257 | __entry->id, (int)__entry->buf[0]), | 239 | __entry->id, (int)__entry->buf[0]) |
| 258 | |||
| 259 | FILTER_OTHER | ||
| 260 | ); | 240 | ); |
| 261 | 241 | ||
| 262 | FTRACE_ENTRY(bputs, bputs_entry, | 242 | FTRACE_ENTRY(bputs, bputs_entry, |
| @@ -269,9 +249,7 @@ FTRACE_ENTRY(bputs, bputs_entry, · 6 unmodified lines | |||
| 269 | ), | 249 | ), |
| 270 | 250 | ||
| 271 | F_printk("%ps: %s", | 251 | F_printk("%ps: %s", |
| 272 | (void *)__entry->ip, __entry->str), | 252 | (void *)__entry->ip, __entry->str) |
| 273 | |||
| 274 | FILTER_OTHER | ||
| 275 | ); | 253 | ); |
| 276 | 254 | ||
| 277 | FTRACE_ENTRY(mmiotrace_rw, trace_mmiotrace_rw, | 255 | FTRACE_ENTRY(mmiotrace_rw, trace_mmiotrace_rw, |
| @@ -283,16 +261,14 @@ FTRACE_ENTRY(mmiotrace_rw, trace_mmiotrace_rw, · 5 unmodified lines | |||
| 283 | __field_desc( resource_size_t, rw, phys ) | 261 | __field_desc( resource_size_t, rw, phys ) |
| 284 | __field_desc( unsigned long, rw, value ) | 262 | __field_desc( unsigned long, rw, value ) |
| 285 | __field_desc( unsigned long, rw, pc ) | 263 | __field_desc( unsigned long, rw, pc ) |
| 286 | __field_desc( int, rw, map_id ) | 264 | __field_desc( int, rw, map_id ) |
| 287 | __field_desc( unsigned char, rw, opcode ) | 265 | __field_desc( unsigned char, rw, opcode ) |
| 288 | __field_desc( unsigned char, rw, width ) | 266 | __field_desc( unsigned char, rw, width ) |
| 289 | ), | 267 | ), |
| 290 | 268 | ||
| 291 | F_printk("%lx %lx %lx %d %x %x", | 269 | F_printk("%lx %lx %lx %d %x %x", |
| 292 | (unsigned long)__entry->phys, __entry->value, __entry->pc, | 270 | (unsigned long)__entry->phys, __entry->value, __entry->pc, |
| 293 | __entry->map_id, __entry->opcode, __entry->width), | 271 | __entry->map_id, __entry->opcode, __entry->width) |
| 294 | |||
| 295 | FILTER_OTHER | ||
| 296 | ); | 272 | ); |
| 297 | 273 | ||
| 298 | FTRACE_ENTRY(mmiotrace_map, trace_mmiotrace_map, | 274 | FTRACE_ENTRY(mmiotrace_map, trace_mmiotrace_map, |
| @@ -304,15 +280,13 @@ FTRACE_ENTRY(mmiotrace_map, trace_mmiotrace_map, · 5 unmodified lines | |||
| 304 | __field_desc( resource_size_t, map, phys ) | 280 | __field_desc( resource_size_t, map, phys ) |
| 305 | __field_desc( unsigned long, map, virt ) | 281 | __field_desc( unsigned long, map, virt ) |
| 306 | __field_desc( unsigned long, map, len ) | 282 | __field_desc( unsigned long, map, len ) |
| 307 | __field_desc( int, map, map_id ) | 283 | __field_desc( int, map, map_id ) |
| 308 | __field_desc( unsigned char, map, opcode ) | 284 | __field_desc( unsigned char, map, opcode ) |
| 309 | ), | 285 | ), |
| 310 | 286 | ||
| 311 | F_printk("%lx %lx %lx %d %x", | 287 | F_printk("%lx %lx %lx %d %x", |
| 312 | (unsigned long)__entry->phys, __entry->virt, __entry->len, | 288 | (unsigned long)__entry->phys, __entry->virt, __entry->len, |
| 313 | __entry->map_id, __entry->opcode), | 289 | __entry->map_id, __entry->opcode) |
| 314 | |||
| 315 | FILTER_OTHER | ||
| 316 | ); | 290 | ); |
| 317 | 291 | ||
| 318 | 292 | ||
| @@ -334,9 +308,7 @@ FTRACE_ENTRY(branch, trace_branch, · 15 unmodified lines | |||
| 334 | F_printk("%u:%s:%s (%u)%s", | 308 | F_printk("%u:%s:%s (%u)%s", |
| 335 | __entry->line, | 309 | __entry->line, |
| 336 | __entry->func, __entry->file, __entry->correct, | 310 | __entry->func, __entry->file, __entry->correct, |
| 337 | __entry->constant ? " CONSTANT" : ""), | 311 | __entry->constant ? " CONSTANT" : "") |
| 338 | |||
| 339 | FILTER_OTHER | ||
| 340 | ); | 312 | ); |
| 341 | 313 | ||
| 342 | 314 | ||
| @@ -362,7 +334,5 @@ FTRACE_ENTRY(hwlat, hwlat_entry, · 19 unmodified lines | |||
| 362 | __entry->duration, | 334 | __entry->duration, |
| 363 | __entry->outer_duration, | 335 | __entry->outer_duration, |
| 364 | __entry->nmi_total_ts, | 336 | __entry->nmi_total_ts, |
| 365 | __entry->nmi_count), | 337 | __entry->nmi_count) |
| 366 | |||
| 367 | FILTER_OTHER | ||
| 368 | ); | 338 | ); |
| @@ -24,6 +24,7 @@ · 23 unmodified lines | |||
| 24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
| 25 | 25 | ||
| 26 | #include <trace/events/sched.h> | 26 | #include <trace/events/sched.h> |
| 27 | #include <trace/syscall.h> | ||
| 27 | 28 | ||
| 28 | #include <asm/setup.h> | 29 | #include <asm/setup.h> |
| 29 | 30 | ||
| @@ -2017,7 +2018,24 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) · 1987 unmodified lines | |||
| 2017 | */ | 2018 | */ |
| 2018 | head = trace_get_fields(call); | 2019 | head = trace_get_fields(call); |
| 2019 | if (list_empty(head)) { | 2020 | if (list_empty(head)) { |
| 2020 | ret = call->class->define_fields(call); | 2021 | struct trace_event_fields *field = call->class->fields_array; |
| 2022 | unsigned int offset = sizeof(struct trace_entry); | ||
| 2023 | |||
| 2024 | for (; field->type; field++) { | ||
| 2025 | if (field->type == TRACE_FUNCTION_TYPE) { | ||
| 2026 | ret = field->define_fields(call); | ||
| 2027 | break; | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | offset = ALIGN(offset, field->align); | ||
| 2031 | ret = trace_define_field(call, field->type, field->name, | ||
| 2032 | offset, field->size, | ||
| 2033 | field->is_signed, field->filter_type); | ||
| 2034 | if (ret) | ||
| 2035 | break; | ||
| 2036 | |||
| 2037 | offset += field->size; | ||
| 2038 | } | ||
| 2021 | if (ret < 0) { | 2039 | if (ret < 0) { |
| 2022 | pr_warn("Could not initialize trace point events/%s\n", | 2040 | pr_warn("Could not initialize trace point events/%s\n", |
| 2023 | name); | 2041 | name); |
| @@ -1155,6 +1155,12 @@ static struct synth_event *find_synth_event(const char *name) · 1154 unmodified lines | |||
| 1155 | return NULL; | 1155 | return NULL; |
| 1156 | } | 1156 | } |
| 1157 | 1157 | ||
| 1158 | static struct trace_event_fields synth_event_fields_array[] = { | ||
| 1159 | { .type = TRACE_FUNCTION_TYPE, | ||
| 1160 | .define_fields = synth_event_define_fields }, | ||
| 1161 | {} | ||
| 1162 | }; | ||
| 1163 | |||
| 1158 | static int register_synth_event(struct synth_event *event) | 1164 | static int register_synth_event(struct synth_event *event) |
| 1159 | { | 1165 | { |
| 1160 | struct trace_event_call *call = &event->call; | 1166 | struct trace_event_call *call = &event->call; |
| @@ -1176,7 +1182,7 @@ static int register_synth_event(struct synth_event *event) · 15 unmodified lines | |||
| 1176 | 1182 | ||
| 1177 | INIT_LIST_HEAD(&call->class->fields); | 1183 | INIT_LIST_HEAD(&call->class->fields); |
| 1178 | call->event.funcs = &synth_event_funcs; | 1184 | call->event.funcs = &synth_event_funcs; |
| 1179 | call->class->define_fields = synth_event_define_fields; | 1185 | call->class->fields_array = synth_event_fields_array; |
| 1180 | 1186 | ||
| 1181 | ret = register_trace_event(&call->event); | 1187 | ret = register_trace_event(&call->event); |
| 1182 | if (!ret) { | 1188 | if (!ret) { |
| @@ -29,10 +29,8 @@ static int ftrace_event_register(struct trace_event_call *call, · 28 unmodified lines | |||
| 29 | * function and thus become accesible via perf. | 29 | * function and thus become accesible via perf. |
| 30 | */ | 30 | */ |
| 31 | #undef FTRACE_ENTRY_REG | 31 | #undef FTRACE_ENTRY_REG |
| 32 | #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ | 32 | #define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, regfn) \ |
| 33 | filter, regfn) \ | 33 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
| 34 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | ||
| 35 | filter) | ||
| 36 | 34 | ||
| 37 | /* not needed for this file */ | 35 | /* not needed for this file */ |
| 38 | #undef __field_struct | 36 | #undef __field_struct |
| @@ -41,6 +39,9 @@ static int ftrace_event_register(struct trace_event_call *call, · 2 unmodified lines | |||
| 41 | #undef __field | 39 | #undef __field |
| 42 | #define __field(type, item) type item; | 40 | #define __field(type, item) type item; |
| 43 | 41 | ||
| 42 | #undef __field_fn | ||
| 43 | #define __field_fn(type, item) type item; | ||
| 44 | |||
| 44 | #undef __field_desc | 45 | #undef __field_desc |
| 45 | #define __field_desc(type, container, item) type item; | 46 | #define __field_desc(type, container, item) type item; |
| 46 | 47 | ||
| @@ -60,7 +61,7 @@ static int ftrace_event_register(struct trace_event_call *call, · 13 unmodified lines | |||
| 60 | #define F_printk(fmt, args...) fmt, args | 61 | #define F_printk(fmt, args...) fmt, args |
| 61 | 62 | ||
| 62 | #undef FTRACE_ENTRY | 63 | #undef FTRACE_ENTRY |
| 63 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ | 64 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ |
| 64 | struct ____ftrace_##name { \ | 65 | struct ____ftrace_##name { \ |
| 65 | tstruct \ | 66 | tstruct \ |
| 66 | }; \ | 67 | }; \ |
| @@ -73,76 +74,46 @@ static void __always_unused ____ftrace_check_##name(void) \ · 6 unmodified lines | |||
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | #undef FTRACE_ENTRY_DUP | 76 | #undef FTRACE_ENTRY_DUP |
| 76 | #define FTRACE_ENTRY_DUP(name, struct_name, id, tstruct, print, filter) \ | 77 | #define FTRACE_ENTRY_DUP(name, struct_name, id, tstruct, print) \ |
| 77 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ | 78 | FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
| 78 | filter) | ||
| 79 | 79 | ||
| 80 | #include "trace_entries.h" | 80 | #include "trace_entries.h" |
| 81 | 81 | ||
| 82 | #undef __field_ext | ||
| 83 | #define __field_ext(_type, _item, _filter_type) { \ | ||
| 84 | .type = #_type, .name = #_item, \ | ||
| 85 | .size = sizeof(_type), .align = __alignof__(_type), \ | ||
| 86 | is_signed_type(_type), .filter_type = _filter_type }, | ||
| 87 | |||
| 82 | #undef __field | 88 | #undef __field |
| 83 | #define __field(type, item) \ | 89 | #define __field(_type, _item) __field_ext(_type, _item, FILTER_OTHER) |
| 84 | ret = trace_define_field(event_call, #type, #item, \ | 90 | |
| 85 | offsetof(typeof(field), item), \ | 91 | #undef __field_fn |
| 86 | sizeof(field.item), \ | 92 | #define __field_fn(_type, _item) __field_ext(_type, _item, FILTER_TRACE_FN) |
| 87 | is_signed_type(type), filter_type); \ | ||
| 88 | if (ret) \ | ||
| 89 | return ret; | ||
| 90 | 93 | ||
| 91 | #undef __field_desc | 94 | #undef __field_desc |
| 92 | #define __field_desc(type, container, item) \ | 95 | #define __field_desc(_type, _container, _item) __field_ext(_type, _item, FILTER_OTHER) |
| 93 | ret = trace_define_field(event_call, #type, #item, \ | ||
| 94 | offsetof(typeof(field), \ | ||
| 95 | container.item), \ | ||
| 96 | sizeof(field.container.item), \ | ||
| 97 | is_signed_type(type), filter_type); \ | ||
| 98 | if (ret) \ | ||
| 99 | return ret; | ||
| 100 | 96 | ||
| 101 | #undef __array | 97 | #undef __array |
| 102 | #define __array(type, item, len) \ | 98 | #define __array(_type, _item, _len) { \ |
| 103 | do { \ | 99 | .type = #_type"["__stringify(_len)"]", .name = #_item, \ |
| 104 | char *type_str = #type"["__stringify(len)"]"; \ | 100 | .size = sizeof(_type[_len]), .align = __alignof__(_type), \ |
| 105 | BUILD_BUG_ON(len > MAX_FILTER_STR_VAL); \ | 101 | is_signed_type(_type), .filter_type = FILTER_OTHER }, |
| 106 | ret = trace_define_field(event_call, type_str, #item, \ | ||
| 107 | offsetof(typeof(field), item), \ | ||
| 108 | sizeof(field.item), \ | ||
| 109 | is_signed_type(type), filter_type); \ | ||
| 110 | if (ret) \ | ||
| 111 | return ret; \ | ||
| 112 | } while (0); | ||
| 113 | 102 | ||
| 114 | #undef __array_desc | 103 | #undef __array_desc |
| 115 | #define __array_desc(type, container, item, len) \ | 104 | #define __array_desc(_type, _container, _item, _len) __array(_type, _item, _len) |
| 116 | BUILD_BUG_ON(len > MAX_FILTER_STR_VAL); \ | ||
| 117 | ret = trace_define_field(event_call, #type "[" #len "]", #item, \ | ||
| 118 | offsetof(typeof(field), \ | ||
| 119 | container.item), \ | ||
| 120 | sizeof(field.container.item), \ | ||
| 121 | is_signed_type(type), filter_type); \ | ||
| 122 | if (ret) \ | ||
| 123 | return ret; | ||
| 124 | 105 | ||
| 125 | #undef __dynamic_array | 106 | #undef __dynamic_array |
| 126 | #define __dynamic_array(type, item) \ | 107 | #define __dynamic_array(_type, _item) { \ |
| 127 | ret = trace_define_field(event_call, #type "[]", #item, \ | 108 | .type = #_type "[]", .name = #_item, \ |
| 128 | offsetof(typeof(field), item), \ | 109 | .size = 0, .align = __alignof__(_type), \ |
| 129 | 0, is_signed_type(type), filter_type);\ | 110 | is_signed_type(_type), .filter_type = FILTER_OTHER }, |
| 130 | if (ret) \ | ||
| 131 | return ret; | ||
| 132 | 111 | ||
| 133 | #undef FTRACE_ENTRY | 112 | #undef FTRACE_ENTRY |
| 134 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ | 113 | #define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ |
| 135 | static int __init \ | 114 | static struct trace_event_fields ftrace_event_fields_##name[] = { \ |
| 136 | ftrace_define_fields_##name(struct trace_event_call *event_call) \ | 115 | tstruct \ |
| 137 | { \ | 116 | {} }; |
| 138 | struct struct_name field; \ | ||
| 139 | int ret; \ | ||
| 140 | int filter_type = filter; \ | ||
| 141 | \ | ||
| 142 | tstruct; \ | ||
| 143 | \ | ||
| 144 | return ret; \ | ||
| 145 | } | ||
| 146 | 117 | ||
| 147 | #include "trace_entries.h" | 118 | #include "trace_entries.h" |
| 148 | 119 | ||
| @@ -152,6 +123,9 @@ ftrace_define_fields_##name(struct trace_event_call *event_call) \ · 3 unmodified lines | |||
| 152 | #undef __field | 123 | #undef __field |
| 153 | #define __field(type, item) | 124 | #define __field(type, item) |
| 154 | 125 | ||
| 126 | #undef __field_fn | ||
| 127 | #define __field_fn(type, item) | ||
| 128 | |||
| 155 | #undef __field_desc | 129 | #undef __field_desc |
| 156 | #define __field_desc(type, container, item) | 130 | #define __field_desc(type, container, item) |
| 157 | 131 | ||
| @@ -168,12 +142,10 @@ ftrace_define_fields_##name(struct trace_event_call *event_call) \ · 10 unmodified lines | |||
| 168 | #define F_printk(fmt, args...) __stringify(fmt) ", " __stringify(args) | 142 | #define F_printk(fmt, args...) __stringify(fmt) ", " __stringify(args) |
| 169 | 143 | ||
| 170 | #undef FTRACE_ENTRY_REG | 144 | #undef FTRACE_ENTRY_REG |
| 171 | #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\ | 145 | #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, regfn) \ |
| 172 | regfn) \ | ||
| 173 | \ | ||
| 174 | static struct trace_event_class __refdata event_class_ftrace_##call = { \ | 146 | static struct trace_event_class __refdata event_class_ftrace_##call = { \ |
| 175 | .system = __stringify(TRACE_SYSTEM), \ | 147 | .system = __stringify(TRACE_SYSTEM), \ |
| 176 | .define_fields = ftrace_define_fields_##call, \ | 148 | .fields_array = ftrace_event_fields_##call, \ |
| 177 | .fields = LIST_HEAD_INIT(event_class_ftrace_##call.fields),\ | 149 | .fields = LIST_HEAD_INIT(event_class_ftrace_##call.fields),\ |
| 178 | .reg = regfn, \ | 150 | .reg = regfn, \ |
| 179 | }; \ | 151 | }; \ |
| @@ -191,9 +163,9 @@ static struct trace_event_call __used \ · 11 unmodified lines | |||
| 191 | __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call; | 163 | __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call; |
| 192 | 164 | ||
| 193 | #undef FTRACE_ENTRY | 165 | #undef FTRACE_ENTRY |
| 194 | #define FTRACE_ENTRY(call, struct_name, etype, tstruct, print, filter) \ | 166 | #define FTRACE_ENTRY(call, struct_name, etype, tstruct, print) \ |
| 195 | FTRACE_ENTRY_REG(call, struct_name, etype, \ | 167 | FTRACE_ENTRY_REG(call, struct_name, etype, \ |
| 196 | PARAMS(tstruct), PARAMS(print), filter, NULL) | 168 | PARAMS(tstruct), PARAMS(print), NULL) |
| 197 | 169 | ||
| 198 | bool ftrace_event_is_function(struct trace_event_call *call) | 170 | bool ftrace_event_is_function(struct trace_event_call *call) |
| 199 | { | 171 | { |
| @@ -1555,16 +1555,28 @@ static struct trace_event_functions kprobe_funcs = { · 1554 unmodified lines | |||
| 1555 | .trace = print_kprobe_event | 1555 | .trace = print_kprobe_event |
| 1556 | }; | 1556 | }; |
| 1557 | 1557 | ||
| 1558 | static struct trace_event_fields kretprobe_fields_array[] = { | ||
| 1559 | { .type = TRACE_FUNCTION_TYPE, | ||
| 1560 | .define_fields = kretprobe_event_define_fields }, | ||
| 1561 | {} | ||
| 1562 | }; | ||
| 1563 | |||
| 1564 | static struct trace_event_fields kprobe_fields_array[] = { | ||
| 1565 | { .type = TRACE_FUNCTION_TYPE, | ||
| 1566 | .define_fields = kprobe_event_define_fields }, | ||
| 1567 | {} | ||
| 1568 | }; | ||
| 1569 | |||
| 1558 | static inline void init_trace_event_call(struct trace_kprobe *tk) | 1570 | static inline void init_trace_event_call(struct trace_kprobe *tk) |
| 1559 | { | 1571 | { |
| 1560 | struct trace_event_call *call = trace_probe_event_call(&tk->tp); | 1572 | struct trace_event_call *call = trace_probe_event_call(&tk->tp); |
| 1561 | 1573 | ||
| 1562 | if (trace_kprobe_is_return(tk)) { | 1574 | if (trace_kprobe_is_return(tk)) { |
| 1563 | call->event.funcs = &kretprobe_funcs; | 1575 | call->event.funcs = &kretprobe_funcs; |
| 1564 | call->class->define_fields = kretprobe_event_define_fields; | 1576 | call->class->fields_array = kretprobe_fields_array; |
| 1565 | } else { | 1577 | } else { |
| 1566 | call->event.funcs = &kprobe_funcs; | 1578 | call->event.funcs = &kprobe_funcs; |
| 1567 | call->class->define_fields = kprobe_event_define_fields; | 1579 | call->class->fields_array = kprobe_fields_array; |
| 1568 | } | 1580 | } |
| 1569 | 1581 | ||
| 1570 | call->flags = TRACE_EVENT_FL_KPROBE; | 1582 | call->flags = TRACE_EVENT_FL_KPROBE; |
| @@ -203,11 +203,10 @@ print_syscall_exit(struct trace_iterator *iter, int flags, · 202 unmodified lines | |||
| 203 | 203 | ||
| 204 | extern char *__bad_type_size(void); | 204 | extern char *__bad_type_size(void); |
| 205 | 205 | ||
| 206 | #define SYSCALL_FIELD(type, field, name) \ | 206 | #define SYSCALL_FIELD(_type, _name) { \ |
| 207 | sizeof(type) != sizeof(trace.field) ? \ | 207 | .type = #_type, .name = #_name, \ |
| 208 | __bad_type_size() : \ | 208 | .size = sizeof(_type), .align = __alignof__(_type), \ |
| 209 | #type, #name, offsetof(typeof(trace), field), \ | 209 | .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER } |
| 210 | sizeof(trace.field), is_signed_type(type) | ||
| 211 | 210 | ||
| 212 | static int __init | 211 | static int __init |
| 213 | __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) | 212 | __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) |
| @@ -274,42 +273,23 @@ static int __init syscall_enter_define_fields(struct trace_event_call *call) · 60 unmodified lines | |||
| 274 | { | 273 | { |
| 275 | struct syscall_trace_enter trace; | 274 | struct syscall_trace_enter trace; |
| 276 | struct syscall_metadata *meta = call->data; | 275 | struct syscall_metadata *meta = call->data; |
| 277 | int ret; | ||
| 278 | int i; | ||
| 279 | int offset = offsetof(typeof(trace), args); | 276 | int offset = offsetof(typeof(trace), args); |
| 280 | 277 | int ret = 0; | |
| 281 | ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr), | 278 | int i; |
| 282 | FILTER_OTHER); | ||
| 283 | if (ret) | ||
| 284 | return ret; | ||
| 285 | 279 | ||
| 286 | for (i = 0; i < meta->nb_args; i++) { | 280 | for (i = 0; i < meta->nb_args; i++) { |
| 287 | ret = trace_define_field(call, meta->types[i], | 281 | ret = trace_define_field(call, meta->types[i], |
| 288 | meta->args[i], offset, | 282 | meta->args[i], offset, |
| 289 | sizeof(unsigned long), 0, | 283 | sizeof(unsigned long), 0, |
| 290 | FILTER_OTHER); | 284 | FILTER_OTHER); |
| 285 | if (ret) | ||
| 286 | break; | ||
| 291 | offset += sizeof(unsigned long); | 287 | offset += sizeof(unsigned long); |
| 292 | } | 288 | } |
| 293 | 289 | ||
| 294 | return ret; | 290 | return ret; |
| 295 | } | 291 | } |
| 296 | 292 | ||
| 297 | static int __init syscall_exit_define_fields(struct trace_event_call *call) | ||
| 298 | { | ||
| 299 | struct syscall_trace_exit trace; | ||
| 300 | int ret; | ||
| 301 | |||
| 302 | ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr), | ||
| 303 | FILTER_OTHER); | ||
| 304 | if (ret) | ||
| 305 | return ret; | ||
| 306 | |||
| 307 | ret = trace_define_field(call, SYSCALL_FIELD(long, ret, ret), | ||
| 308 | FILTER_OTHER); | ||
| 309 | |||
| 310 | return ret; | ||
| 311 | } | ||
| 312 | |||
| 313 | static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) | 293 | static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) |
| 314 | { | 294 | { |
| 315 | struct trace_array *tr = data; | 295 | struct trace_array *tr = data; |
| @@ -507,6 +487,13 @@ static int __init init_syscall_trace(struct trace_event_call *call) · 191 unmodified lines | |||
| 507 | return id; | 487 | return id; |
| 508 | } | 488 | } |
| 509 | 489 | ||
| 490 | static struct trace_event_fields __refdata syscall_enter_fields_array[] = { | ||
| 491 | SYSCALL_FIELD(int, __syscall_nr), | ||
| 492 | { .type = TRACE_FUNCTION_TYPE, | ||
| 493 | .define_fields = syscall_enter_define_fields }, | ||
| 494 | {} | ||
| 495 | }; | ||
| 496 | |||
| 510 | struct trace_event_functions enter_syscall_print_funcs = { | 497 | struct trace_event_functions enter_syscall_print_funcs = { |
| 511 | .trace = print_syscall_enter, | 498 | .trace = print_syscall_enter, |
| 512 | }; | 499 | }; |
| @@ -518,7 +505,7 @@ struct trace_event_functions exit_syscall_print_funcs = { · 5 unmodified lines | |||
| 518 | struct trace_event_class __refdata event_class_syscall_enter = { | 505 | struct trace_event_class __refdata event_class_syscall_enter = { |
| 519 | .system = "syscalls", | 506 | .system = "syscalls", |
| 520 | .reg = syscall_enter_register, | 507 | .reg = syscall_enter_register, |
| 521 | .define_fields = syscall_enter_define_fields, | 508 | .fields_array = syscall_enter_fields_array, |
| 522 | .get_fields = syscall_get_enter_fields, | 509 | .get_fields = syscall_get_enter_fields, |
| 523 | .raw_init = init_syscall_trace, | 510 | .raw_init = init_syscall_trace, |
| 524 | }; | 511 | }; |
| @@ -526,7 +513,11 @@ struct trace_event_class __refdata event_class_syscall_enter = { · 1 unmodified line | |||
| 526 | struct trace_event_class __refdata event_class_syscall_exit = { | 513 | struct trace_event_class __refdata event_class_syscall_exit = { |
| 527 | .system = "syscalls", | 514 | .system = "syscalls", |
| 528 | .reg = syscall_exit_register, | 515 | .reg = syscall_exit_register, |
| 529 | .define_fields = syscall_exit_define_fields, | 516 | .fields_array = (struct trace_event_fields[]){ |
| 517 | SYSCALL_FIELD(int, __syscall_nr), | ||
| 518 | SYSCALL_FIELD(long, ret), | ||
| 519 | {} | ||
| 520 | }, | ||
| 530 | .fields = LIST_HEAD_INIT(event_class_syscall_exit.fields), | 521 | .fields = LIST_HEAD_INIT(event_class_syscall_exit.fields), |
| 531 | .raw_init = init_syscall_trace, | 522 | .raw_init = init_syscall_trace, |
| 532 | }; | 523 | }; |
| @@ -1528,12 +1528,17 @@ static struct trace_event_functions uprobe_funcs = { · 1527 unmodified lines | |||
| 1528 | .trace = print_uprobe_event | 1528 | .trace = print_uprobe_event |
| 1529 | }; | 1529 | }; |
| 1530 | 1530 | ||
| 1531 | static struct trace_event_fields uprobe_fields_array[] = { | ||
| 1532 | { .type = TRACE_FUNCTION_TYPE, | ||
| 1533 | .define_fields = uprobe_event_define_fields }, | ||
| 1534 | {} | ||
| 1535 | }; | ||
| 1536 | |||
| 1531 | static inline void init_trace_event_call(struct trace_uprobe *tu) | 1537 | static inline void init_trace_event_call(struct trace_uprobe *tu) |
| 1532 | { | 1538 | { |
| 1533 | struct trace_event_call *call = trace_probe_event_call(&tu->tp); | 1539 | struct trace_event_call *call = trace_probe_event_call(&tu->tp); |
| 1534 | |||
| 1535 | call->event.funcs = &uprobe_funcs; | 1540 | call->event.funcs = &uprobe_funcs; |
| 1536 | call->class->define_fields = uprobe_event_define_fields; | 1541 | call->class->fields_array = uprobe_fields_array; |
| 1537 | 1542 | ||
| 1538 | call->flags = TRACE_EVENT_FL_UPROBE | TRACE_EVENT_FL_CAP_ANY; | 1543 | call->flags = TRACE_EVENT_FL_UPROBE | TRACE_EVENT_FL_CAP_ANY; |
| 1539 | call->class->reg = trace_uprobe_register; | 1544 | call->class->reg = trace_uprobe_register; |
| @@ -408,20 +408,20 @@ TRACE_EVENT(drv_bss_info_changed, · 407 unmodified lines | |||
| 408 | __field(u32, basic_rates) | 408 | __field(u32, basic_rates) |
| 409 | __array(int, mcast_rate, NUM_NL80211_BANDS) | 409 | __array(int, mcast_rate, NUM_NL80211_BANDS) |
| 410 | __field(u16, ht_operation_mode) | 410 | __field(u16, ht_operation_mode) |
| 411 | __field(s32, cqm_rssi_thold); | 411 | __field(s32, cqm_rssi_thold) |
| 412 | __field(s32, cqm_rssi_hyst); | 412 | __field(s32, cqm_rssi_hyst) |
| 413 | __field(u32, channel_width); | 413 | __field(u32, channel_width) |
| 414 | __field(u32, channel_cfreq1); | 414 | __field(u32, channel_cfreq1) |
| 415 | __dynamic_array(u32, arp_addr_list, | 415 | __dynamic_array(u32, arp_addr_list, |
| 416 | info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? | 416 | info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? |
| 417 | IEEE80211_BSS_ARP_ADDR_LIST_LEN : | 417 | IEEE80211_BSS_ARP_ADDR_LIST_LEN : |
| 418 | info->arp_addr_cnt); | 418 | info->arp_addr_cnt) |
| 419 | __field(int, arp_addr_cnt); | 419 | __field(int, arp_addr_cnt) |
| 420 | __field(bool, qos); | 420 | __field(bool, qos) |
| 421 | __field(bool, idle); | 421 | __field(bool, idle) |
| 422 | __field(bool, ps); | 422 | __field(bool, ps) |
| 423 | __dynamic_array(u8, ssid, info->ssid_len); | 423 | __dynamic_array(u8, ssid, info->ssid_len) |
| 424 | __field(bool, hidden_ssid); | 424 | __field(bool, hidden_ssid) |
| 425 | __field(int, txpower) | 425 | __field(int, txpower) |
| 426 | __field(u8, p2p_oppps_ctwindow) | 426 | __field(u8, p2p_oppps_ctwindow) |
| 427 | ), | 427 | ), |
| @@ -1672,8 +1672,8 @@ TRACE_EVENT(drv_start_ap, · 1244 unmodified lines | |||
| 1672 | VIF_ENTRY | 1672 | VIF_ENTRY |
| 1673 | __field(u8, dtimper) | 1673 | __field(u8, dtimper) |
| 1674 | __field(u16, bcnint) | 1674 | __field(u16, bcnint) |
| 1675 | __dynamic_array(u8, ssid, info->ssid_len); | 1675 | __dynamic_array(u8, ssid, info->ssid_len) |
| 1676 | __field(bool, hidden_ssid); | 1676 | __field(bool, hidden_ssid) |
| 1677 | ), | 1677 | ), |
| 1678 | 1678 | ||
| 1679 | TP_fast_assign( | 1679 | TP_fast_assign( |
| @@ -1739,7 +1739,7 @@ TRACE_EVENT(drv_join_ibss, · 59 unmodified lines | |||
| 1739 | VIF_ENTRY | 1739 | VIF_ENTRY |
| 1740 | __field(u8, dtimper) | 1740 | __field(u8, dtimper) |
| 1741 | __field(u16, bcnint) | 1741 | __field(u16, bcnint) |
| 1742 | __dynamic_array(u8, ssid, info->ssid_len); | 1742 | __dynamic_array(u8, ssid, info->ssid_len) |
| 1743 | ), | 1743 | ), |
| 1744 | 1744 | ||
| 1745 | TP_fast_assign( | 1745 | TP_fast_assign( |
| @@ -2014,7 +2014,7 @@ TRACE_EVENT(rdev_start_nan, · 2013 unmodified lines | |||
| 2014 | WIPHY_ENTRY | 2014 | WIPHY_ENTRY |
| 2015 | WDEV_ENTRY | 2015 | WDEV_ENTRY |
| 2016 | __field(u8, master_pref) | 2016 | __field(u8, master_pref) |
| 2017 | __field(u8, bands); | 2017 | __field(u8, bands) |
| 2018 | ), | 2018 | ), |
| 2019 | TP_fast_assign( | 2019 | TP_fast_assign( |
| 2020 | WIPHY_ASSIGN; | 2020 | WIPHY_ASSIGN; |
| @@ -2036,8 +2036,8 @@ TRACE_EVENT(rdev_nan_change_conf, · 15 unmodified lines | |||
| 2036 | WIPHY_ENTRY | 2036 | WIPHY_ENTRY |
| 2037 | WDEV_ENTRY | 2037 | WDEV_ENTRY |
| 2038 | __field(u8, master_pref) | 2038 | __field(u8, master_pref) |
| 2039 | __field(u8, bands); | 2039 | __field(u8, bands) |
| 2040 | __field(u32, changes); | 2040 | __field(u32, changes) |
| 2041 | ), | 2041 | ), |
| 2042 | TP_fast_assign( | 2042 | TP_fast_assign( |
| 2043 | WIPHY_ASSIGN; | 2043 | WIPHY_ASSIGN; |
| @@ -197,7 +197,7 @@ $(OUTPUT)test-libcrypto.bin: · 196 unmodified lines | |||
| 197 | $(BUILD) -lcrypto | 197 | $(BUILD) -lcrypto |
| 198 | 198 | ||
| 199 | $(OUTPUT)test-gtk2.bin: | 199 | $(OUTPUT)test-gtk2.bin: |
| 200 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) | 200 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -Wno-deprecated-declarations |
| 201 | 201 | ||
| 202 | $(OUTPUT)test-gtk2-infobar.bin: | 202 | $(OUTPUT)test-gtk2-infobar.bin: |
| 203 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) | 203 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) |
| @@ -1,9 +1,15 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include "clang/Basic/Version.h" | ||
| 3 | #if CLANG_VERSION_MAJOR < 8 | ||
| 2 | #include "clang/Basic/VirtualFileSystem.h" | 4 | #include "clang/Basic/VirtualFileSystem.h" |
| 5 | #endif | ||
| 3 | #include "clang/Driver/Driver.h" | 6 | #include "clang/Driver/Driver.h" |
| 4 | #include "clang/Frontend/TextDiagnosticPrinter.h" | 7 | #include "clang/Frontend/TextDiagnosticPrinter.h" |
| 5 | #include "llvm/ADT/IntrusiveRefCntPtr.h" | 8 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| 6 | #include "llvm/Support/ManagedStatic.h" | 9 | #include "llvm/Support/ManagedStatic.h" |
| 10 | #if CLANG_VERSION_MAJOR >= 8 | ||
| 11 | #include "llvm/Support/VirtualFileSystem.h" | ||
| 12 | #endif | ||
| 7 | #include "llvm/Support/raw_ostream.h" | 13 | #include "llvm/Support/raw_ostream.h" |
| 8 | 14 | ||
| 9 | using namespace clang; | 15 | using namespace clang; |
| @@ -15,6 +15,8 @@ void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, · 14 unmodified lines | |||
| 15 | const unsigned long *bitmap2, int bits); | 15 | const unsigned long *bitmap2, int bits); |
| 16 | int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, | 16 | int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, |
| 17 | const unsigned long *bitmap2, unsigned int bits); | 17 | const unsigned long *bitmap2, unsigned int bits); |
| 18 | int __bitmap_equal(const unsigned long *bitmap1, | ||
| 19 | const unsigned long *bitmap2, unsigned int bits); | ||
| 18 | void bitmap_clear(unsigned long *map, unsigned int start, int len); | 20 | void bitmap_clear(unsigned long *map, unsigned int start, int len); |
| 19 | 21 | ||
| 20 | #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) | 22 | #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) |
| @@ -123,6 +125,15 @@ static inline unsigned long *bitmap_alloc(int nbits) · 102 unmodified lines | |||
| 123 | return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); | 125 | return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); |
| 124 | } | 126 | } |
| 125 | 127 | ||
| 128 | /* | ||
| 129 | * bitmap_free - Free bitmap | ||
| 130 | * @bitmap: pointer to bitmap | ||
| 131 | */ | ||
| 132 | static inline void bitmap_free(unsigned long *bitmap) | ||
| 133 | { | ||
| 134 | free(bitmap); | ||
| 135 | } | ||
| 136 | |||
| 126 | /* | 137 | /* |
| 127 | * bitmap_scnprintf - print bitmap list into buffer | 138 | * bitmap_scnprintf - print bitmap list into buffer |
| 128 | * @bitmap: bitmap | 139 | * @bitmap: bitmap |
| @@ -148,4 +159,23 @@ static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, · 19 unmodified lines | |||
| 148 | return __bitmap_and(dst, src1, src2, nbits); | 159 | return __bitmap_and(dst, src1, src2, nbits); |
| 149 | } | 160 | } |
| 150 | 161 | ||
| 162 | #ifdef __LITTLE_ENDIAN | ||
| 163 | #define BITMAP_MEM_ALIGNMENT 8 | ||
| 164 | #else | ||
| 165 | #define BITMAP_MEM_ALIGNMENT (8 * sizeof(unsigned long)) | ||
| 166 | #endif | ||
| 167 | #define BITMAP_MEM_MASK (BITMAP_MEM_ALIGNMENT - 1) | ||
| 168 | #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) | ||
| 169 | |||
| 170 | static inline int bitmap_equal(const unsigned long *src1, | ||
| 171 | const unsigned long *src2, unsigned int nbits) | ||
| 172 | { | ||
| 173 | if (small_const_nbits(nbits)) | ||
| 174 | return !((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); | ||
| 175 | if (__builtin_constant_p(nbits & BITMAP_MEM_MASK) && | ||
| 176 | IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT)) | ||
| 177 | return !memcmp(src1, src2, nbits / 8); | ||
| 178 | return __bitmap_equal(src1, src2, nbits); | ||
| 179 | } | ||
| 180 | |||
| 151 | #endif /* _PERF_BITOPS_H */ | 181 | #endif /* _PERF_BITOPS_H */ |
| @@ -17,7 +17,15 @@ int strtobool(const char *s, bool *res); · 16 unmodified lines | |||
| 17 | * However uClibc headers also define __GLIBC__ hence the hack below | 17 | * However uClibc headers also define __GLIBC__ hence the hack below |
| 18 | */ | 18 | */ |
| 19 | #if defined(__GLIBC__) && !defined(__UCLIBC__) | 19 | #if defined(__GLIBC__) && !defined(__UCLIBC__) |
| 20 | // pragma diagnostic was introduced in gcc 4.6 | ||
| 21 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||
| 22 | #pragma GCC diagnostic push | ||
| 23 | #pragma GCC diagnostic ignored "-Wredundant-decls" | ||
| 24 | #endif | ||
| 20 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 25 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
| 26 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||
| 27 | #pragma GCC diagnostic pop | ||
| 28 | #endif | ||
| 21 | #endif | 29 | #endif |
| 22 | 30 | ||
| 23 | char *str_error_r(int errnum, char *buf, size_t buflen); | 31 | char *str_error_r(int errnum, char *buf, size_t buflen); |
| @@ -210,6 +210,7 @@ static bool fs__env_override(struct fs *fs) · 209 unmodified lines | |||
| 210 | size_t name_len = strlen(fs->name); | 210 | size_t name_len = strlen(fs->name); |
| 211 | /* name + "_PATH" + '\0' */ | 211 | /* name + "_PATH" + '\0' */ |
| 212 | char upper_name[name_len + 5 + 1]; | 212 | char upper_name[name_len + 5 + 1]; |
| 213 | |||
| 213 | memcpy(upper_name, fs->name, name_len); | 214 | memcpy(upper_name, fs->name, name_len); |
| 214 | mem_toupper(upper_name, name_len); | 215 | mem_toupper(upper_name, name_len); |
| 215 | strcpy(&upper_name[name_len], "_PATH"); | 216 | strcpy(&upper_name[name_len], "_PATH"); |
| @@ -219,7 +220,8 @@ static bool fs__env_override(struct fs *fs) · 3 unmodified lines | |||
| 219 | return false; | 220 | return false; |
| 220 | 221 | ||
| 221 | fs->found = true; | 222 | fs->found = true; |
| 222 | strncpy(fs->path, override_path, sizeof(fs->path)); | 223 | strncpy(fs->path, override_path, sizeof(fs->path) - 1); |
| 224 | fs->path[sizeof(fs->path) - 1] = '\0'; | ||
| 223 | return true; | 225 | return true; |
| 224 | } | 226 | } |
| 225 | 227 | ||
| @@ -71,3 +71,18 @@ int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, · 70 unmodified lines | |||
| 71 | BITMAP_LAST_WORD_MASK(bits)); | 71 | BITMAP_LAST_WORD_MASK(bits)); |
| 72 | return result != 0; | 72 | return result != 0; |
| 73 | } | 73 | } |
| 74 | |||
| 75 | int __bitmap_equal(const unsigned long *bitmap1, | ||
| 76 | const unsigned long *bitmap2, unsigned int bits) | ||
| 77 | { | ||
| 78 | unsigned int k, lim = bits/BITS_PER_LONG; | ||
| 79 | for (k = 0; k < lim; ++k) | ||
| 80 | if (bitmap1[k] != bitmap2[k]) | ||
| 81 | return 0; | ||
| 82 | |||
| 83 | if (bits % BITS_PER_LONG) | ||
| 84 | if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) | ||
| 85 | return 0; | ||
| 86 | |||
| 87 | return 1; | ||
| 88 | } | ||
File renamed without changes.
| @@ -0,0 +1,156 @@ | |||
| 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | ||
| 2 | # Most of this file is copied from tools/perf/Documentation/Makefile | ||
| 3 | |||
| 4 | include ../../../scripts/Makefile.include | ||
| 5 | include ../../../scripts/utilities.mak | ||
| 6 | |||
| 7 | MAN3_TXT = libperf.txt | ||
| 8 | MAN7_TXT = libperf-counting.txt libperf-sampling.txt | ||
| 9 | MAN_EX = examples/*.c | ||
| 10 | |||
| 11 | MAN_TXT = $(MAN3_TXT) $(MAN7_TXT) | ||
| 12 | |||
| 13 | _MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT)) | ||
| 14 | _MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT)) | ||
| 15 | _MAN_3 = $(patsubst %.txt,%.3,$(MAN3_TXT)) | ||
| 16 | _MAN_7 = $(patsubst %.txt,%.7,$(MAN7_TXT)) | ||
| 17 | |||
| 18 | MAN_XML = $(addprefix $(OUTPUT),$(_MAN_XML)) | ||
| 19 | MAN_HTML = $(addprefix $(OUTPUT),$(_MAN_HTML)) | ||
| 20 | MAN_3 = $(addprefix $(OUTPUT),$(_MAN_3)) | ||
| 21 | MAN_7 = $(addprefix $(OUTPUT),$(_MAN_7)) | ||
| 22 | MAN_X = $(MAN_3) $(MAN_7) | ||
| 23 | |||
| 24 | # Make the path relative to DESTDIR, not prefix | ||
| 25 | ifndef DESTDIR | ||
| 26 | prefix ?=$(HOME) | ||
| 27 | endif | ||
| 28 | |||
| 29 | mandir ?= $(prefix)/share/man | ||
| 30 | man3dir = $(mandir)/man3 | ||
| 31 | man7dir = $(mandir)/man7 | ||
| 32 | |||
| 33 | docdir ?= $(prefix)/share/doc/libperf | ||
| 34 | htmldir = $(docdir)/html | ||
| 35 | exdir = $(docdir)/examples | ||
| 36 | |||
| 37 | ASCIIDOC = asciidoc | ||
| 38 | ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf | ||
| 39 | ASCIIDOC_HTML = xhtml11 | ||
| 40 | MANPAGE_XSL = manpage-normal.xsl | ||
| 41 | XMLTO_EXTRA = | ||
| 42 | XMLTO =xmlto | ||
| 43 | |||
| 44 | INSTALL ?= install | ||
| 45 | RM ?= rm -f | ||
| 46 | |||
| 47 | # For asciidoc ... | ||
| 48 | # -7.1.2, no extra settings are needed. | ||
| 49 | # 8.0-, set ASCIIDOC8. | ||
| 50 | # | ||
| 51 | |||
| 52 | # For docbook-xsl ... | ||
| 53 | # -1.68.1, set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0) | ||
| 54 | # 1.69.0, no extra settings are needed? | ||
| 55 | # 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP? | ||
| 56 | # 1.71.1, no extra settings are needed? | ||
| 57 | # 1.72.0, set DOCBOOK_XSL_172. | ||
| 58 | # 1.73.0-, set ASCIIDOC_NO_ROFF | ||
| 59 | |||
| 60 | # If you had been using DOCBOOK_XSL_172 in an attempt to get rid | ||
| 61 | # of 'the ".ft C" problem' in your generated manpages, and you | ||
| 62 | # instead ended up with weird characters around callouts, try | ||
| 63 | # using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8). | ||
| 64 | |||
| 65 | ifdef ASCIIDOC8 | ||
| 66 | ASCIIDOC_EXTRA += -a asciidoc7compatible | ||
| 67 | endif | ||
| 68 | ifdef DOCBOOK_XSL_172 | ||
| 69 | ASCIIDOC_EXTRA += -a libperf-asciidoc-no-roff | ||
| 70 | MANPAGE_XSL = manpage-1.72.xsl | ||
| 71 | else | ||
| 72 | ifdef ASCIIDOC_NO_ROFF | ||
| 73 | # docbook-xsl after 1.72 needs the regular XSL, but will not | ||
| 74 | # pass-thru raw roff codes from asciidoc.conf, so turn them off. | ||
| 75 | ASCIIDOC_EXTRA += -a libperf-asciidoc-no-roff | ||
| 76 | endif | ||
| 77 | endif | ||
| 78 | ifdef MAN_BOLD_LITERAL | ||
| 79 | XMLTO_EXTRA += -m manpage-bold-literal.xsl | ||
| 80 | endif | ||
| 81 | ifdef DOCBOOK_SUPPRESS_SP | ||
| 82 | XMLTO_EXTRA += -m manpage-suppress-sp.xsl | ||
| 83 | endif | ||
| 84 | |||
| 85 | DESTDIR ?= | ||
| 86 | DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' | ||
| 87 | |||
| 88 | export DESTDIR DESTDIR_SQ | ||
| 89 | |||
| 90 | # Please note that there is a minor bug in asciidoc. | ||
| 91 | # The version after 6.0.3 _will_ include the patch found here: | ||
| 92 | # http://marc.theaimsgroup.com/?l=libtraceevent&m=111558757202243&w=2 | ||
| 93 | # | ||
| 94 | # Until that version is released you may have to apply the patch | ||
| 95 | # yourself - yes, all 6 characters of it! | ||
| 96 | |||
| 97 | QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir | ||
| 98 | QUIET_SUBDIR1 = | ||
| 99 | |||
| 100 | ifneq ($(findstring $(MAKEFLAGS),w),w) | ||
| 101 | PRINT_DIR = --no-print-directory | ||
| 102 | else # "make -w" | ||
| 103 | NO_SUBDIR = : | ||
| 104 | endif | ||
| 105 | |||
| 106 | ifneq ($(findstring $(MAKEFLAGS),s),s) | ||
| 107 | ifneq ($(V),1) | ||
| 108 | QUIET_ASCIIDOC = @echo ' ASCIIDOC '$@; | ||
| 109 | QUIET_XMLTO = @echo ' XMLTO '$@; | ||
| 110 | endif | ||
| 111 | endif | ||
| 112 | |||
| 113 | all: $(MAN_X) $(MAN_HTML) | ||
| 114 | |||
| 115 | $(MAN_HTML) $(MAN_X): asciidoc.conf | ||
| 116 | |||
| 117 | install-man: all | ||
| 118 | $(call QUIET_INSTALL, man) \ | ||
| 119 | $(INSTALL) -d -m 755 $(DESTDIR)$(man3dir); \ | ||
| 120 | $(INSTALL) -m 644 $(MAN_3) $(DESTDIR)$(man3dir); \ | ||
| 121 | $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \ | ||
| 122 | $(INSTALL) -m 644 $(MAN_7) $(DESTDIR)$(man7dir); | ||
| 123 | |||
| 124 | install-html: | ||
| 125 | $(call QUIET_INSTALL, html) \ | ||
| 126 | $(INSTALL) -d -m 755 $(DESTDIR)$(htmldir); \ | ||
| 127 | $(INSTALL) -m 644 $(MAN_HTML) $(DESTDIR)$(htmldir); \ | ||
| 128 | |||
| 129 | install-examples: | ||
| 130 | $(call QUIET_INSTALL, examples) \ | ||
| 131 | $(INSTALL) -d -m 755 $(DESTDIR)$(exdir); \ | ||
| 132 | $(INSTALL) -m 644 $(MAN_EX) $(DESTDIR)$(exdir); \ | ||
| 133 | |||
| 134 | CLEAN_FILES = \ | ||
| 135 | $(MAN_XML) $(addsuffix +,$(MAN_XML)) \ | ||
| 136 | $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) \ | ||
| 137 | $(MAN_X) | ||
| 138 | |||
| 139 | clean: | ||
| 140 | $(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES) | ||
| 141 | |||
| 142 | $(MAN_3): $(OUTPUT)%.3: %.xml | ||
| 143 | $(QUIET_XMLTO)$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< | ||
| 144 | |||
| 145 | $(MAN_7): $(OUTPUT)%.7: %.xml | ||
| 146 | $(QUIET_XMLTO)$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< | ||
| 147 | |||
| 148 | $(MAN_XML): $(OUTPUT)%.xml: %.txt | ||
| 149 | $(QUIET_ASCIIDOC)$(ASCIIDOC) -b docbook -d manpage \ | ||
| 150 | $(ASCIIDOC_EXTRA) -alibperf_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \ | ||
| 151 | mv $@+ $@ | ||
| 152 | |||
| 153 | $(MAN_HTML): $(OUTPUT)%.html: %.txt | ||
| 154 | $(QUIET_ASCIIDOC)$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \ | ||
| 155 | $(ASCIIDOC_EXTRA) -aperf_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \ | ||
| 156 | mv $@+ $@ | ||
| @@ -0,0 +1,120 @@ | |||
| 1 | ## linktep: macro | ||
| 2 | # | ||
| 3 | # Usage: linktep:command[manpage-section] | ||
| 4 | # | ||
| 5 | # Note, {0} is the manpage section, while {target} is the command. | ||
| 6 | # | ||
| 7 | # Show TEP link as: <command>(<section>); if section is defined, else just show | ||
| 8 | # the command. | ||
| 9 | |||
| 10 | [macros] | ||
| 11 | (?su)[\\]?(?P<name>linktep):(?P<target>\S*?)\[(?P<attrlist>.*?)\]= | ||
| 12 | |||
| 13 | [attributes] | ||
| 14 | asterisk=* | ||
| 15 | plus=+ | ||
| 16 | caret=^ | ||
| 17 | startsb=[ | ||
| 18 | endsb=] | ||
| 19 | tilde=~ | ||
| 20 | |||
| 21 | ifdef::backend-docbook[] | ||
| 22 | [linktep-inlinemacro] | ||
| 23 | {0%{target}} | ||
| 24 | {0#<citerefentry>} | ||
| 25 | {0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>} | ||
| 26 | {0#</citerefentry>} | ||
| 27 | endif::backend-docbook[] | ||
| 28 | |||
| 29 | ifdef::backend-docbook[] | ||
| 30 | ifndef::tep-asciidoc-no-roff[] | ||
| 31 | # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. | ||
| 32 | # v1.72 breaks with this because it replaces dots not in roff requests. | ||
| 33 | [listingblock] | ||
| 34 | <example><title>{title}</title> | ||
| 35 | <literallayout> | ||
| 36 | ifdef::doctype-manpage[] | ||
| 37 | .ft C | ||
| 38 | endif::doctype-manpage[] | ||
| 39 | | | ||
| 40 | ifdef::doctype-manpage[] | ||
| 41 | .ft | ||
| 42 | endif::doctype-manpage[] | ||
| 43 | </literallayout> | ||
| 44 | {title#}</example> | ||
| 45 | endif::tep-asciidoc-no-roff[] | ||
| 46 | |||
| 47 | ifdef::tep-asciidoc-no-roff[] | ||
| 48 | ifdef::doctype-manpage[] | ||
| 49 | # The following two small workarounds insert a simple paragraph after screen | ||
| 50 | [listingblock] | ||
| 51 | <example><title>{title}</title> | ||
| 52 | <literallayout> | ||
| 53 | | | ||
| 54 | </literallayout><simpara></simpara> | ||
| 55 | {title#}</example> | ||
| 56 | |||
| 57 | [verseblock] | ||
| 58 | <formalpara{id? id="{id}"}><title>{title}</title><para> | ||
| 59 | {title%}<literallayout{id? id="{id}"}> | ||
| 60 | {title#}<literallayout> | ||
| 61 | | | ||
| 62 | </literallayout> | ||
| 63 | {title#}</para></formalpara> | ||
| 64 | {title%}<simpara></simpara> | ||
| 65 | endif::doctype-manpage[] | ||
| 66 | endif::tep-asciidoc-no-roff[] | ||
| 67 | endif::backend-docbook[] | ||
| 68 | |||
| 69 | ifdef::doctype-manpage[] | ||
| 70 | ifdef::backend-docbook[] | ||
| 71 | [header] | ||
| 72 | template::[header-declarations] | ||
| 73 | <refentry> | ||
| 74 | <refmeta> | ||
| 75 | <refentrytitle>{mantitle}</refentrytitle> | ||
| 76 | <manvolnum>{manvolnum}</manvolnum> | ||
| 77 | <refmiscinfo class="source">libperf</refmiscinfo> | ||
| 78 | <refmiscinfo class="version">{libperf_version}</refmiscinfo> | ||
| 79 | <refmiscinfo class="manual">libperf Manual</refmiscinfo> | ||
| 80 | </refmeta> | ||
| 81 | <refnamediv> | ||
| 82 | <refname>{manname1}</refname> | ||
| 83 | <refname>{manname2}</refname> | ||
| 84 | <refname>{manname3}</refname> | ||
| 85 | <refname>{manname4}</refname> | ||
| 86 | <refname>{manname5}</refname> | ||
| 87 | <refname>{manname6}</refname> | ||
| 88 | <refname>{manname7}</refname> | ||
| 89 | <refname>{manname8}</refname> | ||
| 90 | <refname>{manname9}</refname> | ||
| 91 | <refname>{manname10}</refname> | ||
| 92 | <refname>{manname11}</refname> | ||
| 93 | <refname>{manname12}</refname> | ||
| 94 | <refname>{manname13}</refname> | ||
| 95 | <refname>{manname14}</refname> | ||
| 96 | <refname>{manname15}</refname> | ||
| 97 | <refname>{manname16}</refname> | ||
| 98 | <refname>{manname17}</refname> | ||
| 99 | <refname>{manname18}</refname> | ||
| 100 | <refname>{manname19}</refname> | ||
| 101 | <refname>{manname20}</refname> | ||
| 102 | <refname>{manname21}</refname> | ||
| 103 | <refname>{manname22}</refname> | ||
| 104 | <refname>{manname23}</refname> | ||
| 105 | <refname>{manname24}</refname> | ||
| 106 | <refname>{manname25}</refname> | ||
| 107 | <refname>{manname26}</refname> | ||
| 108 | <refname>{manname27}</refname> | ||
| 109 | <refname>{manname28}</refname> | ||
| 110 | <refname>{manname29}</refname> | ||
| 111 | <refname>{manname30}</refname> | ||
| 112 | <refpurpose>{manpurpose}</refpurpose> | ||
| 113 | </refnamediv> | ||
| 114 | endif::backend-docbook[] | ||
| 115 | endif::doctype-manpage[] | ||
| 116 | |||
| 117 | ifdef::backend-xhtml11[] | ||
| 118 | [linktep-inlinemacro] | ||
| 119 | <a href="{target}.html">{target}{0?({0})}</a> | ||
| 120 | endif::backend-xhtml11[] | ||
| @@ -0,0 +1,119 @@ | |||
| 1 | #include <linux/perf_event.h> | ||
| 2 | #include <perf/evlist.h> | ||
| 3 | #include <perf/evsel.h> | ||
| 4 | #include <perf/cpumap.h> | ||
| 5 | #include <perf/threadmap.h> | ||
| 6 | #include <perf/mmap.h> | ||
| 7 | #include <perf/core.h> | ||
| 8 | #include <perf/event.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | |||
| 12 | static int libperf_print(enum libperf_print_level level, | ||
| 13 | const char *fmt, va_list ap) | ||
| 14 | { | ||
| 15 | return vfprintf(stderr, fmt, ap); | ||
| 16 | } | ||
| 17 | |||
| 18 | union u64_swap { | ||
| 19 | __u64 val64; | ||
| 20 | __u32 val32[2]; | ||
| 21 | }; | ||
| 22 | |||
| 23 | int main(int argc, char **argv) | ||
| 24 | { | ||
| 25 | struct perf_evlist *evlist; | ||
| 26 | struct perf_evsel *evsel; | ||
| 27 | struct perf_mmap *map; | ||
| 28 | struct perf_cpu_map *cpus; | ||
| 29 | struct perf_event_attr attr = { | ||
| 30 | .type = PERF_TYPE_HARDWARE, | ||
| 31 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
| 32 | .disabled = 1, | ||
| 33 | .freq = 1, | ||
| 34 | .sample_freq = 10, | ||
| 35 | .sample_type = PERF_SAMPLE_IP|PERF_SAMPLE_TID|PERF_SAMPLE_CPU|PERF_SAMPLE_PERIOD, | ||
| 36 | }; | ||
| 37 | int err = -1; | ||
| 38 | union perf_event *event; | ||
| 39 | |||
| 40 | libperf_init(libperf_print); | ||
| 41 | |||
| 42 | cpus = perf_cpu_map__new(NULL); | ||
| 43 | if (!cpus) { | ||
| 44 | fprintf(stderr, "failed to create cpus\n"); | ||
| 45 | return -1; | ||
| 46 | } | ||
| 47 | |||
| 48 | evlist = perf_evlist__new(); | ||
| 49 | if (!evlist) { | ||
| 50 | fprintf(stderr, "failed to create evlist\n"); | ||
| 51 | goto out_cpus; | ||
| 52 | } | ||
| 53 | |||
| 54 | evsel = perf_evsel__new(&attr); | ||
| 55 | if (!evsel) { | ||
| 56 | fprintf(stderr, "failed to create cycles\n"); | ||
| 57 | goto out_cpus; | ||
| 58 | } | ||
| 59 | |||
| 60 | perf_evlist__add(evlist, evsel); | ||
| 61 | |||
| 62 | perf_evlist__set_maps(evlist, cpus, NULL); | ||
| 63 | |||
| 64 | err = perf_evlist__open(evlist); | ||
| 65 | if (err) { | ||
| 66 | fprintf(stderr, "failed to open evlist\n"); | ||
| 67 | goto out_evlist; | ||
| 68 | } | ||
| 69 | |||
| 70 | err = perf_evlist__mmap(evlist, 4); | ||
| 71 | if (err) { | ||
| 72 | fprintf(stderr, "failed to mmap evlist\n"); | ||
| 73 | goto out_evlist; | ||
| 74 | } | ||
| 75 | |||
| 76 | perf_evlist__enable(evlist); | ||
| 77 | sleep(3); | ||
| 78 | perf_evlist__disable(evlist); | ||
| 79 | |||
| 80 | perf_evlist__for_each_mmap(evlist, map, false) { | ||
| 81 | if (perf_mmap__read_init(map) < 0) | ||
| 82 | continue; | ||
| 83 | |||
| 84 | while ((event = perf_mmap__read_event(map)) != NULL) { | ||
| 85 | int cpu, pid, tid; | ||
| 86 | __u64 ip, period, *array; | ||
| 87 | union u64_swap u; | ||
| 88 | |||
| 89 | array = event->sample.array; | ||
| 90 | |||
| 91 | ip = *array; | ||
| 92 | array++; | ||
| 93 | |||
| 94 | u.val64 = *array; | ||
| 95 | pid = u.val32[0]; | ||
| 96 | tid = u.val32[1]; | ||
| 97 | array++; | ||
| 98 | |||
| 99 | u.val64 = *array; | ||
| 100 | cpu = u.val32[0]; | ||
| 101 | array++; | ||
| 102 | |||
| 103 | period = *array; | ||
| 104 | |||
| 105 | fprintf(stdout, "cpu %3d, pid %6d, tid %6d, ip %20llx, period %20llu\n", | ||
| 106 | cpu, pid, tid, ip, period); | ||
| 107 | |||
| 108 | perf_mmap__consume(map); | ||
| 109 | } | ||
| 110 | |||
| 111 | perf_mmap__read_done(map); | ||
| 112 | } | ||
| 113 | |||
| 114 | out_evlist: | ||
| 115 | perf_evlist__delete(evlist); | ||
| 116 | out_cpus: | ||
| 117 | perf_cpu_map__put(cpus); | ||
| 118 | return err; | ||
| 119 | } | ||
| @@ -0,0 +1,211 @@ | |||
| 1 | libperf-counting(7) | ||
| 2 | =================== | ||
| 3 | |||
| 4 | NAME | ||
| 5 | ---- | ||
| 6 | libperf-counting - counting interface | ||
| 7 | |||
| 8 | DESCRIPTION | ||
| 9 | ----------- | ||
| 10 | The counting interface provides API to meassure and get count for specific perf events. | ||
| 11 | |||
| 12 | The following test tries to explain count on `counting.c` example. | ||
| 13 | |||
| 14 | It is by no means complete guide to counting, but shows libperf basic API for counting. | ||
| 15 | |||
| 16 | The `counting.c` comes with libbperf package and can be compiled and run like: | ||
| 17 | |||
| 18 | [source,bash] | ||
| 19 | -- | ||
| 20 | $ gcc -o counting counting.c -lperf | ||
| 21 | $ sudo ./counting | ||
| 22 | count 176792, enabled 176944, run 176944 | ||
| 23 | count 176242, enabled 176242, run 176242 | ||
| 24 | -- | ||
| 25 | |||
| 26 | It requires root access, because of the `PERF_COUNT_SW_CPU_CLOCK` event, | ||
| 27 | which is available only for root. | ||
| 28 | |||
| 29 | The `counting.c` example monitors two events on the current process and displays their count, in a nutshel it: | ||
| 30 | |||
| 31 | * creates events | ||
| 32 | * adds them to the event list | ||
| 33 | * opens and enables events through the event list | ||
| 34 | * does some workload | ||
| 35 | * disables events | ||
| 36 | * reads and displays event counts | ||
| 37 | * destroys the event list | ||
| 38 | |||
| 39 | The first thing you need to do before using libperf is to call init function: | ||
| 40 | |||
| 41 | [source,c] | ||
| 42 | -- | ||
| 43 | 8 static int libperf_print(enum libperf_print_level level, | ||
| 44 | 9 const char *fmt, va_list ap) | ||
| 45 | 10 { | ||
| 46 | 11 return vfprintf(stderr, fmt, ap); | ||
| 47 | 12 } | ||
| 48 | |||
| 49 | 14 int main(int argc, char **argv) | ||
| 50 | 15 { | ||
| 51 | ... | ||
| 52 | 35 libperf_init(libperf_print); | ||
| 53 | -- | ||
| 54 | |||
| 55 | It will setup the library and sets function for debug output from library. | ||
| 56 | |||
| 57 | The `libperf_print` callback will receive any message with its debug level, | ||
| 58 | defined as: | ||
| 59 | |||
| 60 | [source,c] | ||
| 61 | -- | ||
| 62 | enum libperf_print_level { | ||
| 63 | LIBPERF_ERR, | ||
| 64 | LIBPERF_WARN, | ||
| 65 | LIBPERF_INFO, | ||
| 66 | LIBPERF_DEBUG, | ||
| 67 | LIBPERF_DEBUG2, | ||
| 68 | LIBPERF_DEBUG3, | ||
| 69 | }; | ||
| 70 | -- | ||
| 71 | |||
| 72 | Once the setup is complete we start by defining specific events using the `struct perf_event_attr`. | ||
| 73 | |||
| 74 | We create software events for cpu and task: | ||
| 75 | |||
| 76 | [source,c] | ||
| 77 | -- | ||
| 78 | 20 struct perf_event_attr attr1 = { | ||
| 79 | 21 .type = PERF_TYPE_SOFTWARE, | ||
| 80 | 22 .config = PERF_COUNT_SW_CPU_CLOCK, | ||
| 81 | 23 .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING, | ||
| 82 | 24 .disabled = 1, | ||
| 83 | 25 }; | ||
| 84 | 26 struct perf_event_attr attr2 = { | ||
| 85 | 27 .type = PERF_TYPE_SOFTWARE, | ||
| 86 | 28 .config = PERF_COUNT_SW_TASK_CLOCK, | ||
| 87 | 29 .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING, | ||
| 88 | 30 .disabled = 1, | ||
| 89 | 31 }; | ||
| 90 | -- | ||
| 91 | |||
| 92 | The `read_format` setup tells perf to include timing details together with each count. | ||
| 93 | |||
| 94 | Next step is to prepare threads map. | ||
| 95 | |||
| 96 | In this case we will monitor current process, so we create threads map with single pid (0): | ||
| 97 | |||
| 98 | [source,c] | ||
| 99 | -- | ||
| 100 | 37 threads = perf_thread_map__new_dummy(); | ||
| 101 | 38 if (!threads) { | ||
| 102 | 39 fprintf(stderr, "failed to create threads\n"); | ||
| 103 | 40 return -1; | ||
| 104 | 41 } | ||
| 105 | 42 | ||
| 106 | 43 perf_thread_map__set_pid(threads, 0, 0); | ||
| 107 | -- | ||
| 108 | |||
| 109 | Now we create libperf's event list, which will serve as holder for the events we want: | ||
| 110 | |||
| 111 | [source,c] | ||
| 112 | -- | ||
| 113 | 45 evlist = perf_evlist__new(); | ||
| 114 | 46 if (!evlist) { | ||
| 115 | 47 fprintf(stderr, "failed to create evlist\n"); | ||
| 116 | 48 goto out_threads; | ||
| 117 | 49 } | ||
| 118 | -- | ||
| 119 | |||
| 120 | We create libperf's events for the attributes we defined earlier and add them to the list: | ||
| 121 | |||
| 122 | [source,c] | ||
| 123 | -- | ||
| 124 | 51 evsel = perf_evsel__new(&attr1); | ||
| 125 | 52 if (!evsel) { | ||
| 126 | 53 fprintf(stderr, "failed to create evsel1\n"); | ||
| 127 | 54 goto out_evlist; | ||
| 128 | 55 } | ||
| 129 | 56 | ||
| 130 | 57 perf_evlist__add(evlist, evsel); | ||
| 131 | 58 | ||
| 132 | 59 evsel = perf_evsel__new(&attr2); | ||
| 133 | 60 if (!evsel) { | ||
| 134 | 61 fprintf(stderr, "failed to create evsel2\n"); | ||
| 135 | 62 goto out_evlist; | ||
| 136 | 63 } | ||
| 137 | 64 | ||
| 138 | 65 perf_evlist__add(evlist, evsel); | ||
| 139 | -- | ||
| 140 | |||
| 141 | Configure event list with the thread map and open events: | ||
| 142 | |||
| 143 | [source,c] | ||
| 144 | -- | ||
| 145 | 67 perf_evlist__set_maps(evlist, NULL, threads); | ||
| 146 | 68 | ||
| 147 | 69 err = perf_evlist__open(evlist); | ||
| 148 | 70 if (err) { | ||
| 149 | 71 fprintf(stderr, "failed to open evsel\n"); | ||
| 150 | 72 goto out_evlist; | ||
| 151 | 73 } | ||
| 152 | -- | ||
| 153 | |||
| 154 | Both events are created as disabled (note the `disabled = 1` assignment above), | ||
| 155 | so we need to enable the whole list explicitely (both events). | ||
| 156 | |||
| 157 | From this moment events are counting and we can do our workload. | ||
| 158 | |||
| 159 | When we are done we disable the events list. | ||
| 160 | |||
| 161 | [source,c] | ||
| 162 | -- | ||
| 163 | 75 perf_evlist__enable(evlist); | ||
| 164 | 76 | ||
| 165 | 77 while (count--); | ||
| 166 | 78 | ||
| 167 | 79 perf_evlist__disable(evlist); | ||
| 168 | -- | ||
| 169 | |||
| 170 | Now we need to get the counts from events, following code iterates throught the events list and read counts: | ||
| 171 | |||
| 172 | [source,c] | ||
| 173 | -- | ||
| 174 | 81 perf_evlist__for_each_evsel(evlist, evsel) { | ||
| 175 | 82 perf_evsel__read(evsel, 0, 0, &counts); | ||
| 176 | 83 fprintf(stdout, "count %llu, enabled %llu, run %llu\n", | ||
| 177 | 84 counts.val, counts.ena, counts.run); | ||
| 178 | 85 } | ||
| 179 | -- | ||
| 180 | |||
| 181 | And finaly cleanup. | ||
| 182 | |||
| 183 | We close the whole events list (both events) and remove it together with the threads map: | ||
| 184 | |||
| 185 | [source,c] | ||
| 186 | -- | ||
| 187 | 87 perf_evlist__close(evlist); | ||
| 188 | 88 | ||
| 189 | 89 out_evlist: | ||
| 190 | 90 perf_evlist__delete(evlist); | ||
| 191 | 91 out_threads: | ||
| 192 | 92 perf_thread_map__put(threads); | ||
| 193 | 93 return err; | ||
| 194 | 94 } | ||
| 195 | -- | ||
| 196 | |||
| 197 | REPORTING BUGS | ||
| 198 | -------------- | ||
| 199 | Report bugs to <linux-perf-users@vger.kernel.org>. | ||
| 200 | |||
| 201 | LICENSE | ||
| 202 | ------- | ||
| 203 | libperf is Free Software licensed under the GNU LGPL 2.1 | ||
| 204 | |||
| 205 | RESOURCES | ||
| 206 | --------- | ||
| 207 | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git | ||
| 208 | |||
| 209 | SEE ALSO | ||
| 210 | -------- | ||
| 211 | libperf(3), libperf-sampling(7) | ||
| @@ -0,0 +1,243 @@ | |||
| 1 | libperf-sampling(7) | ||
| 2 | =================== | ||
| 3 | |||
| 4 | NAME | ||
| 5 | ---- | ||
| 6 | libperf-sampling - sampling interface | ||
| 7 | |||
| 8 | |||
| 9 | DESCRIPTION | ||
| 10 | ----------- | ||
| 11 | The sampling interface provides API to meassure and get count for specific perf events. | ||
| 12 | |||
| 13 | The following test tries to explain count on `sampling.c` example. | ||
| 14 | |||
| 15 | It is by no means complete guide to sampling, but shows libperf basic API for sampling. | ||
| 16 | |||
| 17 | The `sampling.c` comes with libbperf package and can be compiled and run like: | ||
| 18 | |||
| 19 | [source,bash] | ||
| 20 | -- | ||
| 21 | $ gcc -o sampling sampling.c -lperf | ||
| 22 | $ sudo ./sampling | ||
| 23 | cpu 0, pid 0, tid 0, ip ffffffffad06c4e6, period 1 | ||
| 24 | cpu 0, pid 4465, tid 4469, ip ffffffffad118748, period 18322959 | ||
| 25 | cpu 0, pid 0, tid 0, ip ffffffffad115722, period 33544846 | ||
| 26 | cpu 0, pid 4465, tid 4470, ip 7f84fe0cdad6, period 23687474 | ||
| 27 | cpu 0, pid 0, tid 0, ip ffffffffad9e0349, period 34255790 | ||
| 28 | cpu 0, pid 4465, tid 4469, ip ffffffffad136581, period 38664069 | ||
| 29 | cpu 0, pid 0, tid 0, ip ffffffffad9e55e2, period 21922384 | ||
| 30 | cpu 0, pid 4465, tid 4470, ip 7f84fe0ebebf, period 17655175 | ||
| 31 | ... | ||
| 32 | -- | ||
| 33 | |||
| 34 | It requires root access, because it uses hardware cycles event. | ||
| 35 | |||
| 36 | The `sampling.c` example profiles/samples all CPUs with hardware cycles, in a nutshel it: | ||
| 37 | |||
| 38 | - creates events | ||
| 39 | - adds them to the event list | ||
| 40 | - opens and enables events through the event list | ||
| 41 | - sleeps for 3 seconds | ||
| 42 | - disables events | ||
| 43 | - reads and displays recorded samples | ||
| 44 | - destroys the event list | ||
| 45 | |||
| 46 | The first thing you need to do before using libperf is to call init function: | ||
| 47 | |||
| 48 | [source,c] | ||
| 49 | -- | ||
| 50 | 12 static int libperf_print(enum libperf_print_level level, | ||
| 51 | 13 const char *fmt, va_list ap) | ||
| 52 | 14 { | ||
| 53 | 15 return vfprintf(stderr, fmt, ap); | ||
| 54 | 16 } | ||
| 55 | |||
| 56 | 23 int main(int argc, char **argv) | ||
| 57 | 24 { | ||
| 58 | ... | ||
| 59 | 40 libperf_init(libperf_print); | ||
| 60 | -- | ||
| 61 | |||
| 62 | It will setup the library and sets function for debug output from library. | ||
| 63 | |||
| 64 | The `libperf_print` callback will receive any message with its debug level, | ||
| 65 | defined as: | ||
| 66 | |||
| 67 | [source,c] | ||
| 68 | -- | ||
| 69 | enum libperf_print_level { | ||
| 70 | LIBPERF_ERR, | ||
| 71 | LIBPERF_WARN, | ||
| 72 | LIBPERF_INFO, | ||
| 73 | LIBPERF_DEBUG, | ||
| 74 | LIBPERF_DEBUG2, | ||
| 75 | LIBPERF_DEBUG3, | ||
| 76 | }; | ||
| 77 | -- | ||
| 78 | |||
| 79 | Once the setup is complete we start by defining cycles event using the `struct perf_event_attr`: | ||
| 80 | |||
| 81 | [source,c] | ||
| 82 | -- | ||
| 83 | 29 struct perf_event_attr attr = { | ||
| 84 | 30 .type = PERF_TYPE_HARDWARE, | ||
| 85 | 31 .config = PERF_COUNT_HW_CPU_CYCLES, | ||
| 86 | 32 .disabled = 1, | ||
| 87 | 33 .freq = 1, | ||
| 88 | 34 .sample_freq = 10, | ||
| 89 | 35 .sample_type = PERF_SAMPLE_IP|PERF_SAMPLE_TID|PERF_SAMPLE_CPU|PERF_SAMPLE_PERIOD, | ||
| 90 | 36 }; | ||
| 91 | -- | ||
| 92 | |||
| 93 | Next step is to prepare cpus map. | ||
| 94 | |||
| 95 | In this case we will monitor all the available CPUs: | ||
| 96 | |||
| 97 | [source,c] | ||
| 98 | -- | ||
| 99 | 42 cpus = perf_cpu_map__new(NULL); | ||
| 100 | 43 if (!cpus) { | ||
| 101 | 44 fprintf(stderr, "failed to create cpus\n"); | ||
| 102 | 45 return -1; | ||
| 103 | 46 } | ||
| 104 | -- | ||
| 105 | |||
| 106 | Now we create libperf's event list, which will serve as holder for the cycles event: | ||
| 107 | |||
| 108 | [source,c] | ||
| 109 | -- | ||
| 110 | 48 evlist = perf_evlist__new(); | ||
| 111 | 49 if (!evlist) { | ||
| 112 | 50 fprintf(stderr, "failed to create evlist\n"); | ||
| 113 | 51 goto out_cpus; | ||
| 114 | 52 } | ||
| 115 | -- | ||
| 116 | |||
| 117 | We create libperf's event for the cycles attribute we defined earlier and add it to the list: | ||
| 118 | |||
| 119 | [source,c] | ||
| 120 | -- | ||
| 121 | 54 evsel = perf_evsel__new(&attr); | ||
| 122 | 55 if (!evsel) { | ||
| 123 | 56 fprintf(stderr, "failed to create cycles\n"); | ||
| 124 | 57 goto out_cpus; | ||
| 125 | 58 } | ||
| 126 | 59 | ||
| 127 | 60 perf_evlist__add(evlist, evsel); | ||
| 128 | -- | ||
| 129 | |||
| 130 | Configure event list with the cpus map and open event: | ||
| 131 | |||
| 132 | [source,c] | ||
| 133 | -- | ||
| 134 | 62 perf_evlist__set_maps(evlist, cpus, NULL); | ||
| 135 | 63 | ||
| 136 | 64 err = perf_evlist__open(evlist); | ||
| 137 | 65 if (err) { | ||
| 138 | 66 fprintf(stderr, "failed to open evlist\n"); | ||
| 139 | 67 goto out_evlist; | ||
| 140 | 68 } | ||
| 141 | -- | ||
| 142 | |||
| 143 | Once the events list is open, we can create memory maps AKA perf ring buffers: | ||
| 144 | |||
| 145 | [source,c] | ||
| 146 | -- | ||
| 147 | 70 err = perf_evlist__mmap(evlist, 4); | ||
| 148 | 71 if (err) { | ||
| 149 | 72 fprintf(stderr, "failed to mmap evlist\n"); | ||
| 150 | 73 goto out_evlist; | ||
| 151 | 74 } | ||
| 152 | -- | ||
| 153 | |||
| 154 | The event is created as disabled (note the `disabled = 1` assignment above), | ||
| 155 | so we need to enable the events list explicitely. | ||
| 156 | |||
| 157 | From this moment the cycles event is sampling. | ||
| 158 | |||
| 159 | We will sleep for 3 seconds while the ring buffers get data from all CPUs, then we disable the events list. | ||
| 160 | |||
| 161 | [source,c] | ||
| 162 | -- | ||
| 163 | 76 perf_evlist__enable(evlist); | ||
| 164 | 77 sleep(3); | ||
| 165 | 78 perf_evlist__disable(evlist); | ||
| 166 | -- | ||
| 167 | |||
| 168 | Following code walks through the ring buffers and reads stored events/samples: | ||
| 169 | |||
| 170 | [source,c] | ||
| 171 | -- | ||
| 172 | 80 perf_evlist__for_each_mmap(evlist, map, false) { | ||
| 173 | 81 if (perf_mmap__read_init(map) < 0) | ||
| 174 | 82 continue; | ||
| 175 | 83 | ||
| 176 | 84 while ((event = perf_mmap__read_event(map)) != NULL) { | ||
| 177 | |||
| 178 | /* process event */ | ||
| 179 | |||
| 180 | 108 perf_mmap__consume(map); | ||
| 181 | 109 } | ||
| 182 | 110 perf_mmap__read_done(map); | ||
| 183 | 111 } | ||
| 184 | |||
| 185 | -- | ||
| 186 | |||
| 187 | Each sample needs to get parsed: | ||
| 188 | |||
| 189 | [source,c] | ||
| 190 | -- | ||
| 191 | 85 int cpu, pid, tid; | ||
| 192 | 86 __u64 ip, period, *array; | ||
| 193 | 87 union u64_swap u; | ||
| 194 | 88 | ||
| 195 | 89 array = event->sample.array; | ||
| 196 | 90 | ||
| 197 | 91 ip = *array; | ||
| 198 | 92 array++; | ||
| 199 | 93 | ||
| 200 | 94 u.val64 = *array; | ||
| 201 | 95 pid = u.val32[0]; | ||
| 202 | 96 tid = u.val32[1]; | ||
| 203 | 97 array++; | ||
| 204 | 98 | ||
| 205 | 99 u.val64 = *array; | ||
| 206 | 100 cpu = u.val32[0]; | ||
| 207 | 101 array++; | ||
| 208 | 102 | ||
| 209 | 103 period = *array; | ||
| 210 | 104 | ||
| 211 | 105 fprintf(stdout, "cpu %3d, pid %6d, tid %6d, ip %20llx, period %20llu\n", | ||
| 212 | 106 cpu, pid, tid, ip, period); | ||
| 213 | -- | ||
| 214 | |||
| 215 | And finaly cleanup. | ||
| 216 | |||
| 217 | We close the whole events list (both events) and remove it together with the threads map: | ||
| 218 | |||
| 219 | [source,c] | ||
| 220 | -- | ||
| 221 | 113 out_evlist: | ||
| 222 | 114 perf_evlist__delete(evlist); | ||
| 223 | 115 out_cpus: | ||
| 224 | 116 perf_cpu_map__put(cpus); | ||
| 225 | 117 return err; | ||
| 226 | 118 } | ||
| 227 | -- | ||
| 228 | |||
| 229 | REPORTING BUGS | ||
| 230 | -------------- | ||
| 231 | Report bugs to <linux-perf-users@vger.kernel.org>. | ||
| 232 | |||
| 233 | LICENSE | ||
| 234 | ------- | ||
| 235 | libperf is Free Software licensed under the GNU LGPL 2.1 | ||
| 236 | |||
| 237 | RESOURCES | ||
| 238 | --------- | ||
| 239 | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git | ||
| 240 | |||
| 241 | SEE ALSO | ||
| 242 | -------- | ||
| 243 | libperf(3), libperf-counting(7) | ||
| @@ -0,0 +1,246 @@ | |||
| 1 | libperf(3) | ||
| 2 | ========== | ||
| 3 | |||
| 4 | NAME | ||
| 5 | ---- | ||
| 6 | libperf - Linux kernel perf event library | ||
| 7 | |||
| 8 | |||
| 9 | SYNOPSIS | ||
| 10 | -------- | ||
| 11 | *Generic API:* | ||
| 12 | |||
| 13 | [source,c] | ||
| 14 | -- | ||
| 15 | #include <perf/core.h> | ||
| 16 | |||
| 17 | enum libperf_print_level { | ||
| 18 | LIBPERF_ERR, | ||
| 19 | LIBPERF_WARN, | ||
| 20 | LIBPERF_INFO, | ||
| 21 | LIBPERF_DEBUG, | ||
| 22 | LIBPERF_DEBUG2, | ||
| 23 | LIBPERF_DEBUG3, | ||
| 24 | }; | ||
| 25 | |||
| 26 | typedef int (*libperf_print_fn_t)(enum libperf_print_level level, | ||
| 27 | const char *, va_list ap); | ||
| 28 | |||
| 29 | void libperf_init(libperf_print_fn_t fn); | ||
| 30 | -- | ||
| 31 | |||
| 32 | *API to handle cpu maps:* | ||
| 33 | |||
| 34 | [source,c] | ||
| 35 | -- | ||
| 36 | #include <perf/cpumap.h> | ||
| 37 | |||
| 38 | struct perf_cpu_map; | ||
| 39 | |||
| 40 | struct perf_cpu_map *perf_cpu_map__dummy_new(void); | ||
| 41 | struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list); | ||
| 42 | struct perf_cpu_map *perf_cpu_map__read(FILE *file); | ||
| 43 | struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); | ||
| 44 | struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, | ||
| 45 | struct perf_cpu_map *other); | ||
| 46 | void perf_cpu_map__put(struct perf_cpu_map *map); | ||
| 47 | int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); | ||
| 48 | int perf_cpu_map__nr(const struct perf_cpu_map *cpus); | ||
| 49 | bool perf_cpu_map__empty(const struct perf_cpu_map *map); | ||
| 50 | int perf_cpu_map__max(struct perf_cpu_map *map); | ||
| 51 | |||
| 52 | #define perf_cpu_map__for_each_cpu(cpu, idx, cpus) | ||
| 53 | -- | ||
| 54 | |||
| 55 | *API to handle thread maps:* | ||
| 56 | |||
| 57 | [source,c] | ||
| 58 | -- | ||
| 59 | #include <perf/threadmap.h> | ||
| 60 | |||
| 61 | struct perf_thread_map; | ||
| 62 | |||
| 63 | struct perf_thread_map *perf_thread_map__new_dummy(void); | ||
| 64 | |||
| 65 | void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid); | ||
| 66 | char *perf_thread_map__comm(struct perf_thread_map *map, int thread); | ||
| 67 | int perf_thread_map__nr(struct perf_thread_map *threads); | ||
| 68 | pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread); | ||
| 69 | |||
| 70 | struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map); | ||
| 71 | void perf_thread_map__put(struct perf_thread_map *map); | ||
| 72 | -- | ||
| 73 | |||
| 74 | *API to handle event lists:* | ||
| 75 | |||
| 76 | [source,c] | ||
| 77 | -- | ||
| 78 | #include <perf/evlist.h> | ||
| 79 | |||
| 80 | struct perf_evlist; | ||
| 81 | |||
| 82 | void perf_evlist__add(struct perf_evlist *evlist, | ||
| 83 | struct perf_evsel *evsel); | ||
| 84 | void perf_evlist__remove(struct perf_evlist *evlist, | ||
| 85 | struct perf_evsel *evsel); | ||
| 86 | struct perf_evlist *perf_evlist__new(void); | ||
| 87 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
| 88 | struct perf_evsel* perf_evlist__next(struct perf_evlist *evlist, | ||
| 89 | struct perf_evsel *evsel); | ||
| 90 | int perf_evlist__open(struct perf_evlist *evlist); | ||
| 91 | void perf_evlist__close(struct perf_evlist *evlist); | ||
| 92 | void perf_evlist__enable(struct perf_evlist *evlist); | ||
| 93 | void perf_evlist__disable(struct perf_evlist *evlist); | ||
| 94 | |||
| 95 | #define perf_evlist__for_each_evsel(evlist, pos) | ||
| 96 | |||
| 97 | void perf_evlist__set_maps(struct perf_evlist *evlist, | ||
| 98 | struct perf_cpu_map *cpus, | ||
| 99 | struct perf_thread_map *threads); | ||
| 100 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout); | ||
| 101 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, | ||
| 102 | short revents_and_mask); | ||
| 103 | |||
| 104 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages); | ||
| 105 | void perf_evlist__munmap(struct perf_evlist *evlist); | ||
| 106 | |||
| 107 | struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, | ||
| 108 | struct perf_mmap *map, | ||
| 109 | bool overwrite); | ||
| 110 | |||
| 111 | #define perf_evlist__for_each_mmap(evlist, pos, overwrite) | ||
| 112 | -- | ||
| 113 | |||
| 114 | *API to handle events:* | ||
| 115 | |||
| 116 | [source,c] | ||
| 117 | -- | ||
| 118 | #include <perf/evsel.h>* | ||
| 119 | |||
| 120 | struct perf_evsel; | ||
| 121 | |||
| 122 | struct perf_counts_values { | ||
| 123 | union { | ||
| 124 | struct { | ||
| 125 | uint64_t val; | ||
| 126 | uint64_t ena; | ||
| 127 | uint64_t run; | ||
| 128 | }; | ||
| 129 | uint64_t values[3]; | ||
| 130 | }; | ||
| 131 | }; | ||
| 132 | |||
| 133 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); | ||
| 134 | void perf_evsel__delete(struct perf_evsel *evsel); | ||
| 135 | int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, | ||
| 136 | struct perf_thread_map *threads); | ||
| 137 | void perf_evsel__close(struct perf_evsel *evsel); | ||
| 138 | void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); | ||
| 139 | int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, | ||
| 140 | struct perf_counts_values *count); | ||
| 141 | int perf_evsel__enable(struct perf_evsel *evsel); | ||
| 142 | int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu); | ||
| 143 | int perf_evsel__disable(struct perf_evsel *evsel); | ||
| 144 | int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu); | ||
| 145 | struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel); | ||
| 146 | struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel); | ||
| 147 | struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); | ||
| 148 | -- | ||
| 149 | |||
| 150 | *API to handle maps (perf ring buffers):* | ||
| 151 | |||
| 152 | [source,c] | ||
| 153 | -- | ||
| 154 | #include <perf/mmap.h> | ||
| 155 | |||
| 156 | struct perf_mmap; | ||
| 157 | |||
| 158 | void perf_mmap__consume(struct perf_mmap *map); | ||
| 159 | int perf_mmap__read_init(struct perf_mmap *map); | ||
| 160 | void perf_mmap__read_done(struct perf_mmap *map); | ||
| 161 | union perf_event *perf_mmap__read_event(struct perf_mmap *map); | ||
| 162 | -- | ||
| 163 | |||
| 164 | *Structures to access perf API events:* | ||
| 165 | |||
| 166 | [source,c] | ||
| 167 | -- | ||
| 168 | #include <perf/event.h> | ||
| 169 | |||
| 170 | struct perf_record_mmap; | ||
| 171 | struct perf_record_mmap2; | ||
| 172 | struct perf_record_comm; | ||
| 173 | struct perf_record_namespaces; | ||
| 174 | struct perf_record_fork; | ||
| 175 | struct perf_record_lost; | ||
| 176 | struct perf_record_lost_samples; | ||
| 177 | struct perf_record_read; | ||
| 178 | struct perf_record_throttle; | ||
| 179 | struct perf_record_ksymbol; | ||
| 180 | struct perf_record_bpf_event; | ||
| 181 | struct perf_record_sample; | ||
| 182 | struct perf_record_switch; | ||
| 183 | struct perf_record_header_attr; | ||
| 184 | struct perf_record_record_cpu_map; | ||
| 185 | struct perf_record_cpu_map_data; | ||
| 186 | struct perf_record_cpu_map; | ||
| 187 | struct perf_record_event_update_cpus; | ||
| 188 | struct perf_record_event_update_scale; | ||
| 189 | struct perf_record_event_update; | ||
| 190 | struct perf_trace_event_type; | ||
| 191 | struct perf_record_header_event_type; | ||
| 192 | struct perf_record_header_tracing_data; | ||
| 193 | struct perf_record_header_build_id; | ||
| 194 | struct perf_record_id_index; | ||
| 195 | struct perf_record_auxtrace_info; | ||
| 196 | struct perf_record_auxtrace; | ||
| 197 | struct perf_record_auxtrace_error; | ||
| 198 | struct perf_record_aux; | ||
| 199 | struct perf_record_itrace_start; | ||
| 200 | struct perf_record_thread_map_entry; | ||
| 201 | struct perf_record_thread_map; | ||
| 202 | struct perf_record_stat_config_entry; | ||
| 203 | struct perf_record_stat_config; | ||
| 204 | struct perf_record_stat; | ||
| 205 | struct perf_record_stat_round; | ||
| 206 | struct perf_record_time_conv; | ||
| 207 | struct perf_record_header_feature; | ||
| 208 | struct perf_record_compressed; | ||
| 209 | -- | ||
| 210 | |||
| 211 | DESCRIPTION | ||
| 212 | ----------- | ||
| 213 | The libperf library provides an API to access the linux kernel perf | ||
| 214 | events subsystem. | ||
| 215 | |||
| 216 | Following objects are key to the libperf interface: | ||
| 217 | |||
| 218 | [horizontal] | ||
| 219 | |||
| 220 | struct perf_cpu_map:: Provides a cpu list abstraction. | ||
| 221 | |||
| 222 | struct perf_thread_map:: Provides a thread list abstraction. | ||
| 223 | |||
| 224 | struct perf_evsel:: Provides an abstraction for single a perf event. | ||
| 225 | |||
| 226 | struct perf_evlist:: Gathers several struct perf_evsel object and performs functions on all of them. | ||
| 227 | |||
| 228 | struct perf_mmap:: Provides an abstraction for accessing perf ring buffer. | ||
| 229 | |||
| 230 | The exported API functions bind these objects together. | ||
| 231 | |||
| 232 | REPORTING BUGS | ||
| 233 | -------------- | ||
| 234 | Report bugs to <linux-perf-users@vger.kernel.org>. | ||
| 235 | |||
| 236 | LICENSE | ||
| 237 | ------- | ||
| 238 | libperf is Free Software licensed under the GNU LGPL 2.1 | ||
| 239 | |||
| 240 | RESOURCES | ||
| 241 | --------- | ||
| 242 | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git | ||
| 243 | |||
| 244 | SEE ALSO | ||
| 245 | -------- | ||
| 246 | libperf-sampling(7), libperf-counting(7) | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | <!-- manpage-1.72.xsl: | ||
| 2 | special settings for manpages rendered from asciidoc+docbook | ||
| 3 | handles peculiarities in docbook-xsl 1.72.0 --> | ||
| 4 | <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
| 5 | version="1.0"> | ||
| 6 | |||
| 7 | <xsl:import href="manpage-base.xsl"/> | ||
| 8 | |||
| 9 | <!-- these are the special values for the roff control characters | ||
| 10 | needed for docbook-xsl 1.72.0 --> | ||
| 11 | <xsl:param name="git.docbook.backslash">▓</xsl:param> | ||
| 12 | <xsl:param name="git.docbook.dot" >⌂</xsl:param> | ||
| 13 | |||
| 14 | </xsl:stylesheet> | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | <!-- manpage-base.xsl: | ||
| 2 | special formatting for manpages rendered from asciidoc+docbook --> | ||
| 3 | <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
| 4 | version="1.0"> | ||
| 5 | |||
| 6 | <!-- these params silence some output from xmlto --> | ||
| 7 | <xsl:param name="man.output.quietly" select="1"/> | ||
| 8 | <xsl:param name="refentry.meta.get.quietly" select="1"/> | ||
| 9 | |||
| 10 | <!-- convert asciidoc callouts to man page format; | ||
| 11 | git.docbook.backslash and git.docbook.dot params | ||
| 12 | must be supplied by another XSL file or other means --> | ||
| 13 | <xsl:template match="co"> | ||
| 14 | <xsl:value-of select="concat( | ||
| 15 | $git.docbook.backslash,'fB(', | ||
| 16 | substring-after(@id,'-'),')', | ||
| 17 | $git.docbook.backslash,'fR')"/> | ||
| 18 | </xsl:template> | ||
| 19 | <xsl:template match="calloutlist"> | ||
| 20 | <xsl:value-of select="$git.docbook.dot"/> | ||
| 21 | <xsl:text>sp </xsl:text> | ||
| 22 | <xsl:apply-templates/> | ||
| 23 | <xsl:text> </xsl:text> | ||
| 24 | </xsl:template> | ||
| 25 | <xsl:template match="callout"> | ||
| 26 | <xsl:value-of select="concat( | ||
| 27 | $git.docbook.backslash,'fB', | ||
| 28 | substring-after(@arearefs,'-'), | ||
| 29 | '. ',$git.docbook.backslash,'fR')"/> | ||
| 30 | <xsl:apply-templates/> | ||
| 31 | <xsl:value-of select="$git.docbook.dot"/> | ||
| 32 | <xsl:text>br </xsl:text> | ||
| 33 | </xsl:template> | ||
| 34 | |||
| 35 | </xsl:stylesheet> | ||
| @@ -0,0 +1,17 @@ | |||
| 1 | <!-- manpage-bold-literal.xsl: | ||
| 2 | special formatting for manpages rendered from asciidoc+docbook --> | ||
| 3 | <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
| 4 | version="1.0"> | ||
| 5 | |||
| 6 | <!-- render literal text as bold (instead of plain or monospace); | ||
| 7 | this makes literal text easier to distinguish in manpages | ||
| 8 | viewed on a tty --> | ||
| 9 | <xsl:template match="literal"> | ||
| 10 | <xsl:value-of select="$git.docbook.backslash"/> | ||
| 11 | <xsl:text>fB</xsl:text> | ||
| 12 | <xsl:apply-templates/> | ||
| 13 | <xsl:value-of select="$git.docbook.backslash"/> | ||
| 14 | <xsl:text>fR</xsl:text> | ||
| 15 | </xsl:template> | ||
| 16 | |||
| 17 | </xsl:stylesheet> | ||
| @@ -0,0 +1,13 @@ | |||
| 1 | <!-- manpage-normal.xsl: | ||
| 2 | special settings for manpages rendered from asciidoc+docbook | ||
| 3 | handles anything we want to keep away from docbook-xsl 1.72.0 --> | ||
| 4 | <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
| 5 | version="1.0"> | ||
| 6 | |||
| 7 | <xsl:import href="manpage-base.xsl"/> | ||
| 8 | |||
| 9 | <!-- these are the normal values for the roff control characters --> | ||
| 10 | <xsl:param name="git.docbook.backslash">\</xsl:param> | ||
| 11 | <xsl:param name="git.docbook.dot" >.</xsl:param> | ||
| 12 | |||
| 13 | </xsl:stylesheet> | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | <!-- manpage-suppress-sp.xsl: | ||
| 2 | special settings for manpages rendered from asciidoc+docbook | ||
| 3 | handles erroneous, inline .sp in manpage output of some | ||
| 4 | versions of docbook-xsl --> | ||
| 5 | <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
| 6 | version="1.0"> | ||
| 7 | |||
| 8 | <!-- attempt to work around spurious .sp at the tail of the line | ||
| 9 | that some versions of docbook stylesheets seem to add --> | ||
| 10 | <xsl:template match="simpara"> | ||
| 11 | <xsl:variable name="content"> | ||
| 12 | <xsl:apply-templates/> | ||
| 13 | </xsl:variable> | ||
| 14 | <xsl:value-of select="normalize-space($content)"/> | ||
| 15 | <xsl:if test="not(ancestor::authorblurb) and | ||
| 16 | not(ancestor::personblurb)"> | ||
| 17 | <xsl:text> </xsl:text> | ||
| 18 | </xsl:if> | ||
| 19 | </xsl:template> | ||
| 20 | |||
| 21 | </xsl:stylesheet> | ||
| @@ -60,7 +60,7 @@ else · 59 unmodified lines | |||
| 60 | endif | 60 | endif |
| 61 | 61 | ||
| 62 | INCLUDES = \ | 62 | INCLUDES = \ |
| 63 | -I$(srctree)/tools/perf/lib/include \ | 63 | -I$(srctree)/tools/lib/perf/include \ |
| 64 | -I$(srctree)/tools/lib/ \ | 64 | -I$(srctree)/tools/lib/ \ |
| 65 | -I$(srctree)/tools/include \ | 65 | -I$(srctree)/tools/include \ |
| 66 | -I$(srctree)/tools/arch/$(SRCARCH)/include/ \ | 66 | -I$(srctree)/tools/arch/$(SRCARCH)/include/ \ |
| @@ -181,7 +181,10 @@ install_pkgconfig: $(LIBPERF_PC) · 114 unmodified lines | |||
| 181 | $(call QUIET_INSTALL, $(LIBPERF_PC)) \ | 181 | $(call QUIET_INSTALL, $(LIBPERF_PC)) \ |
| 182 | $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644) | 182 | $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644) |
| 183 | 183 | ||
| 184 | install: install_lib install_headers install_pkgconfig | 184 | install_doc: |
| 185 | $(Q)$(MAKE) -C Documentation install-man install-html install-examples | ||
| 186 | |||
| 187 | install: install_lib install_headers install_pkgconfig install_doc | ||
| 185 | 188 | ||
| 186 | FORCE: | 189 | FORCE: |
| 187 | 190 | ||
File renamed without changes.
File renamed without changes.
| @@ -164,6 +164,9 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, · 163 unmodified lines | |||
| 164 | evlist->threads = perf_thread_map__get(threads); | 164 | evlist->threads = perf_thread_map__get(threads); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | if (!evlist->all_cpus && cpus) | ||
| 168 | evlist->all_cpus = perf_cpu_map__get(cpus); | ||
| 169 | |||
| 167 | perf_evlist__propagate_maps(evlist); | 170 | perf_evlist__propagate_maps(evlist); |
| 168 | } | 171 | } |
| 169 | 172 | ||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
| @@ -16,7 +16,7 @@ all: · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | include $(srctree)/tools/scripts/Makefile.include | 17 | include $(srctree)/tools/scripts/Makefile.include |
| 18 | 18 | ||
| 19 | INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/lib | 19 | INCLUDE = -I$(srctree)/tools/lib/perf/include -I$(srctree)/tools/include -I$(srctree)/tools/lib |
| 20 | 20 | ||
| 21 | $(TESTS_A): FORCE | 21 | $(TESTS_A): FORCE |
| 22 | $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) | 22 | $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
| @@ -96,6 +96,10 @@ int strtobool(const char *s, bool *res) · 95 unmodified lines | |||
| 96 | * If libc has strlcpy() then that version will override this | 96 | * If libc has strlcpy() then that version will override this |
| 97 | * implementation: | 97 | * implementation: |
| 98 | */ | 98 | */ |
| 99 | #ifdef __clang__ | ||
| 100 | #pragma clang diagnostic push | ||
| 101 | #pragma clang diagnostic ignored "-Wignored-attributes" | ||
| 102 | #endif | ||
| 99 | size_t __weak strlcpy(char *dest, const char *src, size_t size) | 103 | size_t __weak strlcpy(char *dest, const char *src, size_t size) |
| 100 | { | 104 | { |
| 101 | size_t ret = strlen(src); | 105 | size_t ret = strlen(src); |
| @@ -107,6 +111,9 @@ size_t __weak strlcpy(char *dest, const char *src, size_t size) · 5 unmodified lines | |||
| 107 | } | 111 | } |
| 108 | return ret; | 112 | return ret; |
| 109 | } | 113 | } |
| 114 | #ifdef __clang__ | ||
| 115 | #pragma clang diagnostic pop | ||
| 116 | #endif | ||
| 110 | 117 | ||
| 111 | /** | 118 | /** |
| 112 | * skip_spaces - Removes leading whitespace from @str. | 119 | * skip_spaces - Removes leading whitespace from @str. |
| @@ -112,6 +112,12 @@ OPTIONS · 111 unmodified lines | |||
| 112 | --objdump=<path>:: | 112 | --objdump=<path>:: |
| 113 | Path to objdump binary. | 113 | Path to objdump binary. |
| 114 | 114 | ||
| 115 | --prefix=PREFIX:: | ||
| 116 | --prefix-strip=N:: | ||
| 117 | Remove first N entries from source file path names in executables | ||
| 118 | and add PREFIX. This allows to display source code compiled on systems | ||
| 119 | with different file system layout. | ||
| 120 | |||
| 115 | --skip-missing:: | 121 | --skip-missing:: |
| 116 | Skip symbols that cannot be annotated. | 122 | Skip symbols that cannot be annotated. |
| 117 | 123 | ||
| @@ -367,6 +367,12 @@ OPTIONS · 366 unmodified lines | |||
| 367 | --objdump=<path>:: | 367 | --objdump=<path>:: |
| 368 | Path to objdump binary. | 368 | Path to objdump binary. |
| 369 | 369 | ||
| 370 | --prefix=PREFIX:: | ||
| 371 | --prefix-strip=N:: | ||
| 372 | Remove first N entries from source file path names in executables | ||
| 373 | and add PREFIX. This allows to display source code compiled on systems | ||
| 374 | with different file system layout. | ||
| 375 | |||
| 370 | --group:: | 376 | --group:: |
| 371 | Show event group information together. It forces group output also | 377 | Show event group information together. It forces group output also |
| 372 | if there are no groups defined in data file. | 378 | if there are no groups defined in data file. |
| @@ -110,6 +110,10 @@ OPTIONS for 'perf sched timehist' · 109 unmodified lines | |||
| 110 | --max-stack:: | 110 | --max-stack:: |
| 111 | Maximum number of functions to display in backtrace, default 5. | 111 | Maximum number of functions to display in backtrace, default 5. |
| 112 | 112 | ||
| 113 | -C=:: | ||
| 114 | --cpu=:: | ||
| 115 | Only show events for the given CPU(s) (comma separated list). | ||
| 116 | |||
| 113 | -p=:: | 117 | -p=:: |
| 114 | --pid=:: | 118 | --pid=:: |
| 115 | Only show events for given process ID (comma separated list). | 119 | Only show events for given process ID (comma separated list). |
| @@ -158,6 +158,12 @@ Default is to monitor all CPUS. · 157 unmodified lines | |||
| 158 | -M:: | 158 | -M:: |
| 159 | --disassembler-style=:: Set disassembler style for objdump. | 159 | --disassembler-style=:: Set disassembler style for objdump. |
| 160 | 160 | ||
| 161 | --prefix=PREFIX:: | ||
| 162 | --prefix-strip=N:: | ||
| 163 | Remove first N entries from source file path names in executables | ||
| 164 | and add PREFIX. This allows to display source code compiled on systems | ||
| 165 | with different file system layout. | ||
| 166 | |||
| 161 | --source:: | 167 | --source:: |
| 162 | Interleave source code with assembly code. Enabled by default, | 168 | Interleave source code with assembly code. Enabled by default, |
| 163 | disable with --no-source. | 169 | disable with --no-source. |
| @@ -7,6 +7,7 @@ tools/lib/traceevent · 6 unmodified lines | |||
| 7 | tools/lib/api | 7 | tools/lib/api |
| 8 | tools/lib/bpf | 8 | tools/lib/bpf |
| 9 | tools/lib/subcmd | 9 | tools/lib/subcmd |
| 10 | tools/lib/perf | ||
| 10 | tools/lib/argv_split.c | 11 | tools/lib/argv_split.c |
| 11 | tools/lib/ctype.c | 12 | tools/lib/ctype.c |
| 12 | tools/lib/hweight.c | 13 | tools/lib/hweight.c |
| @@ -286,7 +286,7 @@ ifeq ($(DEBUG),0) · 285 unmodified lines | |||
| 286 | endif | 286 | endif |
| 287 | endif | 287 | endif |
| 288 | 288 | ||
| 289 | INC_FLAGS += -I$(src-perf)/lib/include | 289 | INC_FLAGS += -I$(srctree)/tools/lib/perf/include |
| 290 | INC_FLAGS += -I$(src-perf)/util/include | 290 | INC_FLAGS += -I$(src-perf)/util/include |
| 291 | INC_FLAGS += -I$(src-perf)/arch/$(SRCARCH)/include | 291 | INC_FLAGS += -I$(src-perf)/arch/$(SRCARCH)/include |
| 292 | INC_FLAGS += -I$(srctree)/tools/include/ | 292 | INC_FLAGS += -I$(srctree)/tools/include/ |
| @@ -230,7 +230,7 @@ LIB_DIR = $(srctree)/tools/lib/api/ · 229 unmodified lines | |||
| 230 | TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ | 230 | TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ |
| 231 | BPF_DIR = $(srctree)/tools/lib/bpf/ | 231 | BPF_DIR = $(srctree)/tools/lib/bpf/ |
| 232 | SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ | 232 | SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ |
| 233 | LIBPERF_DIR = $(srctree)/tools/perf/lib/ | 233 | LIBPERF_DIR = $(srctree)/tools/lib/perf/ |
| 234 | 234 | ||
| 235 | # Set FEATURE_TESTS to 'all' so all possible feature checkers are executed. | 235 | # Set FEATURE_TESTS to 'all' so all possible feature checkers are executed. |
| 236 | # Without this setting the output feature dump file misses some features, for | 236 | # Without this setting the output feature dump file misses some features, for |
| @@ -535,6 +535,10 @@ int cmd_annotate(int argc, const char **argv) · 534 unmodified lines | |||
| 535 | "Display raw encoding of assembly instructions (default)"), | 535 | "Display raw encoding of assembly instructions (default)"), |
| 536 | OPT_STRING('M', "disassembler-style", &annotate.opts.disassembler_style, "disassembler style", | 536 | OPT_STRING('M', "disassembler-style", &annotate.opts.disassembler_style, "disassembler style", |
| 537 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 537 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
| 538 | OPT_STRING(0, "prefix", &annotate.opts.prefix, "prefix", | ||
| 539 | "Add prefix to source file path names in programs (with --prefix-strip)"), | ||
| 540 | OPT_STRING(0, "prefix-strip", &annotate.opts.prefix_strip, "N", | ||
| 541 | "Strip first N entries of source file path name in programs (with --prefix)"), | ||
| 538 | OPT_STRING(0, "objdump", &annotate.opts.objdump_path, "path", | 542 | OPT_STRING(0, "objdump", &annotate.opts.objdump_path, "path", |
| 539 | "objdump binary to use for disassembly and annotations"), | 543 | "objdump binary to use for disassembly and annotations"), |
| 540 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, | 544 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, |
| @@ -574,6 +578,9 @@ int cmd_annotate(int argc, const char **argv) · 33 unmodified lines | |||
| 574 | annotate.sym_hist_filter = argv[0]; | 578 | annotate.sym_hist_filter = argv[0]; |
| 575 | } | 579 | } |
| 576 | 580 | ||
| 581 | if (annotate_check_args(&annotate.opts) < 0) | ||
| 582 | return -EINVAL; | ||
| 583 | |||
| 577 | if (symbol_conf.show_nr_samples && annotate.use_gtk) { | 584 | if (symbol_conf.show_nr_samples && annotate.use_gtk) { |
| 578 | pr_err("--show-nr-samples is not available in --gtk mode at this time\n"); | 585 | pr_err("--show-nr-samples is not available in --gtk mode at this time\n"); |
| 579 | return ret; | 586 | return ret; |
| @@ -595,8 +595,8 @@ tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused, · 594 unmodified lines | |||
| 595 | { | 595 | { |
| 596 | struct c2c_hist_entry *c2c_left; | 596 | struct c2c_hist_entry *c2c_left; |
| 597 | struct c2c_hist_entry *c2c_right; | 597 | struct c2c_hist_entry *c2c_right; |
| 598 | unsigned int tot_hitm_left; | 598 | uint64_t tot_hitm_left; |
| 599 | unsigned int tot_hitm_right; | 599 | uint64_t tot_hitm_right; |
| 600 | 600 | ||
| 601 | c2c_left = container_of(left, struct c2c_hist_entry, he); | 601 | c2c_left = container_of(left, struct c2c_hist_entry, he); |
| 602 | c2c_right = container_of(right, struct c2c_hist_entry, he); | 602 | c2c_right = container_of(right, struct c2c_hist_entry, he); |
| @@ -629,7 +629,8 @@ __f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \ · 26 unmodified lines | |||
| 629 | \ | 629 | \ |
| 630 | c2c_left = container_of(left, struct c2c_hist_entry, he); \ | 630 | c2c_left = container_of(left, struct c2c_hist_entry, he); \ |
| 631 | c2c_right = container_of(right, struct c2c_hist_entry, he); \ | 631 | c2c_right = container_of(right, struct c2c_hist_entry, he); \ |
| 632 | return c2c_left->stats.__f - c2c_right->stats.__f; \ | 632 | return (uint64_t) c2c_left->stats.__f - \ |
| 633 | (uint64_t) c2c_right->stats.__f; \ | ||
| 633 | } | 634 | } |
| 634 | 635 | ||
| 635 | #define STAT_FN(__f) \ | 636 | #define STAT_FN(__f) \ |
| @@ -682,7 +683,8 @@ ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused, · 46 unmodified lines | |||
| 682 | c2c_left = container_of(left, struct c2c_hist_entry, he); | 683 | c2c_left = container_of(left, struct c2c_hist_entry, he); |
| 683 | c2c_right = container_of(right, struct c2c_hist_entry, he); | 684 | c2c_right = container_of(right, struct c2c_hist_entry, he); |
| 684 | 685 | ||
| 685 | return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats); | 686 | return (uint64_t) llc_miss(&c2c_left->stats) - |
| 687 | (uint64_t) llc_miss(&c2c_right->stats); | ||
| 686 | } | 688 | } |
| 687 | 689 | ||
| 688 | static uint64_t total_records(struct c2c_stats *stats) | 690 | static uint64_t total_records(struct c2c_stats *stats) |
| @@ -2384,7 +2386,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) · 1695 unmodified lines | |||
| 2384 | c2c_browser__update_nr_entries(browser); | 2386 | c2c_browser__update_nr_entries(browser); |
| 2385 | 2387 | ||
| 2386 | while (1) { | 2388 | while (1) { |
| 2387 | key = hist_browser__run(browser, "? - help", true); | 2389 | key = hist_browser__run(browser, "? - help", true, 0); |
| 2388 | 2390 | ||
| 2389 | switch (key) { | 2391 | switch (key) { |
| 2390 | case 's': | 2392 | case 's': |
| @@ -2453,7 +2455,7 @@ static int perf_c2c__hists_browse(struct hists *hists) · 62 unmodified lines | |||
| 2453 | c2c_browser__update_nr_entries(browser); | 2455 | c2c_browser__update_nr_entries(browser); |
| 2454 | 2456 | ||
| 2455 | while (1) { | 2457 | while (1) { |
| 2456 | key = hist_browser__run(browser, "? - help", true); | 2458 | key = hist_browser__run(browser, "? - help", true, 0); |
| 2457 | 2459 | ||
| 2458 | switch (key) { | 2460 | switch (key) { |
| 2459 | case 'q': | 2461 | case 'q': |
| @@ -62,6 +62,7 @@ · 61 unmodified lines | |||
| 62 | #include <linux/string.h> | 62 | #include <linux/string.h> |
| 63 | #include <linux/time64.h> | 63 | #include <linux/time64.h> |
| 64 | #include <linux/zalloc.h> | 64 | #include <linux/zalloc.h> |
| 65 | #include <linux/bitmap.h> | ||
| 65 | 66 | ||
| 66 | struct switch_output { | 67 | struct switch_output { |
| 67 | bool enabled; | 68 | bool enabled; |
| @@ -93,7 +94,7 @@ struct record { · 25 unmodified lines | |||
| 93 | bool timestamp_boundary; | 94 | bool timestamp_boundary; |
| 94 | struct switch_output switch_output; | 95 | struct switch_output switch_output; |
| 95 | unsigned long long samples; | 96 | unsigned long long samples; |
| 96 | cpu_set_t affinity_mask; | 97 | struct mmap_cpu_mask affinity_mask; |
| 97 | unsigned long output_max_size; /* = 0: unlimited */ | 98 | unsigned long output_max_size; /* = 0: unlimited */ |
| 98 | }; | 99 | }; |
| 99 | 100 | ||
| @@ -961,10 +962,15 @@ static struct perf_event_header finished_round_event = { · 861 unmodified lines | |||
| 961 | static void record__adjust_affinity(struct record *rec, struct mmap *map) | 962 | static void record__adjust_affinity(struct record *rec, struct mmap *map) |
| 962 | { | 963 | { |
| 963 | if (rec->opts.affinity != PERF_AFFINITY_SYS && | 964 | if (rec->opts.affinity != PERF_AFFINITY_SYS && |
| 964 | !CPU_EQUAL(&rec->affinity_mask, &map->affinity_mask)) { | 965 | !bitmap_equal(rec->affinity_mask.bits, map->affinity_mask.bits, |
| 965 | CPU_ZERO(&rec->affinity_mask); | 966 | rec->affinity_mask.nbits)) { |
| 966 | CPU_OR(&rec->affinity_mask, &rec->affinity_mask, &map->affinity_mask); | 967 | bitmap_zero(rec->affinity_mask.bits, rec->affinity_mask.nbits); |
| 967 | sched_setaffinity(0, sizeof(rec->affinity_mask), &rec->affinity_mask); | 968 | bitmap_or(rec->affinity_mask.bits, rec->affinity_mask.bits, |
| 969 | map->affinity_mask.bits, rec->affinity_mask.nbits); | ||
| 970 | sched_setaffinity(0, MMAP_CPU_MASK_BYTES(&rec->affinity_mask), | ||
| 971 | (cpu_set_t *)rec->affinity_mask.bits); | ||
| 972 | if (verbose == 2) | ||
| 973 | mmap_cpu_mask__scnprintf(&rec->affinity_mask, "thread"); | ||
| 968 | } | 974 | } |
| 969 | } | 975 | } |
| 970 | 976 | ||
| @@ -2433,7 +2439,6 @@ int cmd_record(int argc, const char **argv) · 1462 unmodified lines | |||
| 2433 | # undef REASON | 2439 | # undef REASON |
| 2434 | #endif | 2440 | #endif |
| 2435 | 2441 | ||
| 2436 | CPU_ZERO(&rec->affinity_mask); | ||
| 2437 | rec->opts.affinity = PERF_AFFINITY_SYS; | 2442 | rec->opts.affinity = PERF_AFFINITY_SYS; |
| 2438 | 2443 | ||
| 2439 | rec->evlist = evlist__new(); | 2444 | rec->evlist = evlist__new(); |
| @@ -2499,6 +2504,16 @@ int cmd_record(int argc, const char **argv) · 59 unmodified lines | |||
| 2499 | 2504 | ||
| 2500 | symbol__init(NULL); | 2505 | symbol__init(NULL); |
| 2501 | 2506 | ||
| 2507 | if (rec->opts.affinity != PERF_AFFINITY_SYS) { | ||
| 2508 | rec->affinity_mask.nbits = cpu__max_cpu(); | ||
| 2509 | rec->affinity_mask.bits = bitmap_alloc(rec->affinity_mask.nbits); | ||
| 2510 | if (!rec->affinity_mask.bits) { | ||
| 2511 | pr_err("Failed to allocate thread mask for %zd cpus\n", rec->affinity_mask.nbits); | ||
| 2512 | return -ENOMEM; | ||
| 2513 | } | ||
| 2514 | pr_debug2("thread mask[%zd]: empty\n", rec->affinity_mask.nbits); | ||
| 2515 | } | ||
| 2516 | |||
| 2502 | err = record__auxtrace_init(rec); | 2517 | err = record__auxtrace_init(rec); |
| 2503 | if (err) | 2518 | if (err) |
| 2504 | goto out; | 2519 | goto out; |
| @@ -2613,6 +2628,7 @@ int cmd_record(int argc, const char **argv) · 108 unmodified lines | |||
| 2613 | 2628 | ||
| 2614 | err = __cmd_record(&record, argc, argv); | 2629 | err = __cmd_record(&record, argc, argv); |
| 2615 | out: | 2630 | out: |
| 2631 | bitmap_free(rec->affinity_mask.bits); | ||
| 2616 | evlist__delete(rec->evlist); | 2632 | evlist__delete(rec->evlist); |
| 2617 | symbol__exit(); | 2633 | symbol__exit(); |
| 2618 | auxtrace_record__free(rec->itr); | 2634 | auxtrace_record__free(rec->itr); |
| @@ -412,10 +412,10 @@ static int report__setup_sample_type(struct report *rep) · 411 unmodified lines | |||
| 412 | PERF_SAMPLE_BRANCH_ANY)) | 412 | PERF_SAMPLE_BRANCH_ANY)) |
| 413 | rep->nonany_branch_mode = true; | 413 | rep->nonany_branch_mode = true; |
| 414 | 414 | ||
| 415 | #ifndef HAVE_LIBUNWIND_SUPPORT | 415 | #if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT) |
| 416 | if (dwarf_callchain_users) { | 416 | if (dwarf_callchain_users) { |
| 417 | ui__warning("Please install libunwind development packages " | 417 | ui__warning("Please install libunwind or libdw " |
| 418 | "during the perf build.\n"); | 418 | "development packages during the perf build.\n"); |
| 419 | } | 419 | } |
| 420 | #endif | 420 | #endif |
| 421 | 421 | ||
| @@ -1164,7 +1164,8 @@ int cmd_report(int argc, const char **argv) · 742 unmodified lines | |||
| 1164 | report_callchain_help, &report_parse_callchain_opt, | 1164 | report_callchain_help, &report_parse_callchain_opt, |
| 1165 | callchain_default_opt), | 1165 | callchain_default_opt), |
| 1166 | OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, | 1166 | OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, |
| 1167 | "Accumulate callchains of children and show total overhead as well"), | 1167 | "Accumulate callchains of children and show total overhead as well. " |
| 1168 | "Enabled by default, use --no-children to disable."), | ||
| 1168 | OPT_INTEGER(0, "max-stack", &report.max_stack, | 1169 | OPT_INTEGER(0, "max-stack", &report.max_stack, |
| 1169 | "Set the maximum stack depth when parsing the callchain, " | 1170 | "Set the maximum stack depth when parsing the callchain, " |
| 1170 | "anything beyond the specified depth will be ignored. " | 1171 | "anything beyond the specified depth will be ignored. " |
| @@ -1207,6 +1208,10 @@ int cmd_report(int argc, const char **argv) · 36 unmodified lines | |||
| 1207 | "Display raw encoding of assembly instructions (default)"), | 1208 | "Display raw encoding of assembly instructions (default)"), |
| 1208 | OPT_STRING('M', "disassembler-style", &report.annotation_opts.disassembler_style, "disassembler style", | 1209 | OPT_STRING('M', "disassembler-style", &report.annotation_opts.disassembler_style, "disassembler style", |
| 1209 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1210 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
| 1211 | OPT_STRING(0, "prefix", &report.annotation_opts.prefix, "prefix", | ||
| 1212 | "Add prefix to source file path names in programs (with --prefix-strip)"), | ||
| 1213 | OPT_STRING(0, "prefix-strip", &report.annotation_opts.prefix_strip, "N", | ||
| 1214 | "Strip first N entries of source file path name in programs (with --prefix)"), | ||
| 1210 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 1215 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
| 1211 | "Show a column with the sum of periods"), | 1216 | "Show a column with the sum of periods"), |
| 1212 | OPT_BOOLEAN_SET(0, "group", &symbol_conf.event_group, &report.group_set, | 1217 | OPT_BOOLEAN_SET(0, "group", &symbol_conf.event_group, &report.group_set, |
| @@ -1286,6 +1291,9 @@ int cmd_report(int argc, const char **argv) · 73 unmodified lines | |||
| 1286 | report.symbol_filter_str = argv[0]; | 1291 | report.symbol_filter_str = argv[0]; |
| 1287 | } | 1292 | } |
| 1288 | 1293 | ||
| 1294 | if (annotate_check_args(&report.annotation_opts) < 0) | ||
| 1295 | return -EINVAL; | ||
| 1296 | |||
| 1289 | if (report.mmaps_mode) | 1297 | if (report.mmaps_mode) |
| 1290 | report.tasks_mode = true; | 1298 | report.tasks_mode = true; |
| 1291 | 1299 | ||
| @@ -51,6 +51,9 @@ · 50 unmodified lines | |||
| 51 | #define SYM_LEN 129 | 51 | #define SYM_LEN 129 |
| 52 | #define MAX_PID 1024000 | 52 | #define MAX_PID 1024000 |
| 53 | 53 | ||
| 54 | static const char *cpu_list; | ||
| 55 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
| 56 | |||
| 54 | struct sched_atom; | 57 | struct sched_atom; |
| 55 | 58 | ||
| 56 | struct task_desc { | 59 | struct task_desc { |
| @@ -2008,6 +2011,9 @@ static void timehist_print_sample(struct perf_sched *sched, · 1951 unmodified lines | |||
| 2008 | char nstr[30]; | 2011 | char nstr[30]; |
| 2009 | u64 wait_time; | 2012 | u64 wait_time; |
| 2010 | 2013 | ||
| 2014 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
| 2015 | return; | ||
| 2016 | |||
| 2011 | timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); | 2017 | timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); |
| 2012 | printf("%15s [%04d] ", tstr, sample->cpu); | 2018 | printf("%15s [%04d] ", tstr, sample->cpu); |
| 2013 | 2019 | ||
| @@ -2994,6 +3000,12 @@ static int perf_sched__timehist(struct perf_sched *sched) · 980 unmodified lines | |||
| 2994 | if (IS_ERR(session)) | 3000 | if (IS_ERR(session)) |
| 2995 | return PTR_ERR(session); | 3001 | return PTR_ERR(session); |
| 2996 | 3002 | ||
| 3003 | if (cpu_list) { | ||
| 3004 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | ||
| 3005 | if (err < 0) | ||
| 3006 | goto out; | ||
| 3007 | } | ||
| 3008 | |||
| 2997 | evlist = session->evlist; | 3009 | evlist = session->evlist; |
| 2998 | 3010 | ||
| 2999 | symbol__init(&session->header.env); | 3011 | symbol__init(&session->header.env); |
| @@ -3429,6 +3441,7 @@ int cmd_sched(int argc, const char **argv) · 429 unmodified lines | |||
| 3429 | "analyze events only for given process id(s)"), | 3441 | "analyze events only for given process id(s)"), |
| 3430 | OPT_STRING('t', "tid", &symbol_conf.tid_list_str, "tid[,tid...]", | 3442 | OPT_STRING('t', "tid", &symbol_conf.tid_list_str, "tid[,tid...]", |
| 3431 | "analyze events only for given thread id(s)"), | 3443 | "analyze events only for given thread id(s)"), |
| 3444 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
| 3432 | OPT_PARENT(sched_options) | 3445 | OPT_PARENT(sched_options) |
| 3433 | }; | 3446 | }; |
| 3434 | 3447 | ||
| @@ -1512,6 +1512,10 @@ int cmd_top(int argc, const char **argv) · 1511 unmodified lines | |||
| 1512 | "objdump binary to use for disassembly and annotations"), | 1512 | "objdump binary to use for disassembly and annotations"), |
| 1513 | OPT_STRING('M', "disassembler-style", &top.annotation_opts.disassembler_style, "disassembler style", | 1513 | OPT_STRING('M', "disassembler-style", &top.annotation_opts.disassembler_style, "disassembler style", |
| 1514 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1514 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
| 1515 | OPT_STRING(0, "prefix", &top.annotation_opts.prefix, "prefix", | ||
| 1516 | "Add prefix to source file path names in programs (with --prefix-strip)"), | ||
| 1517 | OPT_STRING(0, "prefix-strip", &top.annotation_opts.prefix_strip, "N", | ||
| 1518 | "Strip first N entries of source file path name in programs (with --prefix)"), | ||
| 1515 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), | 1519 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), |
| 1516 | OPT_CALLBACK(0, "percent-limit", &top, "percent", | 1520 | OPT_CALLBACK(0, "percent-limit", &top, "percent", |
| 1517 | "Don't show entries under that percent", parse_percent_limit), | 1521 | "Don't show entries under that percent", parse_percent_limit), |
| @@ -1582,6 +1586,9 @@ int cmd_top(int argc, const char **argv) · 64 unmodified lines | |||
| 1582 | if (argc) | 1586 | if (argc) |
| 1583 | usage_with_options(top_usage, options); | 1587 | usage_with_options(top_usage, options); |
| 1584 | 1588 | ||
| 1589 | if (annotate_check_args(&top.annotation_opts) < 0) | ||
| 1590 | goto out_delete_evlist; | ||
| 1591 | |||
| 1585 | if (!top.evlist->core.nr_entries && | 1592 | if (!top.evlist->core.nr_entries && |
| 1586 | perf_evlist__add_default(top.evlist) < 0) { | 1593 | perf_evlist__add_default(top.evlist) < 0) { |
| 1587 | pr_err("Not enough memory for event selector list\n"); | 1594 | pr_err("Not enough memory for event selector list\n"); |
| @@ -1,7 +0,0 @@ | |||
| 1 | all: | ||
| 2 | rst2man man/libperf.rst > man/libperf.7 | ||
| 3 | rst2pdf tutorial/tutorial.rst | ||
| 4 | |||
| 5 | clean: | ||
| 6 | rm -f man/libperf.7 | ||
| 7 | rm -f tutorial/tutorial.pdf | ||
| @@ -1,100 +0,0 @@ | |||
| 1 | .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | ||
| 2 | |||
| 3 | libperf | ||
| 4 | |||
| 5 | The libperf library provides an API to access the linux kernel perf | ||
| 6 | events subsystem. It provides the following high level objects: | ||
| 7 | |||
| 8 | - struct perf_cpu_map | ||
| 9 | - struct perf_thread_map | ||
| 10 | - struct perf_evlist | ||
| 11 | - struct perf_evsel | ||
| 12 | |||
| 13 | reference | ||
| 14 | ========= | ||
| 15 | Function reference by header files: | ||
| 16 | |||
| 17 | perf/core.h | ||
| 18 | ----------- | ||
| 19 | .. code-block:: c | ||
| 20 | |||
| 21 | typedef int (\*libperf_print_fn_t)(enum libperf_print_level level, | ||
| 22 | const char \*, va_list ap); | ||
| 23 | |||
| 24 | void libperf_set_print(libperf_print_fn_t fn); | ||
| 25 | |||
| 26 | perf/cpumap.h | ||
| 27 | ------------- | ||
| 28 | .. code-block:: c | ||
| 29 | |||
| 30 | struct perf_cpu_map \*perf_cpu_map__dummy_new(void); | ||
| 31 | struct perf_cpu_map \*perf_cpu_map__new(const char \*cpu_list); | ||
| 32 | struct perf_cpu_map \*perf_cpu_map__read(FILE \*file); | ||
| 33 | struct perf_cpu_map \*perf_cpu_map__get(struct perf_cpu_map \*map); | ||
| 34 | void perf_cpu_map__put(struct perf_cpu_map \*map); | ||
| 35 | int perf_cpu_map__cpu(const struct perf_cpu_map \*cpus, int idx); | ||
| 36 | int perf_cpu_map__nr(const struct perf_cpu_map \*cpus); | ||
| 37 | perf_cpu_map__for_each_cpu(cpu, idx, cpus) | ||
| 38 | |||
| 39 | perf/threadmap.h | ||
| 40 | ---------------- | ||
| 41 | .. code-block:: c | ||
| 42 | |||
| 43 | struct perf_thread_map \*perf_thread_map__new_dummy(void); | ||
| 44 | void perf_thread_map__set_pid(struct perf_thread_map \*map, int thread, pid_t pid); | ||
| 45 | char \*perf_thread_map__comm(struct perf_thread_map \*map, int thread); | ||
| 46 | struct perf_thread_map \*perf_thread_map__get(struct perf_thread_map \*map); | ||
| 47 | void perf_thread_map__put(struct perf_thread_map \*map); | ||
| 48 | |||
| 49 | perf/evlist.h | ||
| 50 | ------------- | ||
| 51 | .. code-block:: | ||
| 52 | |||
| 53 | void perf_evlist__init(struct perf_evlist \*evlist); | ||
| 54 | void perf_evlist__add(struct perf_evlist \*evlist, | ||
| 55 | struct perf_evsel \*evsel); | ||
| 56 | void perf_evlist__remove(struct perf_evlist \*evlist, | ||
| 57 | struct perf_evsel \*evsel); | ||
| 58 | struct perf_evlist \*perf_evlist__new(void); | ||
| 59 | void perf_evlist__delete(struct perf_evlist \*evlist); | ||
| 60 | struct perf_evsel\* perf_evlist__next(struct perf_evlist \*evlist, | ||
| 61 | struct perf_evsel \*evsel); | ||
| 62 | int perf_evlist__open(struct perf_evlist \*evlist); | ||
| 63 | void perf_evlist__close(struct perf_evlist \*evlist); | ||
| 64 | void perf_evlist__enable(struct perf_evlist \*evlist); | ||
| 65 | void perf_evlist__disable(struct perf_evlist \*evlist); | ||
| 66 | perf_evlist__for_each_evsel(evlist, pos) | ||
| 67 | void perf_evlist__set_maps(struct perf_evlist \*evlist, | ||
| 68 | struct perf_cpu_map \*cpus, | ||
| 69 | struct perf_thread_map \*threads); | ||
| 70 | |||
| 71 | perf/evsel.h | ||
| 72 | ------------ | ||
| 73 | .. code-block:: c | ||
| 74 | |||
| 75 | struct perf_counts_values { | ||
| 76 | union { | ||
| 77 | struct { | ||
| 78 | uint64_t val; | ||
| 79 | uint64_t ena; | ||
| 80 | uint64_t run; | ||
| 81 | }; | ||
| 82 | uint64_t values[3]; | ||
| 83 | }; | ||
| 84 | }; | ||
| 85 | |||
| 86 | void perf_evsel__init(struct perf_evsel \*evsel, | ||
| 87 | struct perf_event_attr \*attr); | ||
| 88 | struct perf_evsel \*perf_evsel__new(struct perf_event_attr \*attr); | ||
| 89 | void perf_evsel__delete(struct perf_evsel \*evsel); | ||
| 90 | int perf_evsel__open(struct perf_evsel \*evsel, struct perf_cpu_map \*cpus, | ||
| 91 | struct perf_thread_map \*threads); | ||
| 92 | void perf_evsel__close(struct perf_evsel \*evsel); | ||
| 93 | int perf_evsel__read(struct perf_evsel \*evsel, int cpu, int thread, | ||
| 94 | struct perf_counts_values \*count); | ||
| 95 | int perf_evsel__enable(struct perf_evsel \*evsel); | ||
| 96 | int perf_evsel__disable(struct perf_evsel \*evsel); | ||
| 97 | int perf_evsel__apply_filter(struct perf_evsel \*evsel, const char \*filter); | ||
| 98 | struct perf_cpu_map \*perf_evsel__cpus(struct perf_evsel \*evsel); | ||
| 99 | struct perf_thread_map \*perf_evsel__threads(struct perf_evsel \*evsel); | ||
| 100 | struct perf_event_attr \*perf_evsel__attr(struct perf_evsel \*evsel); | ||
| @@ -1,123 +0,0 @@ | |||
| 1 | .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | ||
| 2 | |||
| 3 | libperf tutorial | ||
| 4 | ================ | ||
| 5 | |||
| 6 | Compile and install libperf from kernel sources | ||
| 7 | =============================================== | ||
| 8 | .. code-block:: bash | ||
| 9 | |||
| 10 | git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git | ||
| 11 | cd linux/tools/perf/lib | ||
| 12 | make | ||
| 13 | sudo make install prefix=/usr | ||
| 14 | |||
| 15 | Libperf object | ||
| 16 | ============== | ||
| 17 | The libperf library provides several high level objects: | ||
| 18 | |||
| 19 | struct perf_cpu_map | ||
| 20 | Provides a cpu list abstraction. | ||
| 21 | |||
| 22 | struct perf_thread_map | ||
| 23 | Provides a thread list abstraction. | ||
| 24 | |||
| 25 | struct perf_evsel | ||
| 26 | Provides an abstraction for single a perf event. | ||
| 27 | |||
| 28 | struct perf_evlist | ||
| 29 | Gathers several struct perf_evsel object and performs functions on all of them. | ||
| 30 | |||
| 31 | The exported API binds these objects together, | ||
| 32 | for full reference see the libperf.7 man page. | ||
| 33 | |||
| 34 | Examples | ||
| 35 | ======== | ||
| 36 | Examples aim to explain libperf functionality on simple use cases. | ||
| 37 | They are based in on a checked out linux kernel git tree: | ||
| 38 | |||
| 39 | .. code-block:: bash | ||
| 40 | |||
| 41 | $ cd tools/perf/lib/Documentation/tutorial/ | ||
| 42 | $ ls -d ex-* | ||
| 43 | ex-1-compile ex-2-evsel-stat ex-3-evlist-stat | ||
| 44 | |||
| 45 | ex-1-compile example | ||
| 46 | ==================== | ||
| 47 | This example shows the basic usage of *struct perf_cpu_map*, | ||
| 48 | how to create it and display its cpus: | ||
| 49 | |||
| 50 | .. code-block:: bash | ||
| 51 | |||
| 52 | $ cd ex-1-compile/ | ||
| 53 | $ make | ||
| 54 | gcc -o test test.c -lperf | ||
| 55 | $ ./test | ||
| 56 | 0 1 2 3 4 5 6 7 | ||
| 57 | |||
| 58 | |||
| 59 | The full code listing is here: | ||
| 60 | |||
| 61 | .. code-block:: c | ||
| 62 | |||
| 63 | 1 #include <perf/cpumap.h> | ||
| 64 | 2 | ||
| 65 | 3 int main(int argc, char **Argv) | ||
| 66 | 4 { | ||
| 67 | 5 struct perf_cpu_map *cpus; | ||
| 68 | 6 int cpu, tmp; | ||
| 69 | 7 | ||
| 70 | 8 cpus = perf_cpu_map__new(NULL); | ||
| 71 | 9 | ||
| 72 | 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) | ||
| 73 | 11 fprintf(stdout, "%d ", cpu); | ||
| 74 | 12 | ||
| 75 | 13 fprintf(stdout, "\n"); | ||
| 76 | 14 | ||
| 77 | 15 perf_cpu_map__put(cpus); | ||
| 78 | 16 return 0; | ||
| 79 | 17 } | ||
| 80 | |||
| 81 | |||
| 82 | First you need to include the proper header to have *struct perf_cpumap* | ||
| 83 | declaration and functions: | ||
| 84 | |||
| 85 | .. code-block:: c | ||
| 86 | |||
| 87 | 1 #include <perf/cpumap.h> | ||
| 88 | |||
| 89 | |||
| 90 | The *struct perf_cpumap* object is created by *perf_cpu_map__new* call. | ||
| 91 | The *NULL* argument asks it to populate the object with the current online CPUs list: | ||
| 92 | |||
| 93 | .. code-block:: c | ||
| 94 | |||
| 95 | 8 cpus = perf_cpu_map__new(NULL); | ||
| 96 | |||
| 97 | This is paired with a *perf_cpu_map__put*, that drops its reference at the end, possibly deleting it. | ||
| 98 | |||
| 99 | .. code-block:: c | ||
| 100 | |||
| 101 | 15 perf_cpu_map__put(cpus); | ||
| 102 | |||
| 103 | The iteration through the *struct perf_cpumap* CPUs is done using the *perf_cpu_map__for_each_cpu* | ||
| 104 | macro which requires 3 arguments: | ||
| 105 | |||
| 106 | - cpu - the cpu numer | ||
| 107 | - tmp - iteration helper variable | ||
| 108 | - cpus - the *struct perf_cpumap* object | ||
| 109 | |||
| 110 | .. code-block:: c | ||
| 111 | |||
| 112 | 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) | ||
| 113 | 11 fprintf(stdout, "%d ", cpu); | ||
| 114 | |||
| 115 | ex-2-evsel-stat example | ||
| 116 | ======================= | ||
| 117 | |||
| 118 | TBD | ||
| 119 | |||
| 120 | ex-3-evlist-stat example | ||
| 121 | ======================== | ||
| 122 | |||
| 123 | TBD | ||
| @@ -263,20 +263,20 @@ int test__bp_signal(struct test *test __maybe_unused, int subtest __maybe_unused · 262 unmodified lines | |||
| 263 | if (count1 == 11) | 263 | if (count1 == 11) |
| 264 | pr_debug("failed: RF EFLAG recursion issue detected\n"); | 264 | pr_debug("failed: RF EFLAG recursion issue detected\n"); |
| 265 | else | 265 | else |
| 266 | pr_debug("failed: wrong count for bp1%lld\n", count1); | 266 | pr_debug("failed: wrong count for bp1: %lld, expected 1\n", count1); |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | if (overflows != 3) | 269 | if (overflows != 3) |
| 270 | pr_debug("failed: wrong overflow hit\n"); | 270 | pr_debug("failed: wrong overflow (%d) hit, expected 3\n", overflows); |
| 271 | 271 | ||
| 272 | if (overflows_2 != 3) | 272 | if (overflows_2 != 3) |
| 273 | pr_debug("failed: wrong overflow_2 hit\n"); | 273 | pr_debug("failed: wrong overflow_2 (%d) hit, expected 3\n", overflows_2); |
| 274 | 274 | ||
| 275 | if (count2 != 3) | 275 | if (count2 != 3) |
| 276 | pr_debug("failed: wrong count for bp2\n"); | 276 | pr_debug("failed: wrong count for bp2 (%lld), expected 3\n", count2); |
| 277 | 277 | ||
| 278 | if (count3 != 2) | 278 | if (count3 != 2) |
| 279 | pr_debug("failed: wrong count for bp3\n"); | 279 | pr_debug("failed: wrong count for bp3 (%lld), expected 2\n", count3); |
| 280 | 280 | ||
| 281 | return count1 == 1 && overflows == 3 && count2 == 3 && overflows_2 == 3 && count3 == 2 ? | 281 | return count1 == 1 && overflows == 3 && count2 == 3 && overflows_2 == 3 && count3 == 2 ? |
| 282 | TEST_OK : TEST_FAIL; | 282 | TEST_OK : TEST_FAIL; |
| @@ -72,5 +72,5 @@ size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg · 71 unmodified lines | |||
| 72 | if (arg->augmented.args) | 72 | if (arg->augmented.args) |
| 73 | return syscall_arg__scnprintf_augmented_sockaddr(arg, bf, size); | 73 | return syscall_arg__scnprintf_augmented_sockaddr(arg, bf, size); |
| 74 | 74 | ||
| 75 | return scnprintf(bf, size, "%#x", arg->val); | 75 | return scnprintf(bf, size, "%#lx", arg->val); |
| 76 | } | 76 | } |
| @@ -18,7 +18,9 @@ · 17 unmodified lines | |||
| 18 | #include "../../util/evlist.h" | 18 | #include "../../util/evlist.h" |
| 19 | #include "../../util/header.h" | 19 | #include "../../util/header.h" |
| 20 | #include "../../util/hist.h" | 20 | #include "../../util/hist.h" |
| 21 | #include "../../util/machine.h" | ||
| 21 | #include "../../util/map.h" | 22 | #include "../../util/map.h" |
| 23 | #include "../../util/maps.h" | ||
| 22 | #include "../../util/symbol.h" | 24 | #include "../../util/symbol.h" |
| 23 | #include "../../util/map_symbol.h" | 25 | #include "../../util/map_symbol.h" |
| 24 | #include "../../util/branch.h" | 26 | #include "../../util/branch.h" |
| @@ -391,6 +393,57 @@ static void hist_entry__init_have_children(struct hist_entry *he) · 366 unmodified lines | |||
| 391 | he->init_have_children = true; | 393 | he->init_have_children = true; |
| 392 | } | 394 | } |
| 393 | 395 | ||
| 396 | static bool hist_browser__selection_has_children(struct hist_browser *browser) | ||
| 397 | { | ||
| 398 | struct hist_entry *he = browser->he_selection; | ||
| 399 | struct map_symbol *ms = browser->selection; | ||
| 400 | |||
| 401 | if (!he || !ms) | ||
| 402 | return false; | ||
| 403 | |||
| 404 | if (ms == &he->ms) | ||
| 405 | return he->has_children; | ||
| 406 | |||
| 407 | return container_of(ms, struct callchain_list, ms)->has_children; | ||
| 408 | } | ||
| 409 | |||
| 410 | static bool hist_browser__he_selection_unfolded(struct hist_browser *browser) | ||
| 411 | { | ||
| 412 | return browser->he_selection ? browser->he_selection->unfolded : false; | ||
| 413 | } | ||
| 414 | |||
| 415 | static bool hist_browser__selection_unfolded(struct hist_browser *browser) | ||
| 416 | { | ||
| 417 | struct hist_entry *he = browser->he_selection; | ||
| 418 | struct map_symbol *ms = browser->selection; | ||
| 419 | |||
| 420 | if (!he || !ms) | ||
| 421 | return false; | ||
| 422 | |||
| 423 | if (ms == &he->ms) | ||
| 424 | return he->unfolded; | ||
| 425 | |||
| 426 | return container_of(ms, struct callchain_list, ms)->unfolded; | ||
| 427 | } | ||
| 428 | |||
| 429 | static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size) | ||
| 430 | { | ||
| 431 | struct hist_entry *he = browser->he_selection; | ||
| 432 | struct map_symbol *ms = browser->selection; | ||
| 433 | struct callchain_list *callchain_entry; | ||
| 434 | |||
| 435 | if (!he || !ms) | ||
| 436 | return NULL; | ||
| 437 | |||
| 438 | if (ms == &he->ms) { | ||
| 439 | hist_entry__sym_snprintf(he, bf, size, 0); | ||
| 440 | return bf + 4; // skip the level, e.g. '[k] ' | ||
| 441 | } | ||
| 442 | |||
| 443 | callchain_entry = container_of(ms, struct callchain_list, ms); | ||
| 444 | return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso); | ||
| 445 | } | ||
| 446 | |||
| 394 | static bool hist_browser__toggle_fold(struct hist_browser *browser) | 447 | static bool hist_browser__toggle_fold(struct hist_browser *browser) |
| 395 | { | 448 | { |
| 396 | struct hist_entry *he = browser->he_selection; | 449 | struct hist_entry *he = browser->he_selection; |
| @@ -624,10 +677,81 @@ static int hist_browser__title(struct hist_browser *browser, char *bf, size_t si · 227 unmodified lines | |||
| 624 | return browser->title ? browser->title(browser, bf, size) : 0; | 677 | return browser->title ? browser->title(browser, bf, size) : 0; |
| 625 | } | 678 | } |
| 626 | 679 | ||
| 680 | static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, int key) | ||
| 681 | { | ||
| 682 | switch (key) { | ||
| 683 | case K_TIMER: { | ||
| 684 | struct hist_browser_timer *hbt = browser->hbt; | ||
| 685 | u64 nr_entries; | ||
| 686 | |||
| 687 | WARN_ON_ONCE(!hbt); | ||
| 688 | |||
| 689 | if (hbt) | ||
| 690 | hbt->timer(hbt->arg); | ||
| 691 | |||
| 692 | if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy) | ||
| 693 | hist_browser__update_nr_entries(browser); | ||
| 694 | |||
| 695 | nr_entries = hist_browser__nr_entries(browser); | ||
| 696 | ui_browser__update_nr_entries(&browser->b, nr_entries); | ||
| 697 | |||
| 698 | if (warn_lost_event && | ||
| 699 | (browser->hists->stats.nr_lost_warned != | ||
| 700 | browser->hists->stats.nr_events[PERF_RECORD_LOST])) { | ||
| 701 | browser->hists->stats.nr_lost_warned = | ||
| 702 | browser->hists->stats.nr_events[PERF_RECORD_LOST]; | ||
| 703 | ui_browser__warn_lost_events(&browser->b); | ||
| 704 | } | ||
| 705 | |||
| 706 | hist_browser__title(browser, title, sizeof(title)); | ||
| 707 | ui_browser__show_title(&browser->b, title); | ||
| 708 | break; | ||
| 709 | } | ||
| 710 | case 'D': { /* Debug */ | ||
| 711 | struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node); | ||
| 712 | static int seq; | ||
| 713 | |||
| 714 | ui_helpline__pop(); | ||
| 715 | ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", | ||
| 716 | seq++, browser->b.nr_entries, browser->hists->nr_entries, | ||
| 717 | browser->b.extra_title_lines, browser->b.rows, | ||
| 718 | browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows); | ||
| 719 | } | ||
| 720 | break; | ||
| 721 | case 'C': | ||
| 722 | /* Collapse the whole world. */ | ||
| 723 | hist_browser__set_folding(browser, false); | ||
| 724 | break; | ||
| 725 | case 'c': | ||
| 726 | /* Collapse the selected entry. */ | ||
| 727 | hist_browser__set_folding_selected(browser, false); | ||
| 728 | break; | ||
| 729 | case 'E': | ||
| 730 | /* Expand the whole world. */ | ||
| 731 | hist_browser__set_folding(browser, true); | ||
| 732 | break; | ||
| 733 | case 'e': | ||
| 734 | /* Expand the selected entry. */ | ||
| 735 | hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser)); | ||
| 736 | break; | ||
| 737 | case 'H': | ||
| 738 | browser->show_headers = !browser->show_headers; | ||
| 739 | hist_browser__update_rows(browser); | ||
| 740 | break; | ||
| 741 | case '+': | ||
| 742 | if (hist_browser__toggle_fold(browser)) | ||
| 743 | break; | ||
| 744 | /* fall thru */ | ||
| 745 | default: | ||
| 746 | return -1; | ||
| 747 | } | ||
| 748 | |||
| 749 | return 0; | ||
| 750 | } | ||
| 751 | |||
| 627 | int hist_browser__run(struct hist_browser *browser, const char *help, | 752 | int hist_browser__run(struct hist_browser *browser, const char *help, |
| 628 | bool warn_lost_event) | 753 | bool warn_lost_event, int key) |
| 629 | { | 754 | { |
| 630 | int key; | ||
| 631 | char title[160]; | 755 | char title[160]; |
| 632 | struct hist_browser_timer *hbt = browser->hbt; | 756 | struct hist_browser_timer *hbt = browser->hbt; |
| 633 | int delay_secs = hbt ? hbt->refresh : 0; | 757 | int delay_secs = hbt ? hbt->refresh : 0; |
| @@ -640,79 +764,14 @@ int hist_browser__run(struct hist_browser *browser, const char *help, · 6 unmodified lines | |||
| 640 | if (ui_browser__show(&browser->b, title, "%s", help) < 0) | 764 | if (ui_browser__show(&browser->b, title, "%s", help) < 0) |
| 641 | return -1; | 765 | return -1; |
| 642 | 766 | ||
| 767 | if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, key)) | ||
| 768 | goto out; | ||
| 769 | |||
| 643 | while (1) { | 770 | while (1) { |
| 644 | key = ui_browser__run(&browser->b, delay_secs); | 771 | key = ui_browser__run(&browser->b, delay_secs); |
| 645 | 772 | ||
| 646 | switch (key) { | 773 | if (hist_browser__handle_hotkey(browser, warn_lost_event, title, key)) |
| 647 | case K_TIMER: { | ||
| 648 | u64 nr_entries; | ||
| 649 | |||
| 650 | WARN_ON_ONCE(!hbt); | ||
| 651 | |||
| 652 | if (hbt) | ||
| 653 | hbt->timer(hbt->arg); | ||
| 654 | |||
| 655 | if (hist_browser__has_filter(browser) || | ||
| 656 | symbol_conf.report_hierarchy) | ||
| 657 | hist_browser__update_nr_entries(browser); | ||
| 658 | |||
| 659 | nr_entries = hist_browser__nr_entries(browser); | ||
| 660 | ui_browser__update_nr_entries(&browser->b, nr_entries); | ||
| 661 | |||
| 662 | if (warn_lost_event && | ||
| 663 | (browser->hists->stats.nr_lost_warned != | ||
| 664 | browser->hists->stats.nr_events[PERF_RECORD_LOST])) { | ||
| 665 | browser->hists->stats.nr_lost_warned = | ||
| 666 | browser->hists->stats.nr_events[PERF_RECORD_LOST]; | ||
| 667 | ui_browser__warn_lost_events(&browser->b); | ||
| 668 | } | ||
| 669 | |||
| 670 | hist_browser__title(browser, title, sizeof(title)); | ||
| 671 | ui_browser__show_title(&browser->b, title); | ||
| 672 | continue; | ||
| 673 | } | ||
| 674 | case 'D': { /* Debug */ | ||
| 675 | static int seq; | ||
| 676 | struct hist_entry *h = rb_entry(browser->b.top, | ||
| 677 | struct hist_entry, rb_node); | ||
| 678 | ui_helpline__pop(); | ||
| 679 | ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", | ||
| 680 | seq++, browser->b.nr_entries, | ||
| 681 | browser->hists->nr_entries, | ||
| 682 | browser->b.extra_title_lines, | ||
| 683 | browser->b.rows, | ||
| 684 | browser->b.index, | ||
| 685 | browser->b.top_idx, | ||
| 686 | h->row_offset, h->nr_rows); | ||
| 687 | } | ||
| 688 | break; | ||
| 689 | case 'C': | ||
| 690 | /* Collapse the whole world. */ | ||
| 691 | hist_browser__set_folding(browser, false); | ||
| 692 | break; | ||
| 693 | case 'c': | ||
| 694 | /* Collapse the selected entry. */ | ||
| 695 | hist_browser__set_folding_selected(browser, false); | ||
| 696 | break; | 774 | break; |
| 697 | case 'E': | ||
| 698 | /* Expand the whole world. */ | ||
| 699 | hist_browser__set_folding(browser, true); | ||
| 700 | break; | ||
| 701 | case 'e': | ||
| 702 | /* Expand the selected entry. */ | ||
| 703 | hist_browser__set_folding_selected(browser, true); | ||
| 704 | break; | ||
| 705 | case 'H': | ||
| 706 | browser->show_headers = !browser->show_headers; | ||
| 707 | hist_browser__update_rows(browser); | ||
| 708 | break; | ||
| 709 | case K_ENTER: | ||
| 710 | if (hist_browser__toggle_fold(browser)) | ||
| 711 | break; | ||
| 712 | /* fall thru */ | ||
| 713 | default: | ||
| 714 | goto out; | ||
| 715 | } | ||
| 716 | } | 775 | } |
| 717 | out: | 776 | out: |
| 718 | ui_browser__hide(&browser->b); | 777 | ui_browser__hide(&browser->b); |
| @@ -2339,7 +2398,7 @@ close_file_and_continue: · 1620 unmodified lines | |||
| 2339 | closedir(pwd_dir); | 2398 | closedir(pwd_dir); |
| 2340 | 2399 | ||
| 2341 | if (nr_options) { | 2400 | if (nr_options) { |
| 2342 | choice = ui__popup_menu(nr_options, options); | 2401 | choice = ui__popup_menu(nr_options, options, NULL); |
| 2343 | if (choice < nr_options && choice >= 0) { | 2402 | if (choice < nr_options && choice >= 0) { |
| 2344 | tmp = strdup(abs_path[choice]); | 2403 | tmp = strdup(abs_path[choice]); |
| 2345 | if (tmp) { | 2404 | if (tmp) { |
| @@ -2411,7 +2470,8 @@ add_annotate_opt(struct hist_browser *browser __maybe_unused, · 65 unmodified lines | |||
| 2411 | struct popup_action *act, char **optstr, | 2470 | struct popup_action *act, char **optstr, |
| 2412 | struct map_symbol *ms) | 2471 | struct map_symbol *ms) |
| 2413 | { | 2472 | { |
| 2414 | if (ms->sym == NULL || ms->map->dso->annotate_warned) | 2473 | if (ms->sym == NULL || ms->map->dso->annotate_warned || |
| 2474 | symbol__annotation(ms->sym)->src == NULL) | ||
| 2415 | return 0; | 2475 | return 0; |
| 2416 | 2476 | ||
| 2417 | if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) | 2477 | if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) |
| @@ -2484,11 +2544,8 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act, · 66 unmodified lines | |||
| 2484 | return 1; | 2544 | return 1; |
| 2485 | } | 2545 | } |
| 2486 | 2546 | ||
| 2487 | static int | 2547 | static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map) |
| 2488 | do_zoom_dso(struct hist_browser *browser, struct popup_action *act) | ||
| 2489 | { | 2548 | { |
| 2490 | struct map *map = act->ms.map; | ||
| 2491 | |||
| 2492 | if (!hists__has(browser->hists, dso) || map == NULL) | 2549 | if (!hists__has(browser->hists, dso) || map == NULL) |
| 2493 | return 0; | 2550 | return 0; |
| 2494 | 2551 | ||
| @@ -2510,6 +2567,12 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act) · 15 unmodified lines | |||
| 2510 | return 0; | 2567 | return 0; |
| 2511 | } | 2568 | } |
| 2512 | 2569 | ||
| 2570 | static int | ||
| 2571 | do_zoom_dso(struct hist_browser *browser, struct popup_action *act) | ||
| 2572 | { | ||
| 2573 | return hists_browser__zoom_map(browser, act->ms.map); | ||
| 2574 | } | ||
| 2575 | |||
| 2513 | static int | 2576 | static int |
| 2514 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, | 2577 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, |
| 2515 | char **optstr, struct map *map) | 2578 | char **optstr, struct map *map) |
| @@ -2517,7 +2580,7 @@ add_dso_opt(struct hist_browser *browser, struct popup_action *act, · 1 unmodified line | |||
| 2517 | if (!hists__has(browser->hists, dso) || map == NULL) | 2580 | if (!hists__has(browser->hists, dso) || map == NULL) |
| 2518 | return 0; | 2581 | return 0; |
| 2519 | 2582 | ||
| 2520 | if (asprintf(optstr, "Zoom %s %s DSO", | 2583 | if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)", |
| 2521 | browser->hists->dso_filter ? "out of" : "into", | 2584 | browser->hists->dso_filter ? "out of" : "into", |
| 2522 | __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) | 2585 | __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) |
| 2523 | return 0; | 2586 | return 0; |
| @@ -2527,6 +2590,28 @@ add_dso_opt(struct hist_browser *browser, struct popup_action *act, · 3 unmodified lines | |||
| 2527 | return 1; | 2590 | return 1; |
| 2528 | } | 2591 | } |
| 2529 | 2592 | ||
| 2593 | static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused) | ||
| 2594 | { | ||
| 2595 | hist_browser__toggle_fold(browser); | ||
| 2596 | return 0; | ||
| 2597 | } | ||
| 2598 | |||
| 2599 | static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) | ||
| 2600 | { | ||
| 2601 | char sym_name[512]; | ||
| 2602 | |||
| 2603 | if (!hist_browser__selection_has_children(browser)) | ||
| 2604 | return 0; | ||
| 2605 | |||
| 2606 | if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)", | ||
| 2607 | hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand", | ||
| 2608 | hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0) | ||
| 2609 | return 0; | ||
| 2610 | |||
| 2611 | act->fn = do_toggle_callchain; | ||
| 2612 | return 1; | ||
| 2613 | } | ||
| 2614 | |||
| 2530 | static int | 2615 | static int |
| 2531 | do_browse_map(struct hist_browser *browser __maybe_unused, | 2616 | do_browse_map(struct hist_browser *browser __maybe_unused, |
| 2532 | struct popup_action *act) | 2617 | struct popup_action *act) |
| @@ -2858,12 +2943,15 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, · 325 unmodified lines | |||
| 2858 | "For symbolic views (--sort has sym):\n\n" \ | 2943 | "For symbolic views (--sort has sym):\n\n" \ |
| 2859 | "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ | 2944 | "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ |
| 2860 | "ESC Zoom out\n" \ | 2945 | "ESC Zoom out\n" \ |
| 2946 | "+ Expand/Collapse one callchain level\n" \ | ||
| 2861 | "a Annotate current symbol\n" \ | 2947 | "a Annotate current symbol\n" \ |
| 2862 | "C Collapse all callchains\n" \ | 2948 | "C Collapse all callchains\n" \ |
| 2863 | "d Zoom into current DSO\n" \ | 2949 | "d Zoom into current DSO\n" \ |
| 2950 | "e Expand/Collapse main entry callchains\n" \ | ||
| 2864 | "E Expand all callchains\n" \ | 2951 | "E Expand all callchains\n" \ |
| 2865 | "F Toggle percentage of filtered entries\n" \ | 2952 | "F Toggle percentage of filtered entries\n" \ |
| 2866 | "H Display column headers\n" \ | 2953 | "H Display column headers\n" \ |
| 2954 | "k Zoom into the kernel map\n" \ | ||
| 2867 | "L Change percent limit\n" \ | 2955 | "L Change percent limit\n" \ |
| 2868 | "m Display context menu\n" \ | 2956 | "m Display context menu\n" \ |
| 2869 | "S Zoom into current Processor Socket\n" \ | 2957 | "S Zoom into current Processor Socket\n" \ |
| @@ -2914,13 +3002,13 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, · 44 unmodified lines | |||
| 2914 | while (1) { | 3002 | while (1) { |
| 2915 | struct thread *thread = NULL; | 3003 | struct thread *thread = NULL; |
| 2916 | struct map *map = NULL; | 3004 | struct map *map = NULL; |
| 2917 | int choice = 0; | 3005 | int choice; |
| 2918 | int socked_id = -1; | 3006 | int socked_id = -1; |
| 2919 | 3007 | ||
| 2920 | nr_options = 0; | 3008 | key = 0; // reset key |
| 2921 | 3009 | do_hotkey: // key came straight from options ui__popup_menu() | |
| 2922 | key = hist_browser__run(browser, helpline, | 3010 | choice = nr_options = 0; |
| 2923 | warn_lost_event); | 3011 | key = hist_browser__run(browser, helpline, warn_lost_event, key); |
| 2924 | 3012 | ||
| 2925 | if (browser->he_selection != NULL) { | 3013 | if (browser->he_selection != NULL) { |
| 2926 | thread = hist_browser__selected_thread(browser); | 3014 | thread = hist_browser__selected_thread(browser); |
| @@ -2950,6 +3038,14 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, · 23 unmodified lines | |||
| 2950 | browser->selection->map->dso->annotate_warned) | 3038 | browser->selection->map->dso->annotate_warned) |
| 2951 | continue; | 3039 | continue; |
| 2952 | 3040 | ||
| 3041 | if (symbol__annotation(browser->selection->sym)->src == NULL) { | ||
| 3042 | ui_browser__warning(&browser->b, delay_secs * 2, | ||
| 3043 | "No samples for the \"%s\" symbol.\n\n" | ||
| 3044 | "Probably appeared just in a callchain", | ||
| 3045 | browser->selection->sym->name); | ||
| 3046 | continue; | ||
| 3047 | } | ||
| 3048 | |||
| 2953 | actions->ms.map = browser->selection->map; | 3049 | actions->ms.map = browser->selection->map; |
| 2954 | actions->ms.sym = browser->selection->sym; | 3050 | actions->ms.sym = browser->selection->sym; |
| 2955 | do_annotate(browser, actions); | 3051 | do_annotate(browser, actions); |
| @@ -2961,6 +3057,10 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, · 5 unmodified lines | |||
| 2961 | actions->ms.map = map; | 3057 | actions->ms.map = map; |
| 2962 | do_zoom_dso(browser, actions); | 3058 | do_zoom_dso(browser, actions); |
| 2963 | continue; | 3059 | continue; |
| 3060 | case 'k': | ||
| 3061 | if (browser->selection != NULL) | ||
| 3062 | hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map); | ||
| 3063 | continue; | ||
| 2964 | case 'V': | 3064 | case 'V': |
| 2965 | verbose = (verbose + 1) % 4; | 3065 | verbose = (verbose + 1) % 4; |
| 2966 | browser->show_dso = verbose > 0; | 3066 | browser->show_dso = verbose > 0; |
| @@ -3062,6 +3162,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, · 95 unmodified lines | |||
| 3062 | 3162 | ||
| 3063 | continue; | 3163 | continue; |
| 3064 | } | 3164 | } |
| 3165 | actions->ms.map = map; | ||
| 3065 | top = pstack__peek(browser->pstack); | 3166 | top = pstack__peek(browser->pstack); |
| 3066 | if (top == &browser->hists->dso_filter) { | 3167 | if (top == &browser->hists->dso_filter) { |
| 3067 | /* | 3168 | /* |
| @@ -3135,6 +3236,7 @@ skip_annotation: · 67 unmodified lines | |||
| 3135 | &options[nr_options], thread); | 3236 | &options[nr_options], thread); |
| 3136 | nr_options += add_dso_opt(browser, &actions[nr_options], | 3237 | nr_options += add_dso_opt(browser, &actions[nr_options], |
| 3137 | &options[nr_options], map); | 3238 | &options[nr_options], map); |
| 3239 | nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]); | ||
| 3138 | nr_options += add_map_opt(browser, &actions[nr_options], | 3240 | nr_options += add_map_opt(browser, &actions[nr_options], |
| 3139 | &options[nr_options], | 3241 | &options[nr_options], |
| 3140 | browser->selection ? | 3242 | browser->selection ? |
| @@ -3193,10 +3295,13 @@ skip_scripting: · 52 unmodified lines | |||
| 3193 | do { | 3295 | do { |
| 3194 | struct popup_action *act; | 3296 | struct popup_action *act; |
| 3195 | 3297 | ||
| 3196 | choice = ui__popup_menu(nr_options, options); | 3298 | choice = ui__popup_menu(nr_options, options, &key); |
| 3197 | if (choice == -1 || choice >= nr_options) | 3299 | if (choice == -1) |
| 3198 | break; | 3300 | break; |
| 3199 | 3301 | ||
| 3302 | if (choice == nr_options) | ||
| 3303 | goto do_hotkey; | ||
| 3304 | |||
| 3200 | act = &actions[choice]; | 3305 | act = &actions[choice]; |
| 3201 | key = act->fn(browser, act); | 3306 | key = act->fn(browser, act); |
| 3202 | } while (key == 1); | 3307 | } while (key == 1); |
| @@ -3492,7 +3597,7 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, · 289 unmodified lines | |||
| 3492 | memset(&action, 0, sizeof(action)); | 3597 | memset(&action, 0, sizeof(action)); |
| 3493 | 3598 | ||
| 3494 | while (1) { | 3599 | while (1) { |
| 3495 | key = hist_browser__run(browser, "? - help", true); | 3600 | key = hist_browser__run(browser, "? - help", true, 0); |
| 3496 | 3601 | ||
| 3497 | switch (key) { | 3602 | switch (key) { |
| 3498 | case 'q': | 3603 | case 'q': |
| @@ -34,7 +34,7 @@ struct hist_browser { · 33 unmodified lines | |||
| 34 | struct hist_browser *hist_browser__new(struct hists *hists); | 34 | struct hist_browser *hist_browser__new(struct hists *hists); |
| 35 | void hist_browser__delete(struct hist_browser *browser); | 35 | void hist_browser__delete(struct hist_browser *browser); |
| 36 | int hist_browser__run(struct hist_browser *browser, const char *help, | 36 | int hist_browser__run(struct hist_browser *browser, const char *help, |
| 37 | bool warn_lost_event); | 37 | bool warn_lost_event, int key); |
| 38 | void hist_browser__init(struct hist_browser *browser, | 38 | void hist_browser__init(struct hist_browser *browser, |
| 39 | struct hists *hists); | 39 | struct hists *hists); |
| 40 | #endif /* _PERF_UI_BROWSER_HISTS_H_ */ | 40 | #endif /* _PERF_UI_BROWSER_HISTS_H_ */ |
| @@ -56,7 +56,7 @@ int res_sample_browse(struct res_sample *res_samples, int num_res, · 55 unmodified lines | |||
| 56 | return -1; | 56 | return -1; |
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | choice = ui__popup_menu(num_res, names); | 59 | choice = ui__popup_menu(num_res, names, NULL); |
| 60 | for (i = 0; i < num_res; i++) | 60 | for (i = 0; i < num_res; i++) |
| 61 | zfree(&names[i]); | 61 | zfree(&names[i]); |
| 62 | free(names); | 62 | free(names); |
| @@ -126,7 +126,7 @@ static int list_scripts(char *script_name, bool *custom, · 125 unmodified lines | |||
| 126 | SCRIPT_FULLPATH_LEN); | 126 | SCRIPT_FULLPATH_LEN); |
| 127 | if (num < 0) | 127 | if (num < 0) |
| 128 | num = 0; | 128 | num = 0; |
| 129 | choice = ui__popup_menu(num + max_std, (char * const *)names); | 129 | choice = ui__popup_menu(num + max_std, (char * const *)names, NULL); |
| 130 | if (choice < 0) { | 130 | if (choice < 0) { |
| 131 | ret = -1; | 131 | ret = -1; |
| 132 | goto out; | 132 | goto out; |
| @@ -1,4 +1,4 @@ | |||
| 1 | CFLAGS_gtk += -fPIC $(GTK_CFLAGS) | 1 | CFLAGS_gtk += -fPIC $(GTK_CFLAGS) -Wno-deprecated-declarations |
| 2 | 2 | ||
| 3 | gtk-y += browser.o | 3 | gtk-y += browser.o |
| 4 | gtk-y += hists.o | 4 | gtk-y += hists.o |
| @@ -7,3 +7,8 @@ gtk-y += util.o · 2 unmodified lines | |||
| 7 | gtk-y += helpline.o | 7 | gtk-y += helpline.o |
| 8 | gtk-y += progress.o | 8 | gtk-y += progress.o |
| 9 | gtk-y += annotate.o | 9 | gtk-y += annotate.o |
| 10 | gtk-y += zalloc.o | ||
| 11 | |||
| 12 | $(OUTPUT)ui/gtk/zalloc.o: ../lib/zalloc.c FORCE | ||
| 13 | $(call rule_mkdir) | ||
| 14 | $(call if_changed_dep,cc_o_c) | ||
| @@ -23,7 +23,7 @@ static void ui_browser__argv_write(struct ui_browser *browser, · 22 unmodified lines | |||
| 23 | ui_browser__write_nstring(browser, *arg, browser->width); | 23 | ui_browser__write_nstring(browser, *arg, browser->width); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | static int popup_menu__run(struct ui_browser *menu) | 26 | static int popup_menu__run(struct ui_browser *menu, int *keyp) |
| 27 | { | 27 | { |
| 28 | int key; | 28 | int key; |
| 29 | 29 | ||
| @@ -45,6 +45,11 @@ static int popup_menu__run(struct ui_browser *menu) · 15 unmodified lines | |||
| 45 | key = -1; | 45 | key = -1; |
| 46 | break; | 46 | break; |
| 47 | default: | 47 | default: |
| 48 | if (keyp) { | ||
| 49 | *keyp = key; | ||
| 50 | key = menu->nr_entries; | ||
| 51 | break; | ||
| 52 | } | ||
| 48 | continue; | 53 | continue; |
| 49 | } | 54 | } |
| 50 | 55 | ||
| @@ -55,7 +60,7 @@ static int popup_menu__run(struct ui_browser *menu) · 4 unmodified lines | |||
| 55 | return key; | 60 | return key; |
| 56 | } | 61 | } |
| 57 | 62 | ||
| 58 | int ui__popup_menu(int argc, char * const argv[]) | 63 | int ui__popup_menu(int argc, char * const argv[], int *keyp) |
| 59 | { | 64 | { |
| 60 | struct ui_browser menu = { | 65 | struct ui_browser menu = { |
| 61 | .entries = (void *)argv, | 66 | .entries = (void *)argv, |
| @@ -64,8 +69,7 @@ int ui__popup_menu(int argc, char * const argv[]) · 2 unmodified lines | |||
| 64 | .write = ui_browser__argv_write, | 69 | .write = ui_browser__argv_write, |
| 65 | .nr_entries = argc, | 70 | .nr_entries = argc, |
| 66 | }; | 71 | }; |
| 67 | 72 | return popup_menu__run(&menu, keyp); | |
| 68 | return popup_menu__run(&menu); | ||
| 69 | } | 73 | } |
| 70 | 74 | ||
| 71 | int ui_browser__input_window(const char *title, const char *text, char *input, | 75 | int ui_browser__input_window(const char *title, const char *text, char *input, |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | |||
| 5 | #include <stdarg.h> | 5 | #include <stdarg.h> |
| 6 | 6 | ||
| 7 | int ui__getch(int delay_secs); | 7 | int ui__getch(int delay_secs); |
| 8 | int ui__popup_menu(int argc, char * const argv[]); | 8 | int ui__popup_menu(int argc, char * const argv[], int *keyp); |
| 9 | int ui__help_window(const char *text); | 9 | int ui__help_window(const char *text); |
| 10 | int ui__dialog_yesno(const char *msg); | 10 | int ui__dialog_yesno(const char *msg); |
| 11 | void __ui__info_window(const char *title, const char *text, const char *exit_msg); | 11 | void __ui__info_window(const char *title, const char *text, const char *exit_msg); |
| @@ -1966,14 +1966,20 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) · 1965 unmodified lines | |||
| 1966 | err = asprintf(&command, | 1966 | err = asprintf(&command, |
| 1967 | "%s %s%s --start-address=0x%016" PRIx64 | 1967 | "%s %s%s --start-address=0x%016" PRIx64 |
| 1968 | " --stop-address=0x%016" PRIx64 | 1968 | " --stop-address=0x%016" PRIx64 |
| 1969 | " -l -d %s %s -C \"$1\"", | 1969 | " -l -d %s %s %s %c%s%c %s%s -C \"$1\"", |
| 1970 | opts->objdump_path ?: "objdump", | 1970 | opts->objdump_path ?: "objdump", |
| 1971 | opts->disassembler_style ? "-M " : "", | 1971 | opts->disassembler_style ? "-M " : "", |
| 1972 | opts->disassembler_style ?: "", | 1972 | opts->disassembler_style ?: "", |
| 1973 | map__rip_2objdump(map, sym->start), | 1973 | map__rip_2objdump(map, sym->start), |
| 1974 | map__rip_2objdump(map, sym->end), | 1974 | map__rip_2objdump(map, sym->end), |
| 1975 | opts->show_asm_raw ? "" : "--no-show-raw-insn", | 1975 | opts->show_asm_raw ? "" : "--no-show-raw-insn", |
| 1976 | opts->annotate_src ? "-S" : ""); | 1976 | opts->annotate_src ? "-S" : "", |
| 1977 | opts->prefix ? "--prefix " : "", | ||
| 1978 | opts->prefix ? '"' : ' ', | ||
| 1979 | opts->prefix ?: "", | ||
| 1980 | opts->prefix ? '"' : ' ', | ||
| 1981 | opts->prefix_strip ? "--prefix-strip=" : "", | ||
| 1982 | opts->prefix_strip ?: ""); | ||
| 1977 | 1983 | ||
| 1978 | if (err < 0) { | 1984 | if (err < 0) { |
| 1979 | pr_err("Failure allocating memory for the command to run\n"); | 1985 | pr_err("Failure allocating memory for the command to run\n"); |
| @@ -3204,3 +3210,12 @@ out: · 1224 unmodified lines | |||
| 3204 | free(str1); | 3210 | free(str1); |
| 3205 | return err; | 3211 | return err; |
| 3206 | } | 3212 | } |
| 3213 | |||
| 3214 | int annotate_check_args(struct annotation_options *args) | ||
| 3215 | { | ||
| 3216 | if (args->prefix_strip && !args->prefix) { | ||
| 3217 | pr_err("--prefix-strip requires --prefix\n"); | ||
| 3218 | return -1; | ||
| 3219 | } | ||
| 3220 | return 0; | ||
| 3221 | } | ||
| @@ -94,6 +94,8 @@ struct annotation_options { · 93 unmodified lines | |||
| 94 | int context; | 94 | int context; |
| 95 | const char *objdump_path; | 95 | const char *objdump_path; |
| 96 | const char *disassembler_style; | 96 | const char *disassembler_style; |
| 97 | const char *prefix; | ||
| 98 | const char *prefix_strip; | ||
| 97 | unsigned int percent_type; | 99 | unsigned int percent_type; |
| 98 | }; | 100 | }; |
| 99 | 101 | ||
| @@ -415,4 +417,7 @@ void annotation_config__init(void); · 315 unmodified lines | |||
| 415 | 417 | ||
| 416 | int annotate_parse_percent_type(const struct option *opt, const char *_str, | 418 | int annotate_parse_percent_type(const struct option *opt, const char *_str, |
| 417 | int unset); | 419 | int unset); |
| 420 | |||
| 421 | int annotate_check_args(struct annotation_options *args); | ||
| 422 | |||
| 418 | #endif /* __PERF_ANNOTATE_H */ | 423 | #endif /* __PERF_ANNOTATE_H */ |
| @@ -71,7 +71,11 @@ getModuleFromSource(llvm::opt::ArgStringList CFlags, · 70 unmodified lines | |||
| 71 | CompilerInstance Clang; | 71 | CompilerInstance Clang; |
| 72 | Clang.createDiagnostics(); | 72 | Clang.createDiagnostics(); |
| 73 | 73 | ||
| 74 | #if CLANG_VERSION_MAJOR < 9 | ||
| 74 | Clang.setVirtualFileSystem(&*VFS); | 75 | Clang.setVirtualFileSystem(&*VFS); |
| 76 | #else | ||
| 77 | Clang.createFileManager(&*VFS); | ||
| 78 | #endif | ||
| 75 | 79 | ||
| 76 | #if CLANG_VERSION_MAJOR < 4 | 80 | #if CLANG_VERSION_MAJOR < 4 |
| 77 | IntrusiveRefCntPtr<CompilerInvocation> CI = | 81 | IntrusiveRefCntPtr<CompilerInvocation> CI = |
| @@ -12,7 +12,8 @@ · 11 unmodified lines | |||
| 12 | #define MAXIDLEN 256 | 12 | #define MAXIDLEN 256 |
| 13 | %} | 13 | %} |
| 14 | 14 | ||
| 15 | %pure-parser | 15 | %define api.pure full |
| 16 | |||
| 16 | %parse-param { double *final_val } | 17 | %parse-param { double *final_val } |
| 17 | %parse-param { struct parse_ctx *ctx } | 18 | %parse-param { struct parse_ctx *ctx } |
| 18 | %parse-param { const char **pp } | 19 | %parse-param { const char **pp } |
| @@ -2922,7 +2922,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) · 2921 unmodified lines | |||
| 2922 | if (ret == -1) | 2922 | if (ret == -1) |
| 2923 | return -1; | 2923 | return -1; |
| 2924 | 2924 | ||
| 2925 | stctime = st.st_ctime; | 2925 | stctime = st.st_mtime; |
| 2926 | fprintf(fp, "# captured on : %s", ctime(&stctime)); | 2926 | fprintf(fp, "# captured on : %s", ctime(&stctime)); |
| 2927 | 2927 | ||
| 2928 | fprintf(fp, "# header version : %u\n", header->version); | 2928 | fprintf(fp, "# header version : %u\n", header->version); |
| @@ -23,6 +23,18 @@ · 22 unmodified lines | |||
| 23 | #include "mmap.h" | 23 | #include "mmap.h" |
| 24 | #include "../perf.h" | 24 | #include "../perf.h" |
| 25 | #include <internal/lib.h> /* page_size */ | 25 | #include <internal/lib.h> /* page_size */ |
| 26 | #include <linux/bitmap.h> | ||
| 27 | |||
| 28 | #define MASK_SIZE 1023 | ||
| 29 | void mmap_cpu_mask__scnprintf(struct mmap_cpu_mask *mask, const char *tag) | ||
| 30 | { | ||
| 31 | char buf[MASK_SIZE + 1]; | ||
| 32 | size_t len; | ||
| 33 | |||
| 34 | len = bitmap_scnprintf(mask->bits, mask->nbits, buf, MASK_SIZE); | ||
| 35 | buf[len] = '\0'; | ||
| 36 | pr_debug("%p: %s mask[%zd]: %s\n", mask, tag, mask->nbits, buf); | ||
| 37 | } | ||
| 26 | 38 | ||
| 27 | size_t mmap__mmap_len(struct mmap *map) | 39 | size_t mmap__mmap_len(struct mmap *map) |
| 28 | { | 40 | { |
| @@ -207,6 +219,8 @@ static void perf_mmap__aio_munmap(struct mmap *map __maybe_unused) · 178 unmodified lines | |||
| 207 | 219 | ||
| 208 | void mmap__munmap(struct mmap *map) | 220 | void mmap__munmap(struct mmap *map) |
| 209 | { | 221 | { |
| 222 | bitmap_free(map->affinity_mask.bits); | ||
| 223 | |||
| 210 | perf_mmap__aio_munmap(map); | 224 | perf_mmap__aio_munmap(map); |
| 211 | if (map->data != NULL) { | 225 | if (map->data != NULL) { |
| 212 | munmap(map->data, mmap__mmap_len(map)); | 226 | munmap(map->data, mmap__mmap_len(map)); |
| @@ -215,7 +229,7 @@ void mmap__munmap(struct mmap *map) · 2 unmodified lines | |||
| 215 | auxtrace_mmap__munmap(&map->auxtrace_mmap); | 229 | auxtrace_mmap__munmap(&map->auxtrace_mmap); |
| 216 | } | 230 | } |
| 217 | 231 | ||
| 218 | static void build_node_mask(int node, cpu_set_t *mask) | 232 | static void build_node_mask(int node, struct mmap_cpu_mask *mask) |
| 219 | { | 233 | { |
| 220 | int c, cpu, nr_cpus; | 234 | int c, cpu, nr_cpus; |
| 221 | const struct perf_cpu_map *cpu_map = NULL; | 235 | const struct perf_cpu_map *cpu_map = NULL; |
| @@ -228,17 +242,23 @@ static void build_node_mask(int node, cpu_set_t *mask) · 6 unmodified lines | |||
| 228 | for (c = 0; c < nr_cpus; c++) { | 242 | for (c = 0; c < nr_cpus; c++) { |
| 229 | cpu = cpu_map->map[c]; /* map c index to online cpu index */ | 243 | cpu = cpu_map->map[c]; /* map c index to online cpu index */ |
| 230 | if (cpu__get_node(cpu) == node) | 244 | if (cpu__get_node(cpu) == node) |
| 231 | CPU_SET(cpu, mask); | 245 | set_bit(cpu, mask->bits); |
| 232 | } | 246 | } |
| 233 | } | 247 | } |
| 234 | 248 | ||
| 235 | static void perf_mmap__setup_affinity_mask(struct mmap *map, struct mmap_params *mp) | 249 | static int perf_mmap__setup_affinity_mask(struct mmap *map, struct mmap_params *mp) |
| 236 | { | 250 | { |
| 237 | CPU_ZERO(&map->affinity_mask); | 251 | map->affinity_mask.nbits = cpu__max_cpu(); |
| 252 | map->affinity_mask.bits = bitmap_alloc(map->affinity_mask.nbits); | ||
| 253 | if (!map->affinity_mask.bits) | ||
| 254 | return -1; | ||
| 255 | |||
| 238 | if (mp->affinity == PERF_AFFINITY_NODE && cpu__max_node() > 1) | 256 | if (mp->affinity == PERF_AFFINITY_NODE && cpu__max_node() > 1) |
| 239 | build_node_mask(cpu__get_node(map->core.cpu), &map->affinity_mask); | 257 | build_node_mask(cpu__get_node(map->core.cpu), &map->affinity_mask); |
| 240 | else if (mp->affinity == PERF_AFFINITY_CPU) | 258 | else if (mp->affinity == PERF_AFFINITY_CPU) |
| 241 | CPU_SET(map->core.cpu, &map->affinity_mask); | 259 | set_bit(map->core.cpu, map->affinity_mask.bits); |
| 260 | |||
| 261 | return 0; | ||
| 242 | } | 262 | } |
| 243 | 263 | ||
| 244 | int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu) | 264 | int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu) |
| @@ -249,7 +269,15 @@ int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu) · 4 unmodified lines | |||
| 249 | return -1; | 269 | return -1; |
| 250 | } | 270 | } |
| 251 | 271 | ||
| 252 | perf_mmap__setup_affinity_mask(map, mp); | 272 | if (mp->affinity != PERF_AFFINITY_SYS && |
| 273 | perf_mmap__setup_affinity_mask(map, mp)) { | ||
| 274 | pr_debug2("failed to alloc mmap affinity mask, error %d\n", | ||
| 275 | errno); | ||
| 276 | return -1; | ||
| 277 | } | ||
| 278 | |||
| 279 | if (verbose == 2) | ||
| 280 | mmap_cpu_mask__scnprintf(&map->affinity_mask, "mmap"); | ||
| 253 | 281 | ||
| 254 | map->core.flush = mp->flush; | 282 | map->core.flush = mp->flush; |
| 255 | 283 | ||
| @@ -15,6 +15,15 @@ · 14 unmodified lines | |||
| 15 | #include "event.h" | 15 | #include "event.h" |
| 16 | 16 | ||
| 17 | struct aiocb; | 17 | struct aiocb; |
| 18 | |||
| 19 | struct mmap_cpu_mask { | ||
| 20 | unsigned long *bits; | ||
| 21 | size_t nbits; | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define MMAP_CPU_MASK_BYTES(m) \ | ||
| 25 | (BITS_TO_LONGS(((struct mmap_cpu_mask *)m)->nbits) * sizeof(unsigned long)) | ||
| 26 | |||
| 18 | /** | 27 | /** |
| 19 | * struct mmap - perf's ring buffer mmap details | 28 | * struct mmap - perf's ring buffer mmap details |
| 20 | * | 29 | * |
| @@ -31,7 +40,7 @@ struct mmap { · 10 unmodified lines | |||
| 31 | int nr_cblocks; | 40 | int nr_cblocks; |
| 32 | } aio; | 41 | } aio; |
| 33 | #endif | 42 | #endif |
| 34 | cpu_set_t affinity_mask; | 43 | struct mmap_cpu_mask affinity_mask; |
| 35 | void *data; | 44 | void *data; |
| 36 | int comp_level; | 45 | int comp_level; |
| 37 | }; | 46 | }; |
| @@ -52,4 +61,6 @@ int perf_mmap__push(struct mmap *md, void *to, · 14 unmodified lines | |||
| 52 | 61 | ||
| 53 | size_t mmap__mmap_len(struct mmap *map); | 62 | size_t mmap__mmap_len(struct mmap *map); |
| 54 | 63 | ||
| 64 | void mmap_cpu_mask__scnprintf(struct mmap_cpu_mask *mask, const char *tag); | ||
| 65 | |||
| 55 | #endif /*__PERF_MMAP_H */ | 66 | #endif /*__PERF_MMAP_H */ |
| @@ -1,4 +1,4 @@ | |||
| 1 | %pure-parser | 1 | %define api.pure full |
| 2 | %parse-param {void *_parse_state} | 2 | %parse-param {void *_parse_state} |
| 3 | %parse-param {void *scanner} | 3 | %parse-param {void *scanner} |
| 4 | %lex-param {void* scanner} | 4 | %lex-param {void* scanner} |
| @@ -324,8 +324,7 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms, · 323 unmodified lines | |||
| 324 | return ret; | 324 | return ret; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | 327 | int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) |
| 328 | size_t size, unsigned int width) | ||
| 329 | { | 328 | { |
| 330 | return _hist_entry__sym_snprintf(&he->ms, he->ip, | 329 | return _hist_entry__sym_snprintf(&he->ms, he->ip, |
| 331 | he->level, bf, size, width); | 330 | he->level, bf, size, width); |
| @@ -164,6 +164,8 @@ static __pure inline bool hist_entry__has_callchains(struct hist_entry *he) · 163 unmodified lines | |||
| 164 | return he->callchain_size != 0; | 164 | return he->callchain_size != 0; |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width); | ||
| 168 | |||
| 167 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | 169 | static inline bool hist_entry__has_pairs(struct hist_entry *he) |
| 168 | { | 170 | { |
| 169 | return !list_empty(&he->pairs.node); | 171 | return !list_empty(&he->pairs.node); |