[PGO kernel] Sync patch from openeuler/A-FOT.

(cherry picked from commit 7ff6c1772a3824ed073ca59d3cc5d8a957ee2ac9)
This commit is contained in:
xiongzhou4 2023-06-02 18:05:30 +08:00 committed by openeuler-sync-bot
parent 6e24f1830d
commit 5d81a61b98
2 changed files with 816 additions and 1 deletions

View File

@ -0,0 +1,807 @@
From 2beb891a3e87e0fa61509d3746f0e216374f6f23 Mon Sep 17 00:00:00 2001
From: xiongzhou4 <xiongzhou4@huawei.com>
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 <cstdio>
+#include <string>
+#include <vector>
+#include <cassert>
+
+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<char>& 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<char>& in, vector<string>& 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<char> 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<char> 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<char> fileNameList;
+ if (argc != 2 || ReadFile(argv[1], fileNameList)) {
+ fprintf(stderr, "USAGE:\n %s <Input File List>\n", argv[0]);
+ return 1;
+ }
+ vector<string> 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 profileall=启用完整的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

View File

@ -1,6 +1,6 @@
Name: A-FOT Name: A-FOT
Version: v1.0 Version: v1.0
Release: 2 Release: 3
Summary: automatic feedback-directed optimization tool for openEuler Summary: automatic feedback-directed optimization tool for openEuler
License: MulanPSL-2.0 License: MulanPSL-2.0
URL: https://gitee.com/openeuler/A-FOT 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 Patch1: 0001-Add-Bear-to-A-FOT.patch
Patch2: 0002-Bugfix-Remove-Backslash-in-Options.patch Patch2: 0002-Bugfix-Remove-Backslash-in-Options.patch
Patch3: 0003-PGO-kernel-Add-PGO-kernel-mode.patch
%description %description
A-FOT is an automatic feedback-directed optimization tool for openEuler A-FOT is an automatic feedback-directed optimization tool for openEuler
@ -41,6 +42,13 @@ cp split_json.py %{buildroot}/%{_bindir}
%doc README.md %doc README.md
%changelog %changelog
* Fri Jun 2 2023 Xiong Zhou <xiongzhou4@huawei.com> - 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 * Mon Dec 19 2022 huitailangzju <804544223@qq.com> - v1.0-2
- Type:Sync - Type:Sync
- ID:NA - ID:NA