From 2872514ea26634954ebf05f31ee1691111502df5 Mon Sep 17 00:00:00 2001 From: xiongzhou4 Date: Tue, 16 May 2023 19:13:45 +0800 Subject: [PATCH] GCC: Add value profile support for kernel. GCC inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I734PM --------------------------------- This feature add value profile support for kernel by changing GCOV option "-fprofile-arcs" to "-fprofile-generate" when the new added config "PGO_KERNEL" is set to y. Like GCOV, the symbols required by value profile are migrated from GCC source codes as they cannot be linked to kernel. Specifically, from libgcc/libgcov-profiler.c to kernel/gcov/gcc_base.c. kernel options: CONFIG_PGO_KERNEL=y Signed-off-by: Xiong Zhou Reviewed-by: Li Yancheng --- Makefile | 7 +- arch/arm64/configs/openeuler_defconfig | 1 + arch/arm64/kvm/hyp/vhe/Makefile | 2 + arch/um/Makefile-skas | 7 +- arch/um/scripts/Makefile.rules | 6 ++ arch/x86/configs/openeuler_defconfig | 1 + arch/x86/um/vdso/Makefile | 5 + drivers/scsi/lpfc/Makefile | 4 + kernel/gcov/Makefile | 6 ++ kernel/gcov/gcc_4_7.c | 14 +-- kernel/gcov/gcc_base.c | 133 +++++++++++++++++++++++++ lib/Kconfig.debug | 12 +++ openEuler/MAINTAINERS | 7 ++ 13 files changed, 197 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 18c3521b7a92..8d6c822984a7 100644 --- a/Makefile +++ b/Makefile @@ -662,7 +662,12 @@ endif # KBUILD_EXTMOD # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage \ +ifeq ($(CONFIG_PGO_KERNEL),y) +CFLAGS_GCOV := -fprofile-generate +else +CFLAGS_GCOV := -fprofile-arcs +endif +CFLAGS_GCOV += -ftest-coverage \ $(call cc-option,-fno-tree-loop-im) \ $(call cc-disable-warning,maybe-uninitialized,) export CFLAGS_GCOV diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 1eb5b7a052f1..af9368d8a6b6 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -7067,6 +7067,7 @@ CONFIG_SECTION_MISMATCH_WARN_ONLY=y CONFIG_ARCH_WANT_FRAME_POINTERS=y CONFIG_FRAME_POINTER=y # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_PGO_KERNEL is not set # end of Compile-time checks and compiler options # diff --git a/arch/arm64/kvm/hyp/vhe/Makefile b/arch/arm64/kvm/hyp/vhe/Makefile index 461e97c375cc..f2aa78486227 100644 --- a/arch/arm64/kvm/hyp/vhe/Makefile +++ b/arch/arm64/kvm/hyp/vhe/Makefile @@ -9,3 +9,5 @@ ccflags-y := -D__KVM_VHE_HYPERVISOR__ obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ ../fpsimd.o ../hyp-entry.o + +GCOV_PROFILE_switch.o := n diff --git a/arch/um/Makefile-skas b/arch/um/Makefile-skas index ac35de5316a6..23b35335f46e 100644 --- a/arch/um/Makefile-skas +++ b/arch/um/Makefile-skas @@ -1,10 +1,15 @@ -# +# # Copyright (C) 2002 Jeff Dike (jdike@karaya.com) # Licensed under the GPL # GPROF_OPT += -pg + +ifeq ($(CONFIG_PGO_KERNEL),y) +GCOV_OPT += -fprofile-generate -ftest-coverage +else GCOV_OPT += -fprofile-arcs -ftest-coverage +endif CFLAGS-$(CONFIG_GCOV) += $(GCOV_OPT) CFLAGS-$(CONFIG_GPROF) += $(GPROF_OPT) diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules index a4dfa7d7636e..60d01d68a84e 100644 --- a/arch/um/scripts/Makefile.rules +++ b/arch/um/scripts/Makefile.rules @@ -22,6 +22,12 @@ $(USER_OBJS) $(UNPROFILE_OBJS): \ CHECKFLAGS := $(patsubst $(NOSTDINC_FLAGS),,$(CHECKFLAGS)) # The stubs can't try to call mcount or update basic block data +ifeq ($(CONFIG_PGO_KERNEL),y) +define unprofile + $(patsubst -pg,,$(patsubst -fprofile-generate -ftest-coverage,,$(1))) +endef +else define unprofile $(patsubst -pg,,$(patsubst -fprofile-arcs -ftest-coverage,,$(1))) endef +endif diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 11323cdc3301..a2303636a363 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -8158,6 +8158,7 @@ CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_SECTION_MISMATCH_WARN_ONLY=y CONFIG_STACK_VALIDATION=y # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_PGO_KERNEL is not set # end of Compile-time checks and compiler options # diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile index 5ca366e15c76..0412217a8ca0 100644 --- a/arch/x86/um/vdso/Makefile +++ b/arch/x86/um/vdso/Makefile @@ -50,8 +50,13 @@ $(vobjs): KBUILD_CFLAGS += $(CFL) # # vDSO code runs in userspace and -pg doesn't help with profiling anyway. # +ifeq ($(CONFIG_PGO_KERNEL),y) +CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-generate -ftest-coverage +CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-generate -ftest-coverage +else CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage +endif # # The DSO images are built using a special linker script. diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile index 092a971d066b..4381b472e0b0 100644 --- a/drivers/scsi/lpfc/Makefile +++ b/drivers/scsi/lpfc/Makefile @@ -21,7 +21,11 @@ # *******************************************************************/ ###################################################################### +ifeq ($(CONFIG_PGO_KERNEL),y) +ccflags-$(GCOV) := -fprofile-generate -ftest-coverage +else ccflags-$(GCOV) := -fprofile-arcs -ftest-coverage +endif ccflags-$(GCOV) += -O0 ifdef WARNINGS_BECOME_ERRORS diff --git a/kernel/gcov/Makefile b/kernel/gcov/Makefile index 16f8ecc7d882..e7810cd273f2 100644 --- a/kernel/gcov/Makefile +++ b/kernel/gcov/Makefile @@ -1,4 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 + +# GCOV must not instrument itself in value profile, or kernel cannot be booted. +ifeq ($(CONFIG_PGO_KERNEL),y) +GCOV_PROFILE := n +endif + ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' obj-y := base.o fs.o diff --git a/kernel/gcov/gcc_4_7.c b/kernel/gcov/gcc_4_7.c index c699feda21ac..96b2efde5478 100644 --- a/kernel/gcov/gcc_4_7.c +++ b/kernel/gcov/gcc_4_7.c @@ -318,14 +318,16 @@ struct gcov_info *gcov_info_dup(struct gcov_info *info) for (ct_idx = 0; ct_idx < active; ct_idx++) { cv_size = sizeof(gcov_type) * sci_ptr->num; + /* The situation may exist where cv_size=0 in value profile. */ + if (cv_size != 0) { + dci_ptr->values = vmalloc(cv_size); - dci_ptr->values = vmalloc(cv_size); + if (!dci_ptr->values) + goto err_free; - if (!dci_ptr->values) - goto err_free; - - dci_ptr->num = sci_ptr->num; - memcpy(dci_ptr->values, sci_ptr->values, cv_size); + dci_ptr->num = sci_ptr->num; + memcpy(dci_ptr->values, sci_ptr->values, cv_size); + } sci_ptr++; dci_ptr++; diff --git a/kernel/gcov/gcc_base.c b/kernel/gcov/gcc_base.c index 3cf736b9f880..5c5bc3caa348 100644 --- a/kernel/gcov/gcc_base.c +++ b/kernel/gcov/gcc_base.c @@ -84,3 +84,136 @@ void __gcov_exit(void) /* Unused. */ } EXPORT_SYMBOL(__gcov_exit); + +#ifdef CONFIG_PGO_KERNEL +/* Number of top N value histogram. */ +#define GCOV_TOPN_VALUES 4 + +void __gcov_merge_topn(gcov_type *counters, unsigned int n_counters) +{ + /* Unused. */ +} +EXPORT_SYMBOL(__gcov_merge_topn); + +struct indirect_call_tuple { + void *callee; + + gcov_type *counters; +}; + +/* Kernel does not support __thread keyword. */ +struct indirect_call_tuple __gcov_indirect_call; +EXPORT_SYMBOL(__gcov_indirect_call); + +gcov_type __gcov_time_profiler_counter; +EXPORT_SYMBOL(__gcov_time_profiler_counter); + +/* + * Tries to determine N most commons value among its inputs. + */ +static inline void __gcov_topn_values_profiler_body(gcov_type *counters, + gcov_type value) +{ + int empty_counter = -1; + unsigned int i; + + counters[0]++; + ++counters; + + /* First try to find an existing value. */ + for (i = 0; i < GCOV_TOPN_VALUES; i++) + if (value == counters[2 * i]) { + counters[2 * i + 1] += GCOV_TOPN_VALUES; + return; + } else if (counters[2 * i + 1] <= 0) + empty_counter = i; + + /* Find an empty slot for a new value. */ + if (empty_counter != -1) { + counters[2 * empty_counter] = value; + counters[2 * empty_counter + 1] = GCOV_TOPN_VALUES; + return; + } + + /* + * We haven't found an empty slot, then decrement all + * counter values by one. + */ + for (i = 0; i < GCOV_TOPN_VALUES; i++) + counters[2 * i + 1]--; +} + +void __gcov_topn_values_profiler(gcov_type *counters, gcov_type value) +{ + __gcov_topn_values_profiler_body(counters, value); +} +EXPORT_SYMBOL(__gcov_topn_values_profiler); + +/* + * Tries to determine the most common value among its inputs. + */ +static inline void __gcov_indirect_call_profiler_body(gcov_type value, + void *cur_func) +{ + /* Removed the C++ virtual tables contents as kernel is written in C. */ + if (cur_func == __gcov_indirect_call.callee) + __gcov_topn_values_profiler_body(__gcov_indirect_call.counters, value); + + __gcov_indirect_call.callee = NULL; +} + +void __gcov_indirect_call_profiler_v4(gcov_type value, void *cur_func) +{ + __gcov_indirect_call_profiler_body(value, cur_func); +} +EXPORT_SYMBOL(__gcov_indirect_call_profiler_v4); + +/* + * Increase corresponding COUNTER by VALUE. + */ +void __gcov_average_profiler(gcov_type *counters, gcov_type value) +{ + counters[0] += value; + counters[1]++; +} +EXPORT_SYMBOL(__gcov_average_profiler); + +void __gcov_ior_profiler(gcov_type *counters, gcov_type value) +{ + *counters |= value; +} +EXPORT_SYMBOL(__gcov_ior_profiler); + +/* + * If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise + * COUNTERS[0] is incremented. + */ +void __gcov_pow2_profiler(gcov_type *counters, gcov_type value) +{ + if (value == 0 || (value & (value - 1))) + counters[0]++; + else + counters[1]++; +} +EXPORT_SYMBOL(__gcov_pow2_profiler); + +/* + * If VALUE is in interval , then increases the + * corresponding counter in COUNTERS. If the VALUE is above or below + * the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased + * instead. + */ +void __gcov_interval_profiler(gcov_type *counters, gcov_type value, + int start, unsigned int steps) +{ + gcov_type delta = value - start; + + if (delta < 0) + counters[steps + 1]++; + else if (delta >= steps) + counters[steps]++; + else + counters[delta]++; +} +EXPORT_SYMBOL(__gcov_interval_profiler); +#endif diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index f53afec6f7ae..b992a8752b8a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -439,6 +439,18 @@ config DEBUG_FORCE_WEAK_PER_CPU To ensure that generic code follows the above rules, this option forces all percpu variables to be defined as weak. +config PGO_KERNEL + bool "Enable profile guided optimization for kernel" + depends on CC_IS_GCC && GCC_VERSION > 100000 + depends on !COMPILE_TEST && DEBUG_KERNEL && DEBUG_FS + select GCOV_KERNEL + select GCOV_PROFILE_ALL + default n + help + This option enables profile guided optimization for kernel. + + If unsure, say N. + endmenu # "Compiler options" menu "Generic Kernel Debugging Instruments" diff --git a/openEuler/MAINTAINERS b/openEuler/MAINTAINERS index a5913e881d5f..5f2adbe1d300 100644 --- a/openEuler/MAINTAINERS +++ b/openEuler/MAINTAINERS @@ -148,6 +148,13 @@ F: drivers/i2c/busses/i2c-hisi.c F: drivers/spi/spi-hisi-* F: drivers/gpio/gpio-hisi.c +PGO KERNEL +M: Yancheng Li +M: Zhiheng Xie +S: Maintained +F: kernel/gcov/gcc_4_7.c +F: kernel/gcov/gcc_base.c + RAS Public Architecture M: Xiaofei Tan S: Maintained -- Gitee