diff --git a/0003-PGO-kernel-Add-PGO-kernel-mode.patch b/0003-PGO-kernel-Add-PGO-kernel-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..3ecc09c8e4ca6a6ef3e7159d0359eef83ba1c3f9 --- /dev/null +++ b/0003-PGO-kernel-Add-PGO-kernel-mode.patch @@ -0,0 +1,807 @@ +From 2beb891a3e87e0fa61509d3746f0e216374f6f23 Mon Sep 17 00:00:00 2001 +From: xiongzhou4 +Date: Mon, 29 May 2023 17:14:22 +0800 +Subject: [PATCH] [PGO kernel] Add PGO kernel mode. + +--- + GcovSummaryAddTool.cpp | 176 +++++++++++++++++++++++ + a-fot | 129 ++++++++++++++--- + a-fot.ini | 45 ++++-- + auto_kernel_pgo.sh | 310 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 630 insertions(+), 30 deletions(-) + create mode 100644 GcovSummaryAddTool.cpp + create mode 100644 auto_kernel_pgo.sh + +diff --git a/GcovSummaryAddTool.cpp b/GcovSummaryAddTool.cpp +new file mode 100644 +index 0000000..656f3d5 +--- /dev/null ++++ b/GcovSummaryAddTool.cpp +@@ -0,0 +1,176 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. ++ * This tool is used to add GCC 9 and GCC 10 summary info into gcov file. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++using namespace std; ++ ++constexpr long long GCOV_DATA_MAGIC = 0x67636461u; ++constexpr long long GCOV_TAG_FUNCTION = 0x01000000u; ++constexpr long long GCOV_TAG_COUNTER_BASE = 0x01a10000u; ++constexpr long long GCOV_TAG_OBJECT_SUMMARY = 0xa1000000u; ++ ++/* value profile counter */ ++constexpr long long GCOV_TAG_COUNTER_INTERVAL = 0x01a30000u; ++constexpr long long GCOV_TAG_COUNTER_POW2 = 0x01a50000u; ++constexpr long long GCOV_TAG_COUNTER_TOPN = 0x01a70000u; ++constexpr long long GCOV_TAG_COUNTER_IC = 0x01a90000u; // indirect call profiler ++constexpr long long GCOV_TAG_COUNTER_AVERAGE = 0x01ab0000u; ++constexpr long long GCOV_TAG_COUNTER_IOR = 0x01ad0000u; ++constexpr long long GCOV_TAG_COUNTER_TP = 0x01af0000u; // time profiler ++ ++static int ReadFile(const string filename, vector& out) ++{ ++ FILE* fp = fopen(filename.c_str(), "rb"); ++ if (!fp) { ++ fprintf(stderr, "[!] Fail to read: %s\n", filename.c_str()); ++ return 1; ++ } ++ constexpr int bufSize = 4096; ++ char buf[bufSize]; ++ size_t sz; ++ while (sz = fread(buf, 1, bufSize, fp)) { ++ out.insert(out.end(), buf, buf + sz); ++ } ++ fclose(fp); ++ return 0; ++} ++ ++static void SplitLines(const vector& in, vector& out) ++{ ++ size_t pos = 0; ++ for (size_t i = 0; i < in.size(); ++i) { ++ if (in[i] != '\r' && in[i] != '\n') { ++ continue; ++ } ++ out.push_back(string(in.begin() + pos, in.begin() + i)); ++ if (in[i] == '\r' && i + 1 < in.size() && in[i + 1] == '\n') { ++ i++; ++ } ++ pos = i + 1; ++ } ++ if (pos < in.size()) { ++ out.push_back(string(in.begin() + pos, in.end())); ++ } ++} ++ ++static int WriteFile(string fileName, const vector in, unsigned int valMax) ++{ ++ if (in.size() < 12) { ++ fprintf(stderr, "[!] Not enough size to write\n"); ++ return 1; ++ } ++ FILE* fp = fopen(fileName.c_str(), "wb"); ++ if (!fp) { ++ fprintf(stderr, "[!] Fail to write: %s \n", fileName.c_str()); ++ return 1; ++ } ++ fwrite(in.data(), 1, 12, fp); ++ unsigned int title[4] = { ++ GCOV_TAG_OBJECT_SUMMARY, ++ 2, ++ 1, ++ valMax ++ }; ++ fwrite(title, 1, 16, fp); ++ fwrite(in.data() + 12, 1, in.size() - 12, fp); ++ fclose(fp); ++ return 0; ++} ++ ++static int ProcessFile(const string fileName) ++{ ++ vector source; ++ if (ReadFile(fileName, source)) { ++ fprintf(stderr, "[!] Fail to read file: %s \n", fileName.c_str()); ++ return 1; ++ } ++ int state = 1; ++ unsigned int valMax = 0; ++ unsigned int count = 0; ++ unsigned int n = source.size() / 4; ++ auto vData = (const unsigned int*) source.data(); ++ for (int i = 0; i < n; ++i) { ++ unsigned int val = vData[i]; ++ switch (state) { ++ case 1: ++ if (val != GCOV_DATA_MAGIC) { ++ fprintf(stderr, "[!] GCOV_DATA_MAGIC mismatches: 0x%x\n", val); ++ return 1; ++ } ++ i += 2; ++ state = 2; ++ break; ++ case 2: ++ if (i == n - 1 && val) { ++ fprintf(stderr, "[!] Single last tag: 0x%x\n", val); ++ return 1; ++ } ++ if (val == GCOV_TAG_FUNCTION) { ++ i += 1 + vData[i + 1]; ++ } else if (val == GCOV_TAG_COUNTER_BASE) { ++ if (vData[i + 1] % 2) { ++ fprintf(stderr, "[!] Invalid length: %d\n", vData[i + 1]); ++ return 1; ++ } ++ count = vData[++i]; ++ if (count) { ++ state = 3; ++ } ++ } else if (val) { ++ switch (val) { ++ case GCOV_TAG_COUNTER_INTERVAL: ++ case GCOV_TAG_COUNTER_POW2: ++ case GCOV_TAG_COUNTER_TOPN: ++ case GCOV_TAG_COUNTER_IC: ++ case GCOV_TAG_COUNTER_AVERAGE: ++ case GCOV_TAG_COUNTER_IOR: ++ case GCOV_TAG_COUNTER_TP: ++ i += 1 + vData[i + 1]; ++ break; ++ default: ++ fprintf(stderr, "[!] Unknown tag: 0x%x\n", val); ++ return 1; ++ } ++ } ++ break; ++ case 3: ++ valMax = valMax < val ? val : valMax; ++ if (--count == 0) { ++ state = 2; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ if (WriteFile(fileName, source, valMax)) { ++ return 1; ++ } ++ return 0; ++} ++ ++int main(int argc, char** argv) ++{ ++ vector fileNameList; ++ if (argc != 2 || ReadFile(argv[1], fileNameList)) { ++ fprintf(stderr, "USAGE:\n %s \n", argv[0]); ++ return 1; ++ } ++ vector fileNames; ++ SplitLines(fileNameList, fileNames); ++ for (size_t i = 0; i < fileNames.size(); ++i) { ++ string fileName = fileNames[i]; ++ fprintf(stderr, "[.] Processing %s \n", fileName.c_str()); ++ if (ProcessFile(fileName)) { ++ return 1; ++ } ++ } ++ fprintf(stderr, "[.] File procesed: %d \n", (int) fileNames.size()); ++ return 0; ++} +diff --git a/a-fot b/a-fot +index c633098..1e7d08e 100755 +--- a/a-fot ++++ b/a-fot +@@ -1,11 +1,13 @@ + #!/bin/bash +-current_path=$(cd "$(dirname "$0")";pwd) +-config_file=${current_path}/a-fot.ini ++afot_path=$(cd "$(dirname "$0")";pwd) ++config_file=${afot_path}/a-fot.ini + + # Profile名字 + profile_name="profile.data" + # gcov名字 + gcov_name="profile.gcov" ++# 内核编译选项 ++kernel_configs=() + + # 解析配置文件配置项 + function parse_config() { +@@ -13,21 +15,21 @@ function parse_config() { + echo "[ERROR] Could not load config file at: ${config_file}, please check!" + exit 1 + fi +- while read line; do +- if [[ ! ${line} =~ "#" ]]; then ++ while read line || [[ -n ${line} ]]; do ++ if [[ ! ${line} =~ "#" ]] && [[ ${line} != "" ]]; then + key=$(echo ${line} | awk -F "=" '{print $1}') + value=$(echo ${line} | awk -F "=" '{print $2}') +- eval "${key}=${value}" ++ if [[ ${key} =~ "CONFIG_" ]]; then ++ kernel_configs+="${key}=${value}" ++ else ++ eval "${key}=${value}" ++ fi + fi + done <${config_file} + } + + # 解析输入的参数 + function parse_input_params() { +- if [[ $# == 1 ]]; then +- suggest_info +- exit 1 +- fi + while [ $# -ge 2 ]; do + case $1 in + --opt_mode) +@@ -78,12 +80,71 @@ function parse_input_params() { + build_mode=$2 + shift 2 + ;; ++ --pgo_mode) ++ pgo_mode=$2 ++ shift 2 ++ ;; ++ --pgo_phase) ++ pgo_phase=$2 ++ shift 2 ++ ;; ++ --kernel_src) ++ kernel_src=$2 ++ shift 2 ++ ;; ++ --kernel_name) ++ kernel_name=$2 ++ shift 2 ++ ;; ++ --run_time) ++ run_time=$2 ++ shift 2 ++ ;; ++ --CONFIG_*) ++ kernel_configs+="${1:2}=$2" ++ shift 2 ++ ;; ++ --last_time) ++ last_time=$2 ++ shift 2 ++ ;; ++ -s) ++ silent=1 ++ shift ++ ;; ++ -n) ++ disable_compilation=1 ++ shift ++ ;; ++ --makefile) ++ makefile=$2 ++ shift 2 ++ ;; ++ --kernel_config) ++ kernel_config=$2 ++ shift 2 ++ ;; ++ --data_dir) ++ data_dir=$2 ++ shift 2 ++ ;; + *) + suggest_info + exit 1 + ;; + esac + done ++ ++ if [[ $# == 1 ]]; then ++ if [[ $1 == "-s" ]]; then ++ silent=1 ++ elif [[ $1 == "-n" ]]; then ++ disable_compilation=1 ++ else ++ suggest_info ++ exit 1 ++ fi ++ fi + } + + function check_config_item() { +@@ -138,6 +199,7 @@ function suggest_info() { + echo """ + Usage: a-fot [OPTION1 ARG1] [OPTION2 ARG2] [...] + ++For perf mode: + --config_file Path of configuration file + --opt_mode Optimization modes (AutoFDO/AutoPrefetch/AutoBOLT) + --perf_time Perf sampling duration (unit: seconds) +@@ -146,10 +208,28 @@ Usage: a-fot [OPTION1 ARG1] [OPTION2 ARG2] [...] + --bin_file Executable binary file path + --build_script Application build script path + --work_path Script working directory (used to compile the application and store the profile) +---run_script Script path for run application ++--run_script Script path for running application + --max_waiting_time Maximum binary startup time (unit: seconds) + --check_success Check optimization result +---build_mode Execute build scrip mode (Wrapper/Bear) ++--build_mode Execute build script mode (Wrapper/Bear) ++ ++For kernel PGO mode: ++--config_file Path of configuration file ++--opt_mode Optimization mode (Auto_kernel_PGO) ++--pgo_mode PGO mode (arc/all) ++--pgo_phase Phase of kernel PGO (1/2) ++--kernel_src Kernel source directory ++--kernel_name Kernel local version name (will be appended with "-pgoing" or "-pgoed") ++--work_path Script working directory (used to store the profile and the log) ++--run_script Script path for running application ++--gcc_path Compiler gcc path ++--CONFIG_... Kernel building ++--last_time Last time directory before rebooting (used to put log infos together) ++-s Silent mode (reboot automatically after kernel installation) ++-n Do not compile kernel automatically ++--makefile Makefile path of kernel ++--kernel_config Config file path of kernel ++--data_dir Profile path generated by kernel + """ + } + +@@ -157,13 +237,13 @@ Usage: a-fot [OPTION1 ARG1] [OPTION2 ARG2] [...] + function load_script() { + case ${opt_mode} in + "AutoFDO") +- source ${current_path}/auto_fdo.sh ++ source ${afot_path}/auto_fdo.sh + ;; + "AutoPrefetch") +- source ${current_path}/auto_prefetch.sh ++ source ${afot_path}/auto_prefetch.sh + ;; + "AutoBOLT") +- source ${current_path}/auto_bolt.sh ++ source ${afot_path}/auto_bolt.sh + ;; + *) + echo "[ERROR] Optimization mode ${opt_mode} is not supported, Check the configuration item: opt_mode" +@@ -193,7 +273,7 @@ function check_common_dependency() { + # 拆分编译数据库 + function split_option() { + if [ "$bear_prefix" ];then +- python3 $current_path/split_json.py -i $PWD/compile_commands.json ++ python3 $afot_path/split_json.py -i $PWD/compile_commands.json + mv $PWD/compile_commands.json $PWD/compile_commands_$1.json + mv $PWD/compile_commands.fail.json $PWD/compile_commands.fail_$1.json + fi +@@ -289,7 +369,7 @@ function second_compilation() { + # 判断上一步执行是否成功 + function is_success() { + pre_result=$1 +- if [[ ${pre_result} -eq 1 ]]; then ++ if [[ ${pre_result} -ne 0 ]]; then + echo "[ERROR] Execution failed, please check the log: ${log_file}" + exit 1 + fi +@@ -342,11 +422,10 @@ function is_opt_success() { + exit 0 + } + +-#执行入口,部分函数为加载不同优化脚本中得到 +-function main() { +- parse_input_params "$@" +- parse_config +- parse_input_params "$@" ++function do_optimization() { ++ if [[ ${opt_mode} == Auto_kernel_PGO ]]; then ++ source ${afot_path}/auto_kernel_pgo.sh ++ fi + check_config_item + init_profile_and_log + load_script +@@ -361,6 +440,14 @@ function main() { + prepare_new_env + second_compilation + is_opt_success ++} ++ ++#执行入口,部分函数为加载不同优化脚本中得到 ++function main() { ++ parse_input_params "$@" ++ parse_config ++ parse_input_params "$@" ++ do_optimization + exit "$?" + } + +diff --git a/a-fot.ini b/a-fot.ini +index b395504..c5da1b0 100644 +--- a/a-fot.ini ++++ b/a-fot.ini +@@ -1,23 +1,50 @@ ++# 文件和目录请使用绝对路径 ++ ++# 优化模式(AutoFDO、AutoPrefetch、AutoBOLT、Auto_kernel_PGO) ++opt_mode=Auto_kernel_PGO ++# 脚本工作目录(用来编译应用程序/存放profile、日志) ++work_path=/opt ++# 应用运行脚本路径 ++run_script=/root/run.sh ++# GCC路径(bin、lib的父目录) ++gcc_path=/usr ++ ++# AutoFDO、AutoPrefetch、AutoBOLT ++# 针对应用的三种优化模式,请填写此部分配置 ++ + # 应用进程名 + application_name=test + # 二进制安装后可执行文件 + bin_file=/tmp/test +-# 脚本工作目录(用来编译应用程序/存放profile) +-work_path=/tmp/ + # 应用构建脚本路径 + build_script=/root/build.sh +-# Benhmark路径及运行命令 +-run_script=/root/benchmark.sh + # 最大二进制启动时间(单位:秒) + max_waiting_time=600 +-# 优化模式(AutoFDO、AutoPrefetch、AutoBOLT) +-opt_mode=AutoPrefetch + # Perf采样时长(单位:秒) + perf_time=100 +-# gcc路径 +-gcc_path=/usr/ + # 检测是否优化成功(1=启用,0=禁用) + check_success=0 + # 构建模式 (Bear、Wrapper) + build_mode=Bear +-# 结束行勿删 +\ No newline at end of file ++ ++# auto_kernel_PGO ++# 针对内核的优化模式,请填写此部分配置 ++ ++# 内核PGO模式(arc=只启用arc profile,all=启用完整的PGO优化) ++pgo_mode=all ++# 执行阶段(1=编译插桩内核阶段,2=编译优化内核阶段) ++pgo_phase=1 ++# 内核源码目录(不指定则自动下载) ++kernel_src=/opt/kernel ++# 内核构建的本地名(将根据阶段添加"-pgoing"或"-pgoed"后缀) ++kernel_name=kernel ++# 内核编译选项(请确保选项修改正确合法,不会造成内核编译失败) ++#CONFIG_...=y ++# 重启前的时间目录(用于将同一套流程的日志存放在一起) ++last_time= ++# 内核源码的Makefile地址(用于不自动编译内核的场景) ++makefile= ++# 内核配置文件路径(用于不自动编译内核的场景) ++kernel_config= ++# 内核生成的原始profile目录(用于不自动编译内核的场景) ++data_dir= +\ No newline at end of file +diff --git a/auto_kernel_pgo.sh b/auto_kernel_pgo.sh +new file mode 100644 +index 0000000..0b62220 +--- /dev/null ++++ b/auto_kernel_pgo.sh +@@ -0,0 +1,310 @@ ++#!/bin/bash ++ ++parallel=$(cat /proc/cpuinfo | grep "processor" | wc -l) ++ ++# 检查某个配置项是否存在 ++function check_item() { ++ if [[ -z $1 ]]; then ++ echo "[ERROR] The configuration item '$2' is missing, please check!" ++ exit 1 ++ fi ++} ++ ++# 检查所有配置项 ++function check_config_items() { ++ check_item ${pgo_phase} pgo_phase ++ if [[ ${pgo_phase} -ne 1 ]] && [[ ${pgo_phase} -ne 2 ]]; then ++ echo "[ERROR] The value of configuration item 'pgo_phase' is invalid, please check!" ++ exit 1 ++ fi ++ if [[ ${pgo_phase} -eq 1 ]]; then ++ check_item ${pgo_mode} pgo_mode ++ if [[ ${pgo_mode} != "arc" ]] && [[ ${pgo_mode} != "all" ]]; then ++ echo "[ERROR] The value of configuration item 'pgo_mode' is invalid, please check!" ++ exit 1 ++ fi ++ fi ++ ++ check_item ${kernel_name} kernel_name ++ check_item ${work_path} work_path ++ if [[ ${work_path} =~ "/tmp" ]]; then ++ echo "[ERROR] Do not put work path under /tmp, or it will be cleaned after rebooting." ++ fi ++ check_item ${run_script} run_script ++ check_item ${gcc_path} gcc_path ++ ++ if [[ -n ${disable_compilation} ]]; then ++ check_item ${makefile} makefile ++ is_file_exist ${makefile} "makefile" ++ if [[ ${pgo_phase} -eq 1 ]]; then ++ check_item ${kernel_config} kernel_config ++ is_file_exist ${kernel_config} "kernel_config" ++ fi ++ if [[ ${pgo_phase} -eq 2 ]]; then ++ check_item ${data_dir} data_dir ++ if [[ ! -d ${data_dir} ]]; then ++ echo "[ERROR] GCOV data directory ${data_dir} does not exist." ++ exit 1 ++ fi ++ fi ++ fi ++} ++ ++# 初始化文件和目录 ++function init() { ++ if [[ -z ${last_time} ]]; then ++ now_time=$(date '+%Y%m%d-%H%M%S') ++ else ++ now_time=${last_time} ++ fi ++ time_dir=${work_path}/${now_time} ++ log_file=${time_dir}/log ++ ++ if [[ -d ${time_dir} ]]; then ++ echo "[INFO] Use last time directory: ${time_dir}" ++ else ++ mkdir -p ${time_dir} ++ echo "[INFO] Create time directory: ${time_dir}" ++ fi ++ ++ if [[ -f ${log_file} ]]; then ++ echo "[INFO] Use last log: ${log_file}" ++ else ++ touch ${log_file} ++ echo "[INFO] Init log file: ${log_file}" ++ fi ++} ++ ++# 检测环境 ++function check_dependency() { ++ sed -i '/a-fot/d' /etc/rc.d/rc.local ++ ++ get_arch=$(arch) ++ if [[ ${get_arch} =~ "x86_64" || ${get_arch} =~ "aarch64" ]]; then ++ echo "[INFO] Current arch: ${get_arch}" ++ else ++ echo "[ERROR] Unsupport arch: ${get_arch}" ++ exit 1 ++ fi ++ is_file_exist ${run_script} "run_script" ++ is_file_exist "${gcc_path}/bin/gcc" "gcc_path" ++ ++ export CC=${gcc_path}/bin/gcc ++ export PATH=${gcc_path}/bin:${PATH} ++ export LD_LIBRARY_PATH=${gcc_path}/lib64:${LD_LIBRARY_PATH} ++ ++ if [[ ${pgo_phase} -eq 1 ]] && [[ ${pgo_mode} == "all" ]] && [[ -z ${disable_compilation} ]]; then ++ if echo 'int main() { return 0; }' | gcc -x c - -o /dev/null -Werror -fkernel-pgo >/dev/null 2>&1; then ++ echo "[INFO] Current GCC supports kernel PGO in runtime, use -fkernel-pgo option." ++ option="-fkernel-pgo" ++ elif gcc -v 2>&1 | grep -- "--disable-tls" >/dev/null; then ++ echo "[INFO] Current GCC is recompiled with --disable-tls, does support kernel PGO in runtime." ++ else ++ echo "[ERROR] Current GCC does not support kernel PGO, you may use openeuler GCC or recompile GCC with --disable-tls --disable-libsanitizer." ++ fi ++ fi ++} ++ ++# 修改内核配置文件 ++function modify_kernel_config() { ++ if [[ ${pgo_mode} == "all" ]]; then ++ if ! grep "PGO" $1 >/dev/null; then ++ echo "[ERROR] Current version of kernel does not support PGO, please use newer ones or choose arc mode." ++ exit 1 ++ fi ++ sed -i 's/.*CONFIG_PGO_KERNEL.*/CONFIG_PGO_KERNEL=y/' $1 ++ fi ++ for config in ${kernel_configs[@]}; do ++ sed -i "s/.*${config%%=*}.*/${config}/" $1 ++ done ++ sed -i "s/CONFIG_LOCALVERSION=\"\"/CONFIG_LOCALVERSION=\"-${kernel_name}-pgoing\"/" $1 ++ sed -i 's/.*CONFIG_GCOV_KERNEL.*/CONFIG_GCOV_KERNEL=y/' $1 ++ sed -i '/CONFIG_ARCH_HAS_GCOV_PROFILE_ALL/ a\CONFIG_GCOV_PROFILE_ALL=y' $1 ++ sed -i '/CONFIG_CC_HAS_ASM_INLINE/ a\CONFIG_CONSTRUCTORS=y' $1 ++ sed -i '/CONFIG_TRACE_EVAL_MAP_FILE/ a\# CONFIG_GCOV_PROFILE_FTRACE is not set' $1 ++} ++ ++# 编译插桩版本的内核 ++function first_compilation() { ++ if [[ -n ${disable_compilation} ]]; then ++ modify_kernel_config ${kernel_config} ++ echo "[INFO] Kernel configuration has been generated, the path is: ${kernel_config}" ++ if [[ -z ${option} ]]; then ++ sed -i "/KBUILD_CFLAGS += \$(KCFLAGS)/ a\KBUILD_CFLAGS += fkernel-pgo" ${makefile} ++ fi ++ echo "[INFO] Kernel makefile has been generated, the path is: ${makefile}" ++ exit 0 ++ fi ++ ++ if [[ -z ${kernel_src} ]]; then ++ echo "[WARNING] ${kernel_src} is not specified, A-FOT will download and build kernel source code automatically in 10 seconds." ++ sleep 10 ++ oe_version=$(grep 'openeulerversion' /etc/openEuler-latest | cut -d '=' -f 2) ++ echo "[INFO] Current openEuler version: ${oe_version}" ++ url="https://repo.huaweicloud.com/openeuler/${oe_version}/source/Packages/" ++ echo "[INFO] Download the kernel source rpm package from ${url}" ++ cd ${work_path} ++ wget --no-parent --no-directories -r -A 'kernel-[0-9]*.rpm' ${url} --no-check-certificate >>${log_file} 2>&1 ++ is_success $? ++ srpm=$(ls -t kernel-[0-9]*.rpm 2>/dev/null | head -n 1) ++ echo "[INFO] Successfully downloaded kernel source rpm package: ${srpm}" ++ echo "[INFO] Build kernel source code." ++ rpm -ivh ${srpm} >>${log_file} 2>&1 ++ cd - >/dev/null ++ rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec >>${log_file} 2>&1 ++ is_success $? ++ src_dir=$(ls -td ~/rpmbuild/BUILD/kernel-*/*-source/ | head -n 1) ++ cp -r ${src_dir} ${work_path}/kernel ++ kernel_src=${work_path}/kernel ++ echo "[INFO] Successfully builded kernel source code: ${kernel_src}" ++ else ++ echo "[INFO] Build kernel in an existing source directory." ++ fi ++ ++ if [[ ! -d ${kernel_src} ]]; then ++ echo "[ERROR] Kernel source directory ${kernel_src} does not exist." ++ exit 1 ++ fi ++ ++ cd ${kernel_src} ++ make openeuler_defconfig ++ modify_kernel_config .config ++ echo "[INFO] Start PGO kernel compilation." ++ Arch=$(arch | sed -e s/x86_64/x86/ -e s/aarch64.*/arm64/) ++ (make clean -j ${parallel} && make KCFLAGS="${option}" ARCH=${Arch} -j ${parallel} && make modules_install ARCH=${Arch} -j ${parallel} && make install ARCH=${Arch} -j ${parallel}) 2>&1 | tee -a ${log_file} ++ is_success ${PIPESTATUS[0]} ++ cd - >/dev/null ++} ++ ++# 第一次重启操作 ++function first_reboot() { ++ grub2-set-default 0 ++ next_cmd="${afot_path}/a-fot --opt_mode Auto_kernel_PGO --pgo_phase 2 --kernel_src ${kernel_src} --kernel_name ${kernel_name} --work_path ${work_path} --run_script ${run_script} --gcc_path ${gcc_path} --last_time ${now_time} -s" ++ if [[ -z ${silent} ]]; then ++ read -p $'PGO kernel has been successfully installed. Reboot now to use?\nPress [y] to reboot now, [n] to reboot yourself later: ' reboot_p ++ if [[ ${reboot_p} == "y" ]]; then ++ echo "[WARNING] System will be rebooted in 10 seconds!!!" ++ echo -e "[INFO] Please run this command to continue after rebooting:\n${next_cmd}" ++ sleep 10 ++ reboot ++ else ++ echo -e "[INFO] Please run this command to continue after rebooting:\n${next_cmd}" ++ fi ++ else ++ echo ${next_cmd} >>/etc/rc.d/rc.local ++ chmod u+x /etc/rc.d/rc.local ++ echo "[WARNING] System will be rebooted in 10 seconds!!!" ++ sleep 10 ++ reboot ++ fi ++ exit 0 ++} ++ ++# 执行应用程序执行脚本 ++function execute_run_script() { ++ echo "[INFO] Start to execute the run_script: ${run_script}" ++ /bin/bash ${run_script} >>${log_file} 2>&1 ++ is_success $? ++} ++ ++# 收集和处理profile文件 ++function process_profiles() { ++ if [[ -z ${disable_compilation} ]]; then ++ data_dir=/sys/kernel/debug/gcov ++ fi ++ if [[ ! -d ${data_dir} ]]; then ++ echo "[ERROR] GCOV data directory ${data_dir} does not exist." ++ exit 1 ++ fi ++ ++ temp_dir=$(mktemp -d) ++ cd ${work_path} ++ echo "[INFO] Start collecting the profiles." ++ find ${data_dir} -type d -exec mkdir -p ${temp_dir}/\{\} \; ++ find ${data_dir} -name '*.gcda' -exec sh -c 'cat < $0 > '${temp_dir}'/$0' {} \; ++ find ${data_dir} -name '*.gcno' -exec sh -c 'cp -d $0 '${temp_dir}'/$0' {} \; ++ ++ echo "[INFO] Start post-processing the profiles." ++ find ${temp_dir} -name '*.gcda' >list.txt ++ /usr/bin/g++ ${afot_path}/GcovSummaryAddTool.cpp -o calcsum ++ ./calcsum list.txt ++ rm -f list.txt ++ ++ profile_dir=${time_dir}/gcovdata ++ rm -rf ${profile_dir} ++ mkdir ${profile_dir} ++ for file in $(find ${temp_dir} -name '*.gcda'); do ++ hash_path=$(echo ${file//\//\#}) ++ name=$(echo ${hash_path#*gcov}) ++ mv $file ${profile_dir}/$name ++ done ++ rm -rf ${temp_dir} ++ cd - >/dev/null ++ echo "[INFO] Profiles have been successfully processed, the path is: ${profile_dir}" ++} ++ ++# 使用profile编译优化的内核 ++function second_compilation() { ++ if [[ -n ${disable_compilation} ]]; then ++ sed -i "/KBUILD_CFLAGS += \$(KCFLAGS)/ a\KBUILD_CFLAGS += -fprofile-use -fprofile-correction -Wno-error=coverage-mismatch -fprofile-dir=${profile_dir}" ${makefile} ++ echo "[INFO] Kernel makefile has been generated, the path is: ${makefile}" ++ exit 0 ++ fi ++ ++ if [[ -z ${kernel_src} ]]; then ++ kernel_src=${work_path}/kernel ++ fi ++ if [[ ! -d ${kernel_src} ]]; then ++ echo "[ERROR] Kernel source directory ${kernel_src} does not exist." ++ exit 1 ++ fi ++ ++ cd ${kernel_src} ++ make openeuler_defconfig ++ for config in ${kernel_configs[@]}; do ++ sed -i 's/.*${config%%=*}.*/${config}/' .config ++ done ++ sed -i "s/CONFIG_LOCALVERSION=\"\"/CONFIG_LOCALVERSION=\"-${kernel_name}-pgoed\"/" .config ++ echo "[INFO] Start optimized kernel compilation." ++ Arch=$(arch | sed -e s/x86_64/x86/ -e s/aarch64.*/arm64/) ++ (make clean -j ${parallel} && make KCFLAGS="-fprofile-use -fprofile-correction -Wno-error=coverage-mismatch -fprofile-dir=${profile_dir}" -j ${parallel} && make modules_install ARCH=${Arch} -j ${parallel} && make install ARCH=${Arch} -j ${parallel}) 2>&1 | tee -a ${log_file} ++ is_success ${PIPESTATUS[0]} ++ cd - >/dev/null ++} ++ ++# 第二次重启操作 ++function second_reboot() { ++ grub2-set-default 0 ++ if [[ -z ${silent} ]]; then ++ read -p $'Optimized kernel has been successfully installed. Reboot now to use?\nPress [y] to reboot now, [n] to reboot yourself later: ' reboot_p ++ fi ++ if [[ -n ${silent} ]] || [[ ${reboot_p} == "y" ]]; then ++ echo "[WARNING] System will be rebooted in 10 seconds!!!" ++ sleep 10 ++ reboot ++ exit 0 ++ fi ++} ++ ++# 执行入口 ++function main() { ++ check_config_items ++ if [[ ${pgo_phase} -eq 1 ]]; then ++ check_dependency ++ init ++ first_compilation ++ first_reboot ++ fi ++ if [[ ${pgo_phase} -eq 2 ]]; then ++ check_dependency ++ init ++ execute_run_script ++ process_profiles ++ second_compilation ++ second_reboot ++ fi ++ exit "$?" ++} ++ ++main ++exit "$?" +-- +2.33.0 + diff --git a/A-FOT.spec b/A-FOT.spec index 3420f358f0592a85ff98d6dc41d47fdd04935314..24f8c0ec0462acd6833b4616930a88a8758f1ad1 100644 --- a/A-FOT.spec +++ b/A-FOT.spec @@ -1,6 +1,6 @@ Name: A-FOT Version: v1.0 -Release: 2 +Release: 3 Summary: automatic feedback-directed optimization tool for openEuler License: MulanPSL-2.0 URL: https://gitee.com/openeuler/A-FOT @@ -11,6 +11,7 @@ Requires: gcc gcc-c++ autofdo llvm-bolt Bear python3 Patch1: 0001-Add-Bear-to-A-FOT.patch Patch2: 0002-Bugfix-Remove-Backslash-in-Options.patch +Patch3: 0003-PGO-kernel-Add-PGO-kernel-mode.patch %description A-FOT is an automatic feedback-directed optimization tool for openEuler @@ -41,6 +42,13 @@ cp split_json.py %{buildroot}/%{_bindir} %doc README.md %changelog +* Fri Jun 2 2023 Xiong Zhou - v1.0-3 +- Type:Sync +- ID:NA +- SUG:NA +- DESC:Sync patch from openeuler/A-FOT + Add PGO kernel mode. + * Mon Dec 19 2022 huitailangzju <804544223@qq.com> - v1.0-2 - Type:Sync - ID:NA