diff --git a/.github/workflows/llvm-18.yml b/.github/workflows/llvm-18.yml new file mode 100644 index 0000000..ac70757 --- /dev/null +++ b/.github/workflows/llvm-18.yml @@ -0,0 +1,102 @@ +name: Test workflow with LLVM 18 + +on: + push: + branches: + - public-approved + workflow_dispatch: + +env: + MCDC_HOME: /home/github-runner/mcdc-workdir + +jobs: + find_runner: + name: Find an available self-hosted runner + runs-on: self-hosted + # Enforce this same runner for all later jobs that depend on this one + # FIXME this requires each runner has a label the same as its name + outputs: + runner_name: ${{ runner.name }} + # Dummy, do nothing + steps: + - uses: actions/checkout@v4 + + install_deps: + name: 1. Install dependencies + needs: find_runner + # Enforce the same runner + runs-on: ${{ needs.find_runner.outputs.runner_name }} + outputs: + runner_name: ${{ runner.name }} + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: ./ci/1_install_deps.sh + + pull_source: + name: 2. Pull the source code and apply patches + needs: install_deps + # Enforce the same runner + runs-on: ${{ needs.install_deps.outputs.runner_name }} + outputs: + runner_name: ${{ runner.name }} + steps: + - uses: actions/checkout@v4 + - name: Create a workspace from clean slate + run: | + rm -rf $MCDC_HOME + mkdir -p $MCDC_HOME + - name: Pull the source code and apply patches + run: ./ci/2_pull_source.sh + + get_llvm: + name: 3. Get LLVM + needs: pull_source + # Enforce the same runner + runs-on: ${{ needs.pull_source.outputs.runner_name }} + outputs: + runner_name: ${{ runner.name }} + steps: + - uses: actions/checkout@v4 + - name: Build from source + run: ./ci/3_get_llvm.sh + - name: Print LLVM build resource usage + run: | + cat /tmp/time.log + du -sh $MCDC_HOME/llvm-project + + build_kernel: + name: 4. Build the kernel + needs: get_llvm + # Enforce the same runner + runs-on: ${{ needs.get_llvm.outputs.runner_name }} + outputs: + runner_name: ${{ runner.name }} + steps: + - uses: actions/checkout@v4 + - name: Set up LLVM path + run: echo "$MCDC_HOME/llvm-project/build/bin" >> $GITHUB_PATH + - name: Build the kernel + run: ./ci/4_build_kernel.sh + - name: Print full kernel build log + run: cat /tmp/make.log + - name: Print kernel build resource usage + run: | + cat /tmp/time.log + du -sh $MCDC_HOME/linux + + boot_kernel_and_collect_coverage: + name: 5. Boot the kernel and collect coverage + needs: build_kernel + # Enforce the same runner + runs-on: ${{ needs.build_kernel.outputs.runner_name }} + outputs: + runner_name: ${{ runner.name }} + steps: + - uses: actions/checkout@v4 + - name: Set up LLVM path + run: echo "$MCDC_HOME/llvm-project/build/bin" >> $GITHUB_PATH + - name: Boot the kernel and collect coverage + run: ./ci/5_boot_kernel_and_collect_coverage.sh + - name: Print the index of coverage report + run: cat $MCDC_HOME/analysis/text-coverage-reports/index.txt diff --git a/ci/1_install_deps.sh b/ci/1_install_deps.sh new file mode 100755 index 0000000..189ceb9 --- /dev/null +++ b/ci/1_install_deps.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# For building LLVM from source (optional) +sudo apt-get -yq install cmake ninja-build mold + +# For building the kernel +sudo apt-get -yq install git bc libncurses-dev wget busybox \ + libssl-dev libelf-dev dwarves flex bison build-essential + +# For booting the kernel +sudo apt-get -yq install qemu-system-x86 diff --git a/ci/2_pull_source.sh b/ci/2_pull_source.sh new file mode 100755 index 0000000..4ea068d --- /dev/null +++ b/ci/2_pull_source.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +cd $MCDC_HOME + +# This meta repository +git clone https://github.com/xlab-uiuc/linux-mcdc.git +# LLVM if we want to build it from source (optional) +git clone https://github.com/llvm/llvm-project.git --branch llvmorg-18.1.7 --depth 5 +# Linux kernel +git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git --branch v5.15.153 --depth 5 + +# Apply kernel patches +cd $MCDC_HOME/linux +git apply $MCDC_HOME/linux-mcdc/patches/v0.4/0001-clang_instr_profile-add-Clang-s-Source-based-Code-Co.patch +git apply $MCDC_HOME/linux-mcdc/patches/v0.4/0002-kbuild-clang_instr_profile-disable-instrumentation-i.patch +git apply $MCDC_HOME/linux-mcdc/patches/v0.4/0003-clang_instr_profile-add-Clang-s-MC-DC-support.patch diff --git a/ci/3_get_llvm.sh b/ci/3_get_llvm.sh new file mode 100755 index 0000000..c2c0863 --- /dev/null +++ b/ci/3_get_llvm.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +cd $MCDC_HOME/llvm-project + +# Patches that improve llvm-cov text output + +cat > /tmp/llvm_cov_divider.patch << EOF +diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp +index 49a35f2a9..00aea4039 100644 +--- a/llvm/tools/llvm-cov/CoverageReport.cpp ++++ b/llvm/tools/llvm-cov/CoverageReport.cpp +@@ -102,8 +102,25 @@ void adjustColumnWidths(ArrayRef Files, + + /// Prints a horizontal divider long enough to cover the given column + /// widths. +-void renderDivider(ArrayRef ColumnWidths, raw_ostream &OS) { +- size_t Length = std::accumulate(ColumnWidths.begin(), ColumnWidths.end(), 0); ++void renderDivider(raw_ostream &OS, const CoverageViewOptions &Options, bool isFileReport) { ++ size_t Length; ++ if (isFileReport) { ++ Length = std::accumulate(std::begin(FileReportColumns), std::end(FileReportColumns), 0); ++ if (!Options.ShowRegionSummary) ++ Length -= (FileReportColumns[1] + FileReportColumns[2] + FileReportColumns[3]); ++ if (!Options.ShowInstantiationSummary) ++ Length -= (FileReportColumns[7] + FileReportColumns[8] + FileReportColumns[9]); ++ if (!Options.ShowBranchSummary) ++ Length -= (FileReportColumns[13] + FileReportColumns[14] + FileReportColumns[15]); ++ if (!Options.ShowMCDCSummary) ++ Length -= (FileReportColumns[16] + FileReportColumns[17] + FileReportColumns[18]); ++ } else { ++ Length = std::accumulate(std::begin(FunctionReportColumns), std::end(FunctionReportColumns), 0); ++ if (!Options.ShowBranchSummary) ++ Length -= (FunctionReportColumns[7] + FunctionReportColumns[8] + FunctionReportColumns[9]); ++ if (!Options.ShowMCDCSummary) ++ Length -= (FunctionReportColumns[10] + FunctionReportColumns[11] + FunctionReportColumns[12]); ++ } + for (size_t I = 0; I < Length; ++I) + OS << '-'; + } +@@ -405,7 +422,7 @@ void CoverageReport::renderFunctionReports(ArrayRef Files, + << column("Miss", FunctionReportColumns[11], Column::RightAlignment) + << column("Cover", FunctionReportColumns[12], Column::RightAlignment); + OS << "\n"; +- renderDivider(FunctionReportColumns, OS); ++ renderDivider(OS, Options, false); + OS << "\n"; + FunctionCoverageSummary Totals("TOTAL"); + for (const auto &F : Functions) { +@@ -418,7 +435,7 @@ void CoverageReport::renderFunctionReports(ArrayRef Files, + render(Function, DC, OS); + } + if (Totals.ExecutionCount) { +- renderDivider(FunctionReportColumns, OS); ++ renderDivider(OS, Options, false); + OS << "\n"; + render(Totals, DC, OS); + } +@@ -544,7 +561,7 @@ void CoverageReport::renderFileReports( + Column::RightAlignment) + << column("Cover", FileReportColumns[18], Column::RightAlignment); + OS << "\n"; +- renderDivider(FileReportColumns, OS); ++ renderDivider(OS, Options, true); + OS << "\n"; + + std::vector EmptyFiles; +@@ -563,7 +580,7 @@ void CoverageReport::renderFileReports( + render(*FCS, OS); + } + +- renderDivider(FileReportColumns, OS); ++ renderDivider(OS, Options, true); + OS << "\n"; + render(Totals, OS); + } + +EOF +git apply /tmp/llvm_cov_divider.patch +cat > /tmp/llvm_cov_final_new_line.patch << EOF +diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +index cab60c2d9..2aa588e6d 100644 +--- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp ++++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +@@ -43,7 +43,8 @@ Error CoveragePrinterText::createIndexFile( + Report.renderFileReports(OSRef, SourceFiles, Filters); + + Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n" +- << Opts.getLLVMVersionString(); ++ << Opts.getLLVMVersionString() ++ << "\n"; + + return Error::success(); + } +@@ -84,7 +85,8 @@ struct CoveragePrinterTextDirectory::Reporter : public DirectoryCoverageReport { + + Options.colored_ostream(OSRef, raw_ostream::CYAN) + << "\n" +- << Options.getLLVMVersionString(); ++ << Options.getLLVMVersionString() ++ << "\n"; + + return Error::success(); + } +EOF +git apply /tmp/llvm_cov_final_new_line.patch +cat > /tmp/llvm_cov_trim_abs_path.patch << EOF +diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp +index 49a35f2a9..dc9f2f9c5 100644 +--- a/llvm/tools/llvm-cov/CoverageReport.cpp ++++ b/llvm/tools/llvm-cov/CoverageReport.cpp +@@ -216,7 +216,7 @@ void CoverageReport::render(const FileCoverageSummary &File, + if (IsDir) + FileName += sys::path::get_separator(); + +- OS << column(FileName, FileReportColumns[0], Column::NoTrim); ++ OS << column(FileName, FileReportColumns[0], Column::RightTrim); + + if (Options.ShowRegionSummary) { + OS << format("%*u", FileReportColumns[1], +EOF +git apply /tmp/llvm_cov_trim_abs_path.patch + +/usr/bin/time -v -o /tmp/time.log $MCDC_HOME/linux-mcdc/scripts/build-llvm.sh diff --git a/ci/4_build_kernel.sh b/ci/4_build_kernel.sh new file mode 100755 index 0000000..378335b --- /dev/null +++ b/ci/4_build_kernel.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +cd $MCDC_HOME/linux +make LLVM=1 defconfig + +./scripts/config -e CONFIG_9P_FS_POSIX_ACL +./scripts/config -e CONFIG_9P_FS +./scripts/config -e CONFIG_NET_9P_VIRTIO +./scripts/config -e CONFIG_NET_9P +./scripts/config -e CONFIG_PCI +./scripts/config -e CONFIG_VIRTIO_PCI +./scripts/config -e CONFIG_OVERLAY_FS +./scripts/config -e CONFIG_DEBUG_FS +./scripts/config -e CONFIG_CONFIGFS_FS +./scripts/config -e CONFIG_MAGIC_SYSRQ +make LLVM=1 olddefconfig + +./scripts/config -e CONFIG_KUNIT +./scripts/config -e CONFIG_KUNIT_ALL_TESTS +make LLVM=1 olddefconfig + +./scripts/config -e CONFIG_INSTR_PROFILE_CLANG +./scripts/config -e CONFIG_SCC_CLANG +./scripts/config -e CONFIG_MCDC_CLANG +make LLVM=1 olddefconfig + +./scripts/config -d CONFIG_DRM_I915 +make LLVM=1 olddefconfig + +cat << EOF +Building the kernel with output suppressed. The log tail will be displayed once +the process finishes. See the full log in the next step. +EOF +/usr/bin/time -v -o /tmp/time.log make LLVM=1 -j$(nproc) >& /tmp/make.log +tail -n 200 /tmp/make.log diff --git a/ci/5_boot_kernel_and_collect_coverage.sh b/ci/5_boot_kernel_and_collect_coverage.sh new file mode 100755 index 0000000..f722e8d --- /dev/null +++ b/ci/5_boot_kernel_and_collect_coverage.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +GUEST_COMMANDS="true" +GUEST_COMMANDS="$GUEST_COMMANDS; uname -a" +GUEST_COMMANDS="$GUEST_COMMANDS; ls /sys/kernel/debug/clang_instr_profile" +GUEST_COMMANDS="$GUEST_COMMANDS; cp /sys/kernel/debug/clang_instr_profile/profraw ." + +cd $MCDC_HOME/linux +$MCDC_HOME/linux-mcdc/scripts/q -c "$GUEST_COMMANDS" + +file profraw |& tee /tmp/file.log +if ! grep "LLVM raw profile data, version 9" /tmp/file.log > /dev/null; then + printf "\nUnexpected profraw\n" + exit 1 +fi + +mkdir -p $MCDC_HOME/analysis +mv profraw $MCDC_HOME/analysis +cd $MCDC_HOME/analysis + +llvm-profdata merge profraw -o profdata +llvm-cov show --show-mcdc \ + --show-mcdc-summary \ + --show-region-summary=false \ + --show-branch-summary=false \ + --format=text \ + -use-color \ + -show-directory-coverage \ + -output-dir=text-coverage-reports \ + -instr-profile profdata \ + $MCDC_HOME/linux/vmlinux