A-FOT/0003-PGO-kernel-Add-PGO-kernel-mode.patch
xiongzhou4 5d81a61b98 [PGO kernel] Sync patch from openeuler/A-FOT.
(cherry picked from commit 7ff6c1772a3824ed073ca59d3cc5d8a957ee2ac9)
2023-06-02 18:50:12 +08:00

808 lines
26 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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