Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions lisa/tools/meson.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Licensed under the MIT license.

from pathlib import PurePath
from typing import cast
from typing import List, Optional, cast

from lisa.executable import Tool
from lisa.operating_system import Posix
Expand Down Expand Up @@ -97,9 +97,18 @@ def _install(self) -> bool:

return self._check_exists()

def setup(self, args: str, cwd: PurePath, build_dir: str = "build") -> PurePath:
def setup(
self,
args: str,
cwd: PurePath,
build_dir: str = "build",
variables: Optional[List[str]] = None,
) -> PurePath:
variable_defs = ""
if variables:
variable_defs = " ".join([f"-D{x}" for x in variables])
self.run(
f"{args} {build_dir}",
f"{args} {build_dir} {variable_defs}",
force_run=True,
shell=True,
cwd=cwd,
Expand Down
13 changes: 12 additions & 1 deletion lisa/tools/timeout.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Dict, Optional

from lisa.executable import ExecutableResult, Process, Tool
from lisa.util.constants import SIGTERM

Expand All @@ -19,6 +21,7 @@ def run_with_timeout(
timeout: int,
signal: int = SIGTERM,
kill_timeout: int = 0,
update_envs: Optional[Dict[str, str]] = None,
) -> ExecutableResult:
# timeout [OPTION] DURATION COMMAND [ARG]...

Expand All @@ -37,6 +40,7 @@ def run_with_timeout(
timeout=timeout,
signal=signal,
kill_timeout=kill_timeout,
update_envs=update_envs,
).wait_result(timeout=command_timeout)

def start_with_timeout(
Expand All @@ -46,9 +50,16 @@ def start_with_timeout(
signal: int = SIGTERM,
kill_timeout: int = 0,
delay_start: int = 0,
update_envs: Optional[Dict[str, str]] = None,
) -> Process:
# timeout [OPTION] DURATION COMMAND [ARG]...
params = f"-s {signal} --preserve-status {timeout} {command}"
if kill_timeout:
params = f"--kill-after {kill_timeout} " + params
return self.run_async(parameters=params, force_run=True, shell=True, sudo=True)
return self.run_async(
parameters=params,
force_run=True,
shell=True,
sudo=True,
update_envs=update_envs,
)
12 changes: 11 additions & 1 deletion microsoft/testsuites/dpdk/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ def _should_install(self, required_version: Optional[VersionInfo] = None) -> boo
)

# run the defined setup and installation steps.
def do_installation(self, required_version: Optional[VersionInfo] = None) -> None:
def do_installation(
self, required_version: Optional[VersionInfo] = None, **kwargs: Any
) -> None:
self._setup_node()
if self._should_install():
self._uninstall()
Expand All @@ -238,6 +240,7 @@ def __init__(
node: Node,
os_dependencies: Optional[DependencyInstaller] = None,
downloader: Optional[Downloader] = None,
**kwargs: Any,
) -> None:
self._node = node
if not isinstance(self._node.os, Posix):
Expand All @@ -248,6 +251,9 @@ def __init__(
self._package_manager_extra_args: List[str] = []
self._os_dependencies = os_dependencies
self._downloader = downloader
self._kwargs = kwargs
# default to no asan, it's applicable for source builds only
self.use_asan = bool(kwargs.pop("use_asan", False))


# Base class for package manager installation
Expand Down Expand Up @@ -379,6 +385,10 @@ def is_url_for_tarball(url: str) -> bool:
return ".tar" in suffixes


def find_libasan_so(node: Node) -> str:
return node.execute("find /usr/lib/ -name libasan.so", sudo=True, shell=True).stdout


def is_url_for_git_repo(url: str) -> bool:
parsed_url = parse_url(url)
scheme = parsed_url.scheme
Expand Down
42 changes: 35 additions & 7 deletions microsoft/testsuites/dpdk/dpdktestpmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import re
from pathlib import PurePath, PurePosixPath
from typing import Any, List, Tuple, Type
from typing import Any, Dict, List, Optional, Tuple, Type

from assertpy import assert_that, fail
from semver import VersionInfo
Expand Down Expand Up @@ -44,6 +44,7 @@
OsPackageDependencies,
PackageManagerInstall,
TarDownloader,
find_libasan_so,
get_debian_backport_repo_args,
is_url_for_git_repo,
is_url_for_tarball,
Expand Down Expand Up @@ -289,8 +290,16 @@ def _install(self) -> None:
node = self._node
# save the pythonpath for later
python_path = node.tools[Python].get_python_path()
# pick meson options, add ASAN build arg if present.
meson_options = ["buildtype=debug"]
if self.use_asan:
meson_options += ["b_sanitize=address"]
# invoke meson
self.dpdk_build_path = node.tools[Meson].setup(
args=sample_apps, build_dir="build", cwd=self.asset_path
args=sample_apps,
build_dir="build",
cwd=self.asset_path,
variables=meson_options,
)
install_result = node.tools[Ninja].run(
cwd=self.dpdk_build_path,
Expand Down Expand Up @@ -606,9 +615,16 @@ def generate_testpmd_command(
def run_for_n_seconds(self, cmd: str, timeout: int) -> str:
self._last_run_timeout = timeout
self.node.log.info(f"{self.node.name} running: {cmd}")

envs: Optional[Dict[str, str]] = (
{
"ASAN_OPTIONS": "detect_leaks=false",
"LD_PRELOAD": find_libasan_so(self.node),
}
if self.installer.use_asan
else None
)
proc_result = self.node.tools[Timeout].run_with_timeout(
cmd, timeout, SIGINT, kill_timeout=timeout + 10
cmd, timeout, SIGINT, kill_timeout=timeout + 10, update_envs=envs
)
self._last_run_output = proc_result.stdout
self.populate_performance_data()
Expand All @@ -617,7 +633,6 @@ def run_for_n_seconds(self, cmd: str, timeout: int) -> str:
def start_for_n_seconds(self, cmd: str, timeout: int) -> str:
self._last_run_timeout = timeout
self.node.log.info(f"{self.node.name} running: {cmd}")

proc_result = self.node.tools[Timeout].run_with_timeout(
cmd, timeout, SIGINT, kill_timeout=timeout + 10
)
Expand Down Expand Up @@ -735,7 +750,17 @@ def get_example_app_path(self, app_name: str) -> PurePath:
# check if there is a build directory and build the application
# (if necessary)
if not shell.exists(source_path.joinpath("build")):
self.node.tools[Make].make("static", cwd=source_path, sudo=True)
libasan_so = find_libasan_so(self.node)
assert libasan_so != "", "couldn't find libasan"
envs: Optional[Dict[str, str]] = (
{
"CFLAGS": "-fsanitize=address",
}
if self.installer.use_asan
else None
)
self.node.tools[Make].make("", cwd=source_path, sudo=True, update_envs=envs)

return source_path.joinpath(f"build/{source_path.name}")

def __init__(self, *args: Any, **kwargs: Any) -> None:
Expand All @@ -749,7 +774,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
self._determine_network_hardware()
if self.use_package_manager_install():
self.installer: Installer = DpdkPackageManagerInstall(
self.node, DPDK_PACKAGE_MANAGER_PACKAGES
self.node,
DPDK_PACKAGE_MANAGER_PACKAGES,
)
# if not package manager, choose source installation
else:
Expand Down Expand Up @@ -777,10 +803,12 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
" Expected https://___/___.git or /path/to/tar.tar[.gz] or "
"https://__/__.tar[.gz]"
)
self._use_asan = bool(kwargs.pop("use_asan", False))
self.installer = DpdkSourceInstall(
node=self.node,
os_dependencies=DPDK_SOURCE_INSTALL_PACKAGES,
downloader=downloader,
use_asan=self._use_asan,
)
# if dpdk is already installed, find the binary and check the version
if self.find_testpmd_binary(assert_on_fail=False):
Expand Down
30 changes: 29 additions & 1 deletion microsoft/testsuites/dpdk/dpdkutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
PackageManagerInstall,
TarDownloader,
check_dpdk_support,
find_libasan_so,
is_url_for_git_repo,
is_url_for_tarball,
update_kernel_from_repo,
Expand Down Expand Up @@ -267,6 +268,7 @@ def enable_uio_hv_generic(node: Node) -> None:
uname = node.tools[Uname]

# check if kernel config for Hyper-V VMBus is enabled

config = "CONFIG_UIO_HV_GENERIC"
if not kconfig.is_enabled(config):
kversion = uname.get_linux_information().kernel_version
Expand Down Expand Up @@ -330,6 +332,7 @@ def initialize_node_resources(
dpdk_branch = variables.get("dpdk_branch", "")
rdma_source = variables.get("rdma_source", "")
rdma_branch = variables.get("rdma_branch", "")
dpdk_use_asan = variables.get("dpdk_use_asan", False)
force_net_failsafe_pmd = variables.get("dpdk_force_net_failsafe_pmd", False)
log.info(
"Dpdk initialize_node_resources running"
Expand Down Expand Up @@ -372,6 +375,7 @@ def initialize_node_resources(
dpdk_branch=dpdk_branch,
sample_apps=sample_apps,
force_net_failsafe_pmd=force_net_failsafe_pmd,
use_asan=dpdk_use_asan,
)
# Tools will skip installation if the binary is present, so
# force invoke install. Installer will skip if the correct
Expand Down Expand Up @@ -492,6 +496,7 @@ def _run_command_with_testkit(
run_kit: Tuple[DpdkTestResources, str],
) -> Tuple[DpdkTestResources, str]:
testkit, cmd = run_kit

return (testkit, testkit.testpmd.run_for_n_seconds(cmd, seconds))

task_manager = run_in_parallel_async(
Expand Down Expand Up @@ -1344,13 +1349,23 @@ class DpdkDevnameInfo:
def __init__(self, testpmd: DpdkTestpmd) -> None:
self._node = testpmd.node
self._testpmd = testpmd
self.env_args: Optional[Dict[str, str]] = None

def get_port_info(self, nics: List[NicInfo], expect_ports: int = 1) -> str:
# since we only need this for netvsc, we'll only generate
# the device inclusion info for netvsc cases.
# This is needed because the port_ids will change
# depending on how many NICs are present _and_ enabled
# by the EAL.
self.env_args = (
{
"ASAN_OPTIONS": "detect_leaks=false",
"LD_PRELOAD": f"{find_libasan_so(self._node)}",
}
if self._testpmd.installer.use_asan
else None
)

if self._node.nics.is_mana_device_present():
# mana needs a vdev argument of pci info
# followed by kv pairs for mac addresses.
Expand All @@ -1370,9 +1385,10 @@ def get_port_info(self, nics: List[NicInfo], expect_ports: int = 1) -> str:
# run the application with the device include arguments.

output = self._node.execute(
f"{str(self._testpmd.get_example_app_path('devname'))} {nic_args}",
(f"{str(self._testpmd.get_example_app_path('devname'))} {nic_args}"),
sudo=True,
shell=True,
update_envs=self.env_args,
).stdout

# find all the matches for devices bound to net_netvsc PMD
Expand Down Expand Up @@ -1520,6 +1536,16 @@ def run_dpdk_symmetric_mp(
"--log-level vmbus,debug "
f"-- -p {port_mask} --num-procs 2"
)
libasan_so = find_libasan_so(test_kit.node)
assert libasan_so != "", "Test bug: libasan.so is missing after source build."
symmetric_mp_envs = (
{
"LD_PRELOAD": libasan_so,
"ASAN_OPTIONS": "detect_leaks=false ",
}
if test_kit.testpmd.installer.use_asan
else None
)
# start the first process (id 0) on core 1
primary = node.tools[Timeout].start_with_timeout(
command=(
Expand All @@ -1529,6 +1555,7 @@ def run_dpdk_symmetric_mp(
timeout=660,
signal=SIGINT,
kill_timeout=30,
update_envs=symmetric_mp_envs,
)

# wait for it to start
Expand All @@ -1543,6 +1570,7 @@ def run_dpdk_symmetric_mp(
timeout=600,
signal=SIGINT,
kill_timeout=35,
update_envs=symmetric_mp_envs,
)
secondary.wait_output("APP: Finished Process Init", timeout=20)

Expand Down
Loading