1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
import logging
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
from typing import Optional, TextIO
from util import nproc, out_of_date
from vmtest.config import Kernel, local_kernel
from vmtest.download import downloaded_compiler
logger = logging.getLogger(__name__)
def build_kmod(
download_dir: Path, kernel: Kernel, outfile: Optional[TextIO] = None
) -> Path:
kmod = kernel.path.parent / f"drgn_test-{kernel.release}.ko"
# External modules can't do out-of-tree builds for some reason, so copy the
# source files to a temporary directory and build the module there, then
# move it to the final location.
kmod_source_dir = Path("tests/linux_kernel/kmod")
source_files = ("drgn_test.c", "Makefile")
if out_of_date(kmod, *[kmod_source_dir / filename for filename in source_files]):
logger.info("building %s", kmod)
compiler = downloaded_compiler(download_dir, kernel.arch)
kernel_build_dir = kernel.path / "build"
with tempfile.TemporaryDirectory(dir=kmod.parent) as tmp_name:
tmp_dir = Path(tmp_name)
# Make sure that header files have the same paths as in the
# original kernel build.
debug_prefix_map = [
f"{kernel_build_dir.resolve()}=.",
f"{tmp_dir.resolve()}=./drgn_test",
]
cflags = " ".join(["-fdebug-prefix-map=" + map for map in debug_prefix_map])
for filename in source_files:
shutil.copy(kmod_source_dir / filename, tmp_dir / filename)
subprocess.check_call(
[
"make",
"ARCH=" + kernel.arch.kernel_arch,
"-C",
kernel_build_dir,
f"M={tmp_dir.resolve()}",
"KAFLAGS=" + cflags,
"KCFLAGS=" + cflags,
"-j",
str(nproc()),
],
env={**os.environ, **compiler.env()},
stdout=outfile,
stderr=outfile,
)
(tmp_dir / "drgn_test.ko").rename(kmod)
else:
logger.info("%s is up to date", kmod)
return kmod
def _main() -> None:
import argparse
from vmtest.download import (
DOWNLOAD_KERNEL_ARGPARSE_METAVAR,
DownloadCompiler,
download_in_thread,
download_kernel_argparse_type,
)
logging.basicConfig(
format="%(asctime)s:%(levelname)s:%(name)s:%(message)s", level=logging.INFO
)
parser = argparse.ArgumentParser(
description="build drgn test kernel module",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-k",
"--kernel",
metavar=DOWNLOAD_KERNEL_ARGPARSE_METAVAR,
dest="kernels",
action="append",
type=download_kernel_argparse_type,
default=argparse.SUPPRESS,
help="kernel to build for; may be given multiple times",
)
parser.add_argument(
"-d",
"--directory",
metavar="DIR",
type=Path,
default="build/vmtest",
help="directory to download assets to",
)
args = parser.parse_args()
if not hasattr(args, "kernels"):
args.kernels = []
compilers_needed = {
kernel.arch.name: DownloadCompiler(kernel.arch) for kernel in args.kernels
}
to_download = list(compilers_needed.values())
for kernel in args.kernels:
if not kernel.pattern.startswith(".") and not kernel.pattern.startswith("/"):
to_download.append(kernel)
with download_in_thread(args.directory, to_download) as downloads:
download_it = iter(downloads)
for i in range(len(compilers_needed)):
next(download_it)
for kernel in args.kernels:
if kernel.pattern.startswith(".") or kernel.pattern.startswith("/"):
downloaded = local_kernel(kernel.arch, Path(kernel.pattern))
else:
downloaded = next(download_it) # type: ignore[assignment]
print(build_kmod(args.directory, downloaded))
if __name__ == "__main__":
_main()
|