From d7da2df830432e10232462e41dffcf40b672dab8 Mon Sep 17 00:00:00 2001 From: baconyao Date: Tue, 16 Jul 2024 13:04:17 +0800 Subject: [PATCH 01/18] Initialize the scenraio of gst_v4l2_video_decoder_performance --- .../bin/gst_v4l2_video_decoder_performance.py | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100755 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py new file mode 100755 index 0000000000..678ca4b729 --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# This file is part of Checkbox. +# +# Copyright 2024 Canonical Ltd. +# Written by: +# Patrick Chang +# +# Checkbox is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# Checkbox is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Checkbox. If not, see . + +import argparse +import logging +import os +import re +import shlex +import subprocess + +logging.basicConfig(level=logging.INFO) + + +def register_arguments(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=( + "Script helps verify the performance of specific decoder won't" + " violate some Pass Criteria." + ), + ) + + parser.add_argument( + "-gp", + "--golden_sample_path", + required=True, + type=str, + help="Path of Golden Sample file", + ) + + parser.add_argument( + "-dp", + "--decoder_plugin", + required=True, + type=str, + help="Decoder plugin be used in gstreamer pipeline e.g. v4l2h264dec", + ) + + parser.add_argument( + "-s", + "--sink", + default="fakesink", + type=str, + help=("Specific sink that helps on judgement (default: fakesink)"), + ) + + parser.add_argument( + "-mf", + "--minimum_fps", + required=True, + type=str, + help=( + "The minimum value of FPS that " + "all average FPS value should not violate" + ), + ) + + args = parser.parse_args() + return args + + +def build_gst_command( + gst_bin: str, golden_sample_path: str, decoder: str, sink: str +) -> str: + """ + Builds a GStreamer command to process the golden sample. + + :param gst_bin: + The binary name of gstreamer. Default is "gst-launch-1.0" + You can assign the snap name to GST_LAUNCH_BIN env variable if you + want to using snap. + :param golden_sample: + The path to the golden sample file. + :param decoder: + The decoder to use for the video, e.g., "v4l2vp8dec", "v4l2vp9dec". + :param sink: + The desired sink option, e.g., "fakesink". + + :returns: + The GStreamer command to execute. + """ + cmd = ( + "{} -v filesrc location={} ! parsebin ! queue ! {} ! queue ! " + "v4l2convert output-io-mode=dmabuf-import capture-io-mode=dmabuf ! " + 'queue ! fpsdisplaysink video-sink="{}"' + " text-overlay=false sync=false" + ).format(gst_bin, golden_sample_path, decoder, sink) + + return cmd + + +def execute_command(cmd: str) -> str: + """ + Executes the GStreamer command and extracts the specific data from the + output. The specific data is the value of last-message which is exposed by + fpsdisplaysink. + + :param cmd: + The GStreamer command to execute. + + :returns: + The extracted last_message. + """ + try: + logging.info("Starting command: '{}'".format(cmd)) + ret = subprocess.run( + shlex.split(cmd), + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + timeout=30, + ) + logging.info(ret.stdout) + return ret.stdout + except Exception as e: + logging.error(e.stderr) + raise SystemExit(e.returncode) + + +def is_valid_result(input_text: str, min_fps: float) -> bool: + """ + Extracts the last-message value from the given input string. + Example + last-message = rendered: 98, dropped: 0, current: 95.53, + average: 98.43 + Pass criteria + 1. The value of dropped frame must be 0 + 2. The value of average fps must greater than or equal to min_fps + + :param input_text: + The input string containing the data of last-message. + + :param min_fps: + A value that all average FPS must not fall below + + :returns: + True if the result meets the pass criteria; false otherwise . + """ + # Find all matches in the input text + pattern = re.compile(r"dropped: (\d+), current: [\d.]+, average: ([\d.]+)") + matches = pattern.findall(input_text) + if not matches: + logging.error("Unable to find any matching data.") + return False + for dropped, average in matches: + # Leave once a value doesn't match the pass criteria + if int(dropped) != 0 or float(average) < float(min_fps): + logging.error("Found values that violate the pass criteria.") + return False + return True + + +def main() -> None: + """ + This function performs the following steps: + + 1. Checks if the golden sample file exist. + 2. Builds a GStreamer command to process the golden sample using the + specified decoder. + 3. Executes the command and get the outcome back + 4. Judge the outcome to see if it meets the Pass Criteria + + :param args: + An object containing the following attributes: + - `golden_sample_path` (str): The path to the golden sample file. + - `decoder_plugin` (str): The video decoder to use, e.g., + "v4l2vp8dec", "v4l2vp9dec". + - `minimum_average_fps` (str): The minimum value of FPS + that all average FPS value should not violate + + :raises SystemExit: + If the golden sample file does not exist, or if the outcome violates + the pass criteria. + """ + args = register_arguments() + logging.info( + "Pass Criteria" + "\n 1. All dropped frames must be 0" + "\n 2. All average fps values must greater than or equal to {}".format( + args.minimum_fps + ) + ) + # Check the golden sample exixt + if not os.path.exists(args.golden_sample_path): + raise SystemExit( + "Golden Sample '{}' doesn't exist".format(args.golden_sample_path) + ) + gst_launch_bin = os.getenv("GST_LAUNCH_BIN", "gst-launch-1.0") + cmd = build_gst_command( + gst_bin=gst_launch_bin, + golden_sample_path=args.golden_sample_path, + decoder=args.decoder_plugin, + sink=args.sink, + ) + + output = execute_command(cmd).rstrip(os.linesep) + if not is_valid_result(output, args.minimum_fps): + raise SystemExit(1) + logging.info("Pass") + + +if __name__ == "__main__": + main() From 6b50117f8677f4875abc95e85908509adc5a084c Mon Sep 17 00:00:00 2001 From: baconyao Date: Tue, 16 Jul 2024 17:38:07 +0800 Subject: [PATCH 02/18] Add performance script to decoder_performance script --- .../bin/genio_performance_mode.py | 412 ++++++++++++++++++ .../bin/gst_v4l2_video_decoder_performance.py | 14 +- .../bin/performance_mode_controller.py | 38 ++ 3 files changed, 463 insertions(+), 1 deletion(-) create mode 100755 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py create mode 100755 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py new file mode 100755 index 0000000000..091ab9823f --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python3 +# This script is used to all genio boards and should be run as a super user + +# Copyright 2024 Canonical Ltd. +# Written by: +# Patrick Chang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import contextlib +import logging +import os +import re + +logging.basicConfig(level=logging.INFO) + +SYS_DEVICES_CPU = "/sys/devices/system/cpu" +PATH_CPU_GOVERNOR = SYS_DEVICES_CPU + "/cpufreq/policy{}/scaling_governor" +PATH_CPUIDLE_STATE_DISABLE = SYS_DEVICES_CPU + "/cpu{}/cpuidle/state{}/disable" +PATH_GPU_GOVERNOR = "/sys/devices/platform/soc/{}/devfreq/{}/governor" +SYS_CLASS_THERMAL_ZONE = "/sys/class/thermal/thermal_zone{}" +PATH_OF_THERMAL_MODE = SYS_CLASS_THERMAL_ZONE + "/mode" +PATH_OF_THERMAL_TRIP_POINT = SYS_CLASS_THERMAL_ZONE + "/trip_point_{}_temp" +BACKUP_CPU_GOVERNOR_STR_TEMPLEATE = "/tmp/p_{}_sg" +BACKUP_GPU_GOVERNOR_STR_TEMPLEATE = "/tmp/soc_{}_g" +BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE = "/tmp/thermal_{}_trip_{}_temp" +BACKUP_THERMAL_MODE_STR_TEMPLEATE = "/tmp/thermal_{}_mode" +BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE = "/tmp/c_{}_s_{}_disable" + + +class PerformanceController(): + ''' + PerformanceController provides some methods that handle the get, set and + backup actions. + ''' + def _get_node_value(self, node_path): + with open(node_path, 'r') as f: + value = f.read().strip() + return value + + def _set_node_value(self, node_path, value): + with open(node_path, 'w') as out: + out.write(str(value)) + + def _get_cpu_governor(self, policy): + node_path = PATH_CPU_GOVERNOR.format(policy) + governor = self._get_node_value(node_path=node_path) + return governor + + def _set_cpu_governor(self, governor_mode, policy): + node_path = PATH_CPU_GOVERNOR.format(policy) + self._set_node_value(node_path=node_path, value=governor_mode) + + def _get_gpu_governor(self, soc): + node_path = PATH_GPU_GOVERNOR.format(soc, soc) + governor = self._get_node_value(node_path=node_path) + return governor + + def _set_gpu_governor(self, governor_mode, soc): + node_path = PATH_GPU_GOVERNOR.format(soc, soc) + self._set_node_value(node_path=node_path, value=governor_mode) + + def _get_thermal_trip_point_temp(self, thermal_zone, trip_point_count): + node_path = PATH_OF_THERMAL_TRIP_POINT.format( + thermal_zone, trip_point_count) + temp_value = self._get_node_value(node_path=node_path) + return temp_value + + def _set_thermal_trip_point_temp( + self, thermal_zone, trip_point_count, temp_value + ): + node_path = PATH_OF_THERMAL_TRIP_POINT.format( + thermal_zone, trip_point_count) + self._set_node_value(node_path=node_path, value=temp_value) + + def _set_apusys(self, value): + node_path = "/sys/kernel/debug/apusys/power" + self._set_node_value(node_path=node_path, value=value) + + def _toggle_cpuidle_state_disable(self, cpu_count, state_count, value): + ''' + Toggle the 'disable' attribute for CPU idle states. + + Args: + cpu_count (int): The count of CPUs. + state_count (int): The count of CPU idle stat. + value (int): + The value to set for the 'disable' attribute (0 or 1). + + Raises: + ValueError: If the provided value is not 0 or 1. + ''' + if value not in [0, 1]: + raise ValueError("Value must be 0 or 1") + self._set_node_value( + PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count), value) + + def _toggle_thermal_mode(self, thermal_zone, value): + if value not in ["disabled", "enabled"]: + raise ValueError("Thermal mode must be enabled or disabled") + self._set_node_value( + PATH_OF_THERMAL_MODE.format(thermal_zone), value) + + +class PerformanceModeManager(PerformanceController): + ''' + PerformanceModeManager class for managing system performance modes. + + This class extends the PerformanceController class and provides methods + to set and restore performance modes for CPU, GPU, APU, and thermal + configurations. + + Args: + affected_policies (list): List of affected CPU policies. + affected_thermal_zone (int): Thermal zone to be affected. + affected_thermal_trip_points (list): + List of thermal trip points to be affected. + affected_trip_point_temp (int): + Temperature value for affected thermal trip points. + change_thermal_mode (bool): + Flag to indicate if thermal mode should be changed. + gpu_soc_name (str): Name of the GPU SoC to be affected. + enable_apu (bool): Flag to indicate if APU should be enabled. + + Methods: + set_performance_mode(): + Configures CPU, GPU, APU and thermal settings for performance mode. + + restore_default_mode(): + Restores default CPU, GPU and thermal settings. + + Note: + This class assumes the existence of methods such as + _backup_cpu_governor, _set_cpu_governor, _toggle_cpuidle_state_disable, + _backup_gpu_governor, _set_gpu_governor, _set_apusys, + _toggle_thermal_mode, _backup_thermal_trip_point_temp, + _set_thermal_trip_point_temp, _restore_cpu_governor, + _restore_gpu_governor, and _restore_thermal_trip_point_temp + in the parent class (PerformanceController). + ''' + def __init__( + self, + affected_policies: list, + affected_thermal_zone: int, + affected_thermal_trip_points: list, + affected_trip_point_temp: int, + change_thermal_mode: bool, + gpu_soc_name: str, + enable_apu: bool + ): + self._affected_policies = affected_policies + self._affected_thermal_zone = affected_thermal_zone + self._affected_thermal_trip_points = affected_thermal_trip_points + self._affected_trip_point_temp = affected_trip_point_temp + self._change_thermal_mode = change_thermal_mode + self._gpu_soc_name = gpu_soc_name + self._enable_apu = enable_apu + self._affected_cpuidle_count_state = self._extract_cpu_state_numbers() + + def set_performance_mode(self): + ''' + Set performance mode by configuring CPU, GPU, APU, and thermal + settings. + + This method iterates over affected CPU policies, sets CPU governors to + "performance", toggles CPU idle state, sets GPU governor to + "performance", sets APU configuration, and adjusts thermal settings + based on provided parameters. + ''' + # Configure CPU + for policy in self._affected_policies: + self._backup_cpu_governor(policy) + self._set_cpu_governor( + governor_mode="performance", policy=policy) + for item in self._affected_cpuidle_count_state: + self._backup_cpuidle_state_disable(item[0], item[1]) + self._toggle_cpuidle_state_disable(item[0], item[1], 1) + # Configure GPU + if self._gpu_soc_name: + self._backup_gpu_governor(soc=self._gpu_soc_name) + self._set_gpu_governor( + governor_mode="performance", soc=self._gpu_soc_name) + # Configure APU + if self._enable_apu: + self._set_apusys(value="dvfs_debug 0") + # Configure Thermal + if self._change_thermal_mode: + self._backup_thermal_mode() + self._toggle_thermal_mode(self._affected_thermal_zone, "disabled") + if self._affected_thermal_trip_points and \ + self._affected_trip_point_temp: + for trip_point in self._affected_thermal_trip_points: + self._backup_thermal_trip_point_temp( + thermal_zone=self._affected_thermal_zone, + trip_point_count=trip_point + ) + self._set_thermal_trip_point_temp( + thermal_zone=self._affected_thermal_zone, + trip_point_count=trip_point, + temp_value=self._affected_trip_point_temp + ) + + def restore_default_mode(self): + # Configure CPU + for policy in self._affected_policies: + self._restore_cpu_governor(policy) + for item in self._affected_cpuidle_count_state: + self._restore_cpuidle_state(item[0], item[1]) + # Configure GPU + if self._gpu_soc_name: + self._restore_gpu_governor(soc=self._gpu_soc_name) + # Configure Thermal + if self._change_thermal_mode: + self._restore_thermal_mode() + if self._affected_thermal_trip_points and \ + self._affected_trip_point_temp: + for trip_point in self._affected_thermal_trip_points: + self._restore_thermal_trip_point_temp( + thermal_zone=self._affected_thermal_zone, + trip_point_count=trip_point + ) + + def _backup_cpu_governor(self, policy): + governor = self._get_cpu_governor(policy) + self._set_node_value( + node_path=BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy), + value=governor + ) + + def _restore_cpu_governor(self, policy): + origianl_value = self._get_node_value( + BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy)) + self._set_cpu_governor(governor_mode=origianl_value, policy=policy) + + def _backup_cpuidle_state_disable(self, cpu_count, state_count): + value = self._get_node_value( + PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count)) + self._set_node_value( + BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( + cpu_count, state_count), + value + ) + + def _restore_cpuidle_state(self, cpu_count, state_count): + origianl_value = self._get_node_value( + BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( + cpu_count, state_count)) + self._toggle_cpuidle_state_disable( + cpu_count, state_count, int(origianl_value)) + + def _extract_cpu_state_numbers(self): + ''' + Extracts CPU state numbers from files in the specified directory. + + Returns: + - List of tuples: + Each tuple contains two integers representing CPU numbers (X) and + state numbers (Y). + ''' + pattern = re.compile(r'/cpu(\d+)/cpuidle/state(\d+)/disable') + results = [] + + # Walk through the directory and its subdirectories + for root, _, files in os.walk(SYS_DEVICES_CPU): + for file in files: + # Construct the full path for each file + full_path = os.path.join(root, file) + + # Use the regular expression pattern to directly extract + # numbers + match = pattern.search(full_path) + if match: + results.append((int(match.group(1)), int(match.group(2)))) + + return results + + def _backup_gpu_governor(self, soc): + governor = self._get_gpu_governor(soc) + self._set_node_value( + node_path=BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc), + value=governor + ) + + def _restore_gpu_governor(self, soc): + origianl_value = self._get_node_value( + BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc)) + self._set_gpu_governor(governor_mode=origianl_value, soc=soc) + + def _backup_thermal_mode(self): + value = self._get_node_value(node_path=PATH_OF_THERMAL_MODE.format( + self._affected_thermal_zone)) + self._set_node_value( + node_path=BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( + self._affected_thermal_zone), + value=value + ) + + def _restore_thermal_mode(self): + origianl_value = self._get_node_value( + BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( + self._affected_thermal_zone)) + self._toggle_thermal_mode(self._affected_thermal_zone, origianl_value) + + def _backup_thermal_trip_point_temp(self, thermal_zone, trip_point_count): + temp_value = self._get_thermal_trip_point_temp( + thermal_zone=thermal_zone, trip_point_count=trip_point_count) + self._set_node_value( + node_path=BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( + thermal_zone, trip_point_count), + value=temp_value + ) + + def _restore_thermal_trip_point_temp(self, thermal_zone, trip_point_count): + origianl_temp = self._get_node_value( + BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( + thermal_zone, trip_point_count)) + self._set_thermal_trip_point_temp( + thermal_zone=thermal_zone, + trip_point_count=trip_point_count, + temp_value=origianl_temp) + + +def get_testing_parameters(platform): + """ + Get testing parameters based on the specified platform. + + Args: + platform (str): The platform identifier. + + Returns: + dict: A dictionary containing testing parameters for the given + platform. + The keys and values may include: + - 'affected_policies' (list): List of affected CPU policies. + - 'affected_cpuidle_count_state' (list): + List containing CPU idle count and state. + - 'affected_thermal_zone' (int): Thermal zone to be affected. + - 'affected_thermal_trip_points' (list): + List of affected thermal trip points. + - 'affected_trip_point_temp' (int): + Temperature value for affected thermal trip points. + - 'change_thermal_mode' (bool): + Flag to indicate if thermal mode should be changed. + - 'gpu_soc_name' (str): Name of the GPU SoC to be affected. + - 'enable_apu' (bool): Flag to indicate if APU should be enabled. + """ + testing_params = {} + if platform == "genio-1200": + testing_params = { + "affected_policies": [0, 4], + "affected_thermal_zone": 0, + "affected_thermal_trip_points": [], + "affected_trip_point_temp": 0, + "change_thermal_mode": True, + "gpu_soc_name": "13000000.mali", + "enable_apu": True, + } + elif platform == "genio-700": + testing_params = { + "affected_policies": [0, 6], + "affected_thermal_zone": 0, + "affected_thermal_trip_points": [0, 1, 2], + "affected_trip_point_temp": 1115000, + "change_thermal_mode": True, + "gpu_soc_name": "13000000.mali", + "enable_apu": True, + } + elif platform == "genio-510": + testing_params = { + "affected_policies": [0, 4], + "affected_thermal_zone": 0, + "affected_thermal_trip_points": [0, 1, 2], + "affected_trip_point_temp": 1115000, + "change_thermal_mode": True, + "gpu_soc_name": "13000000.mali", + "enable_apu": True, + } + elif platform == "genio-350": + testing_params = { + "affected_policies": [0], + "affected_thermal_zone": 0, + "affected_thermal_trip_points": [], + "affected_trip_point_temp": 0, + "change_thermal_mode": True, + "gpu_soc_name": "13040000.mali", + "enable_apu": False, + } + + return testing_params + + +@contextlib.contextmanager +def performance_mode(target: str): + # Run Performance Mode + try: + logging.info("======== Enable Performance Mode ========") + testing_params = get_testing_parameters(target) + PerformanceModeManager(**testing_params).set_performance_mode() + yield + finally: + logging.info("======== Disable Performance Mode ========") + PerformanceModeManager(**testing_params).restore_default_mode() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 678ca4b729..064ffee339 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -24,6 +24,8 @@ import shlex import subprocess +from performance_mode_controller import performance_mode + logging.basicConfig(level=logging.INFO) @@ -71,6 +73,14 @@ def register_arguments(): ), ) + parser.add_argument( + "-pmt", + "--performance_mode_target", + default="", + type=str, + help="", + ) + args = parser.parse_args() return args @@ -210,7 +220,9 @@ def main() -> None: sink=args.sink, ) - output = execute_command(cmd).rstrip(os.linesep) + with performance_mode(args.performance_mode_target): + output = execute_command(cmd).rstrip(os.linesep) + if not is_valid_result(output, args.minimum_fps): raise SystemExit(1) logging.info("Pass") diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py new file mode 100755 index 0000000000..12a1e8204b --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# Since there are lots of devices from different projects need to run case +# with Performance mode, therefore, this script aims to be an unified entry +# and can be called by other scripts. As for the detail of each device, we +# implement it in each different specific script. + +# Copyright 2024 Canonical Ltd. +# Written by: +# Patrick Chang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import contextlib + + +@contextlib.contextmanager +def performance_mode(target: str = "", **kwrags): + try: + # Run performance mode if target string starts with "genio" + if target.startswith("genio"): + from genio_performance_mode import performance_mode + with performance_mode(target): + yield + # Do nothing if no specific target + else: + yield + finally: + pass From 20007410061e8cbb4270d311e98203e3528f282d Mon Sep 17 00:00:00 2001 From: baconyao Date: Wed, 17 Jul 2024 13:29:33 +0800 Subject: [PATCH 03/18] Rebase to main branch --- .../bin/gst_resources_generator.py | 29 ++++++++++++--- .../bin/gst_v4l2_video_decoder_performance.py | 7 +++- .../video-codec-test-confs/genio-1200.json | 35 +++++++++++++++++-- .../units/video-codec/jobs.pxu | 18 ++++++++++ .../units/video-codec/test-plan.pxu | 1 + 5 files changed, 82 insertions(+), 8 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py index 0c412ddae7..d4a46554b3 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py @@ -31,11 +31,7 @@ def register_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=( - "Script helps verify the MD5 checksum from specific Gstreamer" - " Decoder with different resolutions and color spaces is exactly" - " match golden reference" - ), + description=("This script generates the resource for all scenarios"), ) parser.add_argument( @@ -171,6 +167,29 @@ def gst_v4l2_audio_video_synchronization( } ) + def gst_v4l2_video_decoder_performance_fakesink( + self, scenario_data: List[Dict] + ) -> None: + for item in scenario_data: + self._resource_items.append( + { + "scenario": self._current_scenario_name, + "decoder_plugin": item["decoder_plugin"], + "minimum_fps": item["minimum_fps"], + "golden_sample_file": "{}/video_golden_samples/{}".format( + self._args.video_codec_testing_data_path, + item["golden_sample_file"], + ), + # performance_target is "" means won't enable performance + # mode. + "performance_target": ( + self._args.video_codec_conf_file + if item["enable_performance_mode"] + else "" + ), + } + ) + def main(self): for scenario in self._scenarios: self._current_scenario_name = scenario diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 064ffee339..853f08d5b8 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -78,7 +78,12 @@ def register_arguments(): "--performance_mode_target", default="", type=str, - help="", + help=( + "Once this value be assigned, script will enable performance mode" + " by the given value. For instance, if 'genio-1200' be assigned, " + "script will find the performance controller and execute the logic" + ' of genio-1200 board. (Default: "", will do nothing)' + ), ) args = parser.parse_args() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-1200.json b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-1200.json index 97d7a31b1a..278efcdb13 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-1200.json +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-1200.json @@ -54,6 +54,37 @@ ] } ] - } + }, + "gst_v4l2_video_decoder_performance_fakesink": [ + { + "decoder_plugin": "v4l2h264dec", + "golden_sample_file": "H264_8Bit_High@L6.1_3840X2160_90Fps_160Mbps_AACLC-10sec.mp4", + "minimum_fps": 90, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2h265dec", + "golden_sample_file": "HEVC_8Bit_High@L5.2_3840X2160_90Fps_160Mbps_AACLC-10sec.mp4", + "minimum_fps": 90, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2mpeg4dec", + "golden_sample_file": "MPEG-4_1920x1080_60fps_60Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp8dec", + "golden_sample_file": "VP8_1920x1080_60fps_40Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp9dec", + "golden_sample_file": "VP9_3840x2160_90fps_120Mbps.webm", + "minimum_fps": 90, + "enable_performance_mode": true + } + ] } - diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu index 060509ebe2..4ac68d74d7 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu @@ -54,3 +54,21 @@ _verification: 2. Is the timing of audio and video synchronized? command: gst_v4l2_audio_video_synchronization.py -dp {{decoder_plugin}} -gp {{golden_sample_file}} -vs {{video_sink}} -cp "{{capssetter_pipeline}}" + +template-filter: video_codec_resource.scenario == "gst_v4l2_video_decoder_performance_fakesink" +template-unit: job +template-id: ce-oem-video-codec/gst_v4l2_video_decoder_performance_fakesink +_template-summary: To check if the performance of decoder doesn't violate Pass Criteria +id: ce-oem-video-codec/{{ scenario }}-{{ decoder_plugin }} +_summary: Performance test of decoder {{ decoder_plugin }} - fakesink +_description: Test if while the sink is fakesink, the decoder's performance, {{ decoder_plugin }}, doesn't violate the Pass Criteria. (1) There are no frame losses (2) Average FPS not fall below the specification's definition +plugin: shell +user: root +category_id: video-codec +estimated_duration: 1s +imports: from com.canonical.plainbox import manifest +requires: manifest.has_video_codec == 'True' +flags: also-after-suspend +environ: GST_LAUNCH_BIN +command: + gst_v4l2_video_decoder_performance.py -gp {{golden_sample_file}} -dp {{decoder_plugin}} -mf {{minimum_fps}} -pmt {{performance_target}} diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu index 7b346f7824..3ae96c5604 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu @@ -24,3 +24,4 @@ bootstrap_include: video_codec_resource include: ce-oem-video-codec/gst_v4l2_video_decoder_md5_checksum_comparison + ce-oem-video-codec/gst_v4l2_video_decoder_performance_fakesink From fd271cb2540cb861cacedc1831758ee0660e986f Mon Sep 17 00:00:00 2001 From: baconyao Date: Fri, 26 Jul 2024 09:32:16 +0800 Subject: [PATCH 04/18] Fix black issue --- .../checkbox-provider-ce-oem/bin/performance_mode_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py index 12a1e8204b..a5541a12c3 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py @@ -29,6 +29,7 @@ def performance_mode(target: str = "", **kwrags): # Run performance mode if target string starts with "genio" if target.startswith("genio"): from genio_performance_mode import performance_mode + with performance_mode(target): yield # Do nothing if no specific target From 7b6c3888e123e5e124caa5e9dffffc9c1b44d1b9 Mon Sep 17 00:00:00 2001 From: baconyao Date: Fri, 26 Jul 2024 09:46:13 +0800 Subject: [PATCH 05/18] Fix black issue --- .../bin/genio_performance_mode.py | 122 +++++++++++------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py index 091ab9823f..4e6d22a1bb 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py @@ -38,18 +38,19 @@ BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE = "/tmp/c_{}_s_{}_disable" -class PerformanceController(): - ''' +class PerformanceController: + """ PerformanceController provides some methods that handle the get, set and backup actions. - ''' + """ + def _get_node_value(self, node_path): - with open(node_path, 'r') as f: + with open(node_path, "r") as f: value = f.read().strip() return value def _set_node_value(self, node_path, value): - with open(node_path, 'w') as out: + with open(node_path, "w") as out: out.write(str(value)) def _get_cpu_governor(self, policy): @@ -72,7 +73,8 @@ def _set_gpu_governor(self, governor_mode, soc): def _get_thermal_trip_point_temp(self, thermal_zone, trip_point_count): node_path = PATH_OF_THERMAL_TRIP_POINT.format( - thermal_zone, trip_point_count) + thermal_zone, trip_point_count + ) temp_value = self._get_node_value(node_path=node_path) return temp_value @@ -80,7 +82,8 @@ def _set_thermal_trip_point_temp( self, thermal_zone, trip_point_count, temp_value ): node_path = PATH_OF_THERMAL_TRIP_POINT.format( - thermal_zone, trip_point_count) + thermal_zone, trip_point_count + ) self._set_node_value(node_path=node_path, value=temp_value) def _set_apusys(self, value): @@ -88,7 +91,7 @@ def _set_apusys(self, value): self._set_node_value(node_path=node_path, value=value) def _toggle_cpuidle_state_disable(self, cpu_count, state_count, value): - ''' + """ Toggle the 'disable' attribute for CPU idle states. Args: @@ -99,21 +102,21 @@ def _toggle_cpuidle_state_disable(self, cpu_count, state_count, value): Raises: ValueError: If the provided value is not 0 or 1. - ''' + """ if value not in [0, 1]: raise ValueError("Value must be 0 or 1") self._set_node_value( - PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count), value) + PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count), value + ) def _toggle_thermal_mode(self, thermal_zone, value): if value not in ["disabled", "enabled"]: raise ValueError("Thermal mode must be enabled or disabled") - self._set_node_value( - PATH_OF_THERMAL_MODE.format(thermal_zone), value) + self._set_node_value(PATH_OF_THERMAL_MODE.format(thermal_zone), value) class PerformanceModeManager(PerformanceController): - ''' + """ PerformanceModeManager class for managing system performance modes. This class extends the PerformanceController class and provides methods @@ -147,7 +150,8 @@ class PerformanceModeManager(PerformanceController): _set_thermal_trip_point_temp, _restore_cpu_governor, _restore_gpu_governor, and _restore_thermal_trip_point_temp in the parent class (PerformanceController). - ''' + """ + def __init__( self, affected_policies: list, @@ -156,7 +160,7 @@ def __init__( affected_trip_point_temp: int, change_thermal_mode: bool, gpu_soc_name: str, - enable_apu: bool + enable_apu: bool, ): self._affected_policies = affected_policies self._affected_thermal_zone = affected_thermal_zone @@ -168,7 +172,7 @@ def __init__( self._affected_cpuidle_count_state = self._extract_cpu_state_numbers() def set_performance_mode(self): - ''' + """ Set performance mode by configuring CPU, GPU, APU, and thermal settings. @@ -176,12 +180,11 @@ def set_performance_mode(self): "performance", toggles CPU idle state, sets GPU governor to "performance", sets APU configuration, and adjusts thermal settings based on provided parameters. - ''' + """ # Configure CPU for policy in self._affected_policies: self._backup_cpu_governor(policy) - self._set_cpu_governor( - governor_mode="performance", policy=policy) + self._set_cpu_governor(governor_mode="performance", policy=policy) for item in self._affected_cpuidle_count_state: self._backup_cpuidle_state_disable(item[0], item[1]) self._toggle_cpuidle_state_disable(item[0], item[1], 1) @@ -189,7 +192,8 @@ def set_performance_mode(self): if self._gpu_soc_name: self._backup_gpu_governor(soc=self._gpu_soc_name) self._set_gpu_governor( - governor_mode="performance", soc=self._gpu_soc_name) + governor_mode="performance", soc=self._gpu_soc_name + ) # Configure APU if self._enable_apu: self._set_apusys(value="dvfs_debug 0") @@ -197,17 +201,19 @@ def set_performance_mode(self): if self._change_thermal_mode: self._backup_thermal_mode() self._toggle_thermal_mode(self._affected_thermal_zone, "disabled") - if self._affected_thermal_trip_points and \ - self._affected_trip_point_temp: + if ( + self._affected_thermal_trip_points + and self._affected_trip_point_temp + ): for trip_point in self._affected_thermal_trip_points: self._backup_thermal_trip_point_temp( thermal_zone=self._affected_thermal_zone, - trip_point_count=trip_point + trip_point_count=trip_point, ) self._set_thermal_trip_point_temp( thermal_zone=self._affected_thermal_zone, trip_point_count=trip_point, - temp_value=self._affected_trip_point_temp + temp_value=self._affected_trip_point_temp, ) def restore_default_mode(self): @@ -222,52 +228,60 @@ def restore_default_mode(self): # Configure Thermal if self._change_thermal_mode: self._restore_thermal_mode() - if self._affected_thermal_trip_points and \ - self._affected_trip_point_temp: + if ( + self._affected_thermal_trip_points + and self._affected_trip_point_temp + ): for trip_point in self._affected_thermal_trip_points: self._restore_thermal_trip_point_temp( thermal_zone=self._affected_thermal_zone, - trip_point_count=trip_point + trip_point_count=trip_point, ) def _backup_cpu_governor(self, policy): governor = self._get_cpu_governor(policy) self._set_node_value( node_path=BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy), - value=governor + value=governor, ) def _restore_cpu_governor(self, policy): origianl_value = self._get_node_value( - BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy)) + BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy) + ) self._set_cpu_governor(governor_mode=origianl_value, policy=policy) def _backup_cpuidle_state_disable(self, cpu_count, state_count): value = self._get_node_value( - PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count)) + PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count) + ) self._set_node_value( BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( - cpu_count, state_count), - value + cpu_count, state_count + ), + value, ) def _restore_cpuidle_state(self, cpu_count, state_count): origianl_value = self._get_node_value( BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( - cpu_count, state_count)) + cpu_count, state_count + ) + ) self._toggle_cpuidle_state_disable( - cpu_count, state_count, int(origianl_value)) + cpu_count, state_count, int(origianl_value) + ) def _extract_cpu_state_numbers(self): - ''' + """ Extracts CPU state numbers from files in the specified directory. Returns: - List of tuples: Each tuple contains two integers representing CPU numbers (X) and state numbers (Y). - ''' - pattern = re.compile(r'/cpu(\d+)/cpuidle/state(\d+)/disable') + """ + pattern = re.compile(r"/cpu(\d+)/cpuidle/state(\d+)/disable") results = [] # Walk through the directory and its subdirectories @@ -288,46 +302,56 @@ def _backup_gpu_governor(self, soc): governor = self._get_gpu_governor(soc) self._set_node_value( node_path=BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc), - value=governor + value=governor, ) def _restore_gpu_governor(self, soc): origianl_value = self._get_node_value( - BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc)) + BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc) + ) self._set_gpu_governor(governor_mode=origianl_value, soc=soc) def _backup_thermal_mode(self): - value = self._get_node_value(node_path=PATH_OF_THERMAL_MODE.format( - self._affected_thermal_zone)) + value = self._get_node_value( + node_path=PATH_OF_THERMAL_MODE.format(self._affected_thermal_zone) + ) self._set_node_value( node_path=BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( - self._affected_thermal_zone), - value=value + self._affected_thermal_zone + ), + value=value, ) def _restore_thermal_mode(self): origianl_value = self._get_node_value( BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( - self._affected_thermal_zone)) + self._affected_thermal_zone + ) + ) self._toggle_thermal_mode(self._affected_thermal_zone, origianl_value) def _backup_thermal_trip_point_temp(self, thermal_zone, trip_point_count): temp_value = self._get_thermal_trip_point_temp( - thermal_zone=thermal_zone, trip_point_count=trip_point_count) + thermal_zone=thermal_zone, trip_point_count=trip_point_count + ) self._set_node_value( node_path=BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( - thermal_zone, trip_point_count), - value=temp_value + thermal_zone, trip_point_count + ), + value=temp_value, ) def _restore_thermal_trip_point_temp(self, thermal_zone, trip_point_count): origianl_temp = self._get_node_value( BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( - thermal_zone, trip_point_count)) + thermal_zone, trip_point_count + ) + ) self._set_thermal_trip_point_temp( thermal_zone=thermal_zone, trip_point_count=trip_point_count, - temp_value=origianl_temp) + temp_value=origianl_temp, + ) def get_testing_parameters(platform): From 8e1cd1bc85a909aa0e5a3874ee7fcbd5c56332da Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 1 Aug 2024 16:37:12 +0800 Subject: [PATCH 06/18] Support load external python module to enable performance mode --- .../bin/genio_performance_mode.py | 4 +- .../bin/gst_v4l2_video_decoder_performance.py | 10 +- .../bin/performance_mode_controller.py | 185 ++++++++++++++++-- .../units/video-codec/jobs.pxu | 4 +- .../units/video-codec/test-plan.pxu | 2 +- 5 files changed, 184 insertions(+), 21 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py index 4e6d22a1bb..a51ddacbb9 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py @@ -424,11 +424,11 @@ def get_testing_parameters(platform): @contextlib.contextmanager -def performance_mode(target: str): +def performance_mode(**kwargs): # Run Performance Mode try: logging.info("======== Enable Performance Mode ========") - testing_params = get_testing_parameters(target) + testing_params = get_testing_parameters(**kwargs) PerformanceModeManager(**testing_params).set_performance_mode() yield finally: diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 853f08d5b8..47dd0f7188 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -24,7 +24,7 @@ import shlex import subprocess -from performance_mode_controller import performance_mode +from performance_mode_controller import get_performance_ctx_function logging.basicConfig(level=logging.INFO) @@ -225,7 +225,13 @@ def main() -> None: sink=args.sink, ) - with performance_mode(args.performance_mode_target): + output = "" + + if args.performance_mode_target: + performance_mode_ctx = get_performance_ctx_function() + with performance_mode_ctx(platform=args.performance_mode_target): + output = execute_command(cmd).rstrip(os.linesep) + else: output = execute_command(cmd).rstrip(os.linesep) if not is_valid_result(output, args.minimum_fps): diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py index a5541a12c3..74aaeb0d46 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py @@ -20,20 +20,177 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import contextlib +import importlib.util +import logging +import os +from importlib.machinery import SourceFileLoader +from typing import Callable, Optional -@contextlib.contextmanager -def performance_mode(target: str = "", **kwrags): +logging.basicConfig(level=logging.INFO) + + +def get_performance_ctx_function() -> Optional[Callable]: + + # Get the full path of the specific python module which implements the + # real logic to control the enable and disable performance mode. + # In this way, we can leverage the perfromance python module which be + # implemented at each project checkbox instead of duplicating same script + # in ce-oem provider. + # It's the empty value by defaul, you have to define it as Checkbox's + # configuraion. + PERFORMANCE_PYTHON_MODULE_PATH = os.environ.get( + "PERFORMANCE_PYTHON_MODULE_PATH", "" + ) + if not os.path.exists(PERFORMANCE_PYTHON_MODULE_PATH): + raise FileNotFoundError( + ( + "Fail to get the full path of performance python module. " + "Please define the path in Checkbox configuration. " + "e.g. PERFORMANCE_PYTHON_MODULE_PATH=/path/of/the/module.py" + ) + ) + # Get the name of context manager function which controls the performance + # mode. Default name is performance_mode. You can assign the specific name + # via environment variable + PERFORMANCE_FUNCTION_NAME = os.environ.get( + "PERFORMANCE_FUNCTION_NAME", "performance_mode" + ) + + return get_function_from_a_module( + module_name=os.path.basename(PERFORMANCE_PYTHON_MODULE_PATH), + module_path=PERFORMANCE_PYTHON_MODULE_PATH, + function_name=PERFORMANCE_FUNCTION_NAME, + ) + + +class ModuleLoader: + """ + A class to load Python modules dynamically from a given file path. + + Attributes: + ----------- + module_name : str + The name to assign to the module once it's loaded. + full_path : str + The full file path to the module. + module : Optional[object] + The loaded module object, initialized to None. + + Methods: + -------- + load_module() -> None: + Loads the module from the specified file path. + get_function(func_name: str) -> Callable: + Retrieves a function from the loaded module. + """ + + def __init__(self, module_name: str, full_path: str): + """ + Initializes the ModuleLoader with the given module name and path. + + Parameters: + ----------- + module_name : str + The name to assign to the module once it's loaded. + full_path : str + The full file path to the module. + """ + self.module_name = module_name + self.full_path = full_path + self.module: Optional[object] = None + + def load_module(self) -> None: + """ + Loads the module from the specified file path. + + Raises: + ------- + FileNotFoundError: + If the module file does not exist. + """ + try: + loader = SourceFileLoader(self.module_name, self.full_path) + spec = importlib.util.spec_from_loader(self.module_name, loader) + self.module = importlib.util.module_from_spec(spec) + loader.exec_module(self.module) + except FileNotFoundError: + logging.error( + "Module '{}' cannot be loaded since '{}' doesn't exist".format( + self.module_name, self.full_path + ) + ) + raise + + def get_function(self, func_name: str) -> Callable: + """ + Retrieves a function from the loaded module. + + Parameters: + ----------- + func_name : str + The name of the function to retrieve. + + Returns: + -------- + Callable: + The function object from the module. + + Raises: + ------- + AttributeError: + If the function is not found in the module. + """ + if self.module is None: + self.load_module() + try: + return getattr(self.module, func_name) + except AttributeError: + logging.error( + "Function '{}' not found in module '{}'".format( + func_name, self.module_name + ) + ) + raise + + +def get_function_from_a_module( + module_name: str, + module_path: str, + function_name: str, +) -> Optional[Callable]: + """ + Loads a module and retrieves a specified function. + + Parameters: + ----------- + module_name : str + The name of the module to load. + module_path : str + The full file path to the module. + function_name : str, optional + The name of the function to retrieve from the module + (default is "performance_mode"). + + Returns: + -------- + Optional[Callable]: + The function object if found + + Raises: + ------- + SystemExit: + If the module or function cannot be loaded, the program exits. + """ try: - # Run performance mode if target string starts with "genio" - if target.startswith("genio"): - from genio_performance_mode import performance_mode - - with performance_mode(target): - yield - # Do nothing if no specific target - else: - yield - finally: - pass + logging.info( + ( + "Trying to load the '{}' module from '{}' to get '{}'" + " function" + ).format(module_name, module_path, function_name) + ) + return ModuleLoader(module_name, module_path).get_function( + function_name + ) + except Exception: + raise SystemExit() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu index 4ac68d74d7..22e2a552bc 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu @@ -69,6 +69,6 @@ estimated_duration: 1s imports: from com.canonical.plainbox import manifest requires: manifest.has_video_codec == 'True' flags: also-after-suspend -environ: GST_LAUNCH_BIN +environ: GST_LAUNCH_BIN, PERFORMANCE_PYTHON_MODULE_PATH, PERFORMANCE_FUNCTION_NAME command: - gst_v4l2_video_decoder_performance.py -gp {{golden_sample_file}} -dp {{decoder_plugin}} -mf {{minimum_fps}} -pmt {{performance_target}} + gst_v4l2_video_decoder_performance.py -gp {{golden_sample_file}} -dp {{decoder_plugin}} -mf {{minimum_fps}} -pmt "{{performance_target}}" diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu index 3ae96c5604..b82afa754e 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu @@ -23,5 +23,5 @@ _description: Automated tests for Video Codec in before suspend and post suspend bootstrap_include: video_codec_resource include: - ce-oem-video-codec/gst_v4l2_video_decoder_md5_checksum_comparison + #ce-oem-video-codec/gst_v4l2_video_decoder_md5_checksum_comparison ce-oem-video-codec/gst_v4l2_video_decoder_performance_fakesink From cad3d99fc23b22ba970fef85bdd0d2907cf8557d Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 1 Aug 2024 16:47:34 +0800 Subject: [PATCH 07/18] Don't need to log error log --- .../bin/gst_v4l2_video_decoder_performance.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 47dd0f7188..9304acc79c 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -145,8 +145,7 @@ def execute_command(cmd: str) -> str: logging.info(ret.stdout) return ret.stdout except Exception as e: - logging.error(e.stderr) - raise SystemExit(e.returncode) + raise SystemExit(e) def is_valid_result(input_text: str, min_fps: float) -> bool: From cef4d0533ecd2357dfa56605180c830d70db2612 Mon Sep 17 00:00:00 2001 From: baconyao Date: Tue, 6 Aug 2024 10:05:24 +0800 Subject: [PATCH 08/18] Refactor to a singl function to get function from a module instead of using Class --- .../bin/performance_mode_controller.py | 143 ++++++------------ 1 file changed, 46 insertions(+), 97 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py index 74aaeb0d46..e19583ad04 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py @@ -31,10 +31,34 @@ def get_performance_ctx_function() -> Optional[Callable]: + """ + Get the specific context manager function of controlling performance mode. + The function helps you load a module which can not in the PYTHONPATH. + Therefore, developers can implement the python script of controlling + performance mode in each Checkbox Project and be used by ce-oem's script. + + Environment Variables: + ----------- + PERFORMANCE_PYTHON_MODULE_PATH + The full path of a python module that you are interested in even it's + not in PYTHONPATH + PERFORMANCE_FUNCTION_NAME + The name of ctx function to control the performance in a module. + + Returns: + -------- + Optional[Callable]: + The function object if found + + Raises: + ------- + SystemExit: + If the module or function cannot be loaded, the program exits. + """ # Get the full path of the specific python module which implements the # real logic to control the enable and disable performance mode. - # In this way, we can leverage the perfromance python module which be + # In this way, we can leverage the performance python module which be # implemented at each project checkbox instead of duplicating same script # in ce-oem provider. # It's the empty value by defaul, you have to define it as Checkbox's @@ -42,14 +66,15 @@ def get_performance_ctx_function() -> Optional[Callable]: PERFORMANCE_PYTHON_MODULE_PATH = os.environ.get( "PERFORMANCE_PYTHON_MODULE_PATH", "" ) - if not os.path.exists(PERFORMANCE_PYTHON_MODULE_PATH): + if PERFORMANCE_PYTHON_MODULE_PATH == "": raise FileNotFoundError( ( - "Fail to get the full path of performance python module. " + "Fail to get a performance python module" "Please define the path in Checkbox configuration. " "e.g. PERFORMANCE_PYTHON_MODULE_PATH=/path/of/the/module.py" ) ) + # Get the name of context manager function which controls the performance # mode. Default name is performance_mode. You can assign the specific name # via environment variable @@ -64,96 +89,6 @@ def get_performance_ctx_function() -> Optional[Callable]: ) -class ModuleLoader: - """ - A class to load Python modules dynamically from a given file path. - - Attributes: - ----------- - module_name : str - The name to assign to the module once it's loaded. - full_path : str - The full file path to the module. - module : Optional[object] - The loaded module object, initialized to None. - - Methods: - -------- - load_module() -> None: - Loads the module from the specified file path. - get_function(func_name: str) -> Callable: - Retrieves a function from the loaded module. - """ - - def __init__(self, module_name: str, full_path: str): - """ - Initializes the ModuleLoader with the given module name and path. - - Parameters: - ----------- - module_name : str - The name to assign to the module once it's loaded. - full_path : str - The full file path to the module. - """ - self.module_name = module_name - self.full_path = full_path - self.module: Optional[object] = None - - def load_module(self) -> None: - """ - Loads the module from the specified file path. - - Raises: - ------- - FileNotFoundError: - If the module file does not exist. - """ - try: - loader = SourceFileLoader(self.module_name, self.full_path) - spec = importlib.util.spec_from_loader(self.module_name, loader) - self.module = importlib.util.module_from_spec(spec) - loader.exec_module(self.module) - except FileNotFoundError: - logging.error( - "Module '{}' cannot be loaded since '{}' doesn't exist".format( - self.module_name, self.full_path - ) - ) - raise - - def get_function(self, func_name: str) -> Callable: - """ - Retrieves a function from the loaded module. - - Parameters: - ----------- - func_name : str - The name of the function to retrieve. - - Returns: - -------- - Callable: - The function object from the module. - - Raises: - ------- - AttributeError: - If the function is not found in the module. - """ - if self.module is None: - self.load_module() - try: - return getattr(self.module, func_name) - except AttributeError: - logging.error( - "Function '{}' not found in module '{}'".format( - func_name, self.module_name - ) - ) - raise - - def get_function_from_a_module( module_name: str, module_path: str, @@ -189,8 +124,22 @@ def get_function_from_a_module( " function" ).format(module_name, module_path, function_name) ) - return ModuleLoader(module_name, module_path).get_function( - function_name + loader = SourceFileLoader(module_name, module_path) + spec = importlib.util.spec_from_loader(module_name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return getattr(module, function_name) + except FileNotFoundError: + logging.error( + "Module '%s' cannot be loaded since '%s' doesn't exist", + module_name, + module_path, + ) + raise + except AttributeError: + logging.error( + "Function '%s' not found in module '%s'", + function_name, + module_name, ) - except Exception: - raise SystemExit() + raise From 82c1cbfead948cb098fdd26efc1456db82f30698 Mon Sep 17 00:00:00 2001 From: baconyao Date: Mon, 12 Aug 2024 09:18:58 +0800 Subject: [PATCH 09/18] Remove genio_performance_mode script --- .../bin/genio_performance_mode.py | 436 ------------------ 1 file changed, 436 deletions(-) delete mode 100755 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py deleted file mode 100755 index a51ddacbb9..0000000000 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/genio_performance_mode.py +++ /dev/null @@ -1,436 +0,0 @@ -#!/usr/bin/env python3 -# This script is used to all genio boards and should be run as a super user - -# Copyright 2024 Canonical Ltd. -# Written by: -# Patrick Chang -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import contextlib -import logging -import os -import re - -logging.basicConfig(level=logging.INFO) - -SYS_DEVICES_CPU = "/sys/devices/system/cpu" -PATH_CPU_GOVERNOR = SYS_DEVICES_CPU + "/cpufreq/policy{}/scaling_governor" -PATH_CPUIDLE_STATE_DISABLE = SYS_DEVICES_CPU + "/cpu{}/cpuidle/state{}/disable" -PATH_GPU_GOVERNOR = "/sys/devices/platform/soc/{}/devfreq/{}/governor" -SYS_CLASS_THERMAL_ZONE = "/sys/class/thermal/thermal_zone{}" -PATH_OF_THERMAL_MODE = SYS_CLASS_THERMAL_ZONE + "/mode" -PATH_OF_THERMAL_TRIP_POINT = SYS_CLASS_THERMAL_ZONE + "/trip_point_{}_temp" -BACKUP_CPU_GOVERNOR_STR_TEMPLEATE = "/tmp/p_{}_sg" -BACKUP_GPU_GOVERNOR_STR_TEMPLEATE = "/tmp/soc_{}_g" -BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE = "/tmp/thermal_{}_trip_{}_temp" -BACKUP_THERMAL_MODE_STR_TEMPLEATE = "/tmp/thermal_{}_mode" -BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE = "/tmp/c_{}_s_{}_disable" - - -class PerformanceController: - """ - PerformanceController provides some methods that handle the get, set and - backup actions. - """ - - def _get_node_value(self, node_path): - with open(node_path, "r") as f: - value = f.read().strip() - return value - - def _set_node_value(self, node_path, value): - with open(node_path, "w") as out: - out.write(str(value)) - - def _get_cpu_governor(self, policy): - node_path = PATH_CPU_GOVERNOR.format(policy) - governor = self._get_node_value(node_path=node_path) - return governor - - def _set_cpu_governor(self, governor_mode, policy): - node_path = PATH_CPU_GOVERNOR.format(policy) - self._set_node_value(node_path=node_path, value=governor_mode) - - def _get_gpu_governor(self, soc): - node_path = PATH_GPU_GOVERNOR.format(soc, soc) - governor = self._get_node_value(node_path=node_path) - return governor - - def _set_gpu_governor(self, governor_mode, soc): - node_path = PATH_GPU_GOVERNOR.format(soc, soc) - self._set_node_value(node_path=node_path, value=governor_mode) - - def _get_thermal_trip_point_temp(self, thermal_zone, trip_point_count): - node_path = PATH_OF_THERMAL_TRIP_POINT.format( - thermal_zone, trip_point_count - ) - temp_value = self._get_node_value(node_path=node_path) - return temp_value - - def _set_thermal_trip_point_temp( - self, thermal_zone, trip_point_count, temp_value - ): - node_path = PATH_OF_THERMAL_TRIP_POINT.format( - thermal_zone, trip_point_count - ) - self._set_node_value(node_path=node_path, value=temp_value) - - def _set_apusys(self, value): - node_path = "/sys/kernel/debug/apusys/power" - self._set_node_value(node_path=node_path, value=value) - - def _toggle_cpuidle_state_disable(self, cpu_count, state_count, value): - """ - Toggle the 'disable' attribute for CPU idle states. - - Args: - cpu_count (int): The count of CPUs. - state_count (int): The count of CPU idle stat. - value (int): - The value to set for the 'disable' attribute (0 or 1). - - Raises: - ValueError: If the provided value is not 0 or 1. - """ - if value not in [0, 1]: - raise ValueError("Value must be 0 or 1") - self._set_node_value( - PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count), value - ) - - def _toggle_thermal_mode(self, thermal_zone, value): - if value not in ["disabled", "enabled"]: - raise ValueError("Thermal mode must be enabled or disabled") - self._set_node_value(PATH_OF_THERMAL_MODE.format(thermal_zone), value) - - -class PerformanceModeManager(PerformanceController): - """ - PerformanceModeManager class for managing system performance modes. - - This class extends the PerformanceController class and provides methods - to set and restore performance modes for CPU, GPU, APU, and thermal - configurations. - - Args: - affected_policies (list): List of affected CPU policies. - affected_thermal_zone (int): Thermal zone to be affected. - affected_thermal_trip_points (list): - List of thermal trip points to be affected. - affected_trip_point_temp (int): - Temperature value for affected thermal trip points. - change_thermal_mode (bool): - Flag to indicate if thermal mode should be changed. - gpu_soc_name (str): Name of the GPU SoC to be affected. - enable_apu (bool): Flag to indicate if APU should be enabled. - - Methods: - set_performance_mode(): - Configures CPU, GPU, APU and thermal settings for performance mode. - - restore_default_mode(): - Restores default CPU, GPU and thermal settings. - - Note: - This class assumes the existence of methods such as - _backup_cpu_governor, _set_cpu_governor, _toggle_cpuidle_state_disable, - _backup_gpu_governor, _set_gpu_governor, _set_apusys, - _toggle_thermal_mode, _backup_thermal_trip_point_temp, - _set_thermal_trip_point_temp, _restore_cpu_governor, - _restore_gpu_governor, and _restore_thermal_trip_point_temp - in the parent class (PerformanceController). - """ - - def __init__( - self, - affected_policies: list, - affected_thermal_zone: int, - affected_thermal_trip_points: list, - affected_trip_point_temp: int, - change_thermal_mode: bool, - gpu_soc_name: str, - enable_apu: bool, - ): - self._affected_policies = affected_policies - self._affected_thermal_zone = affected_thermal_zone - self._affected_thermal_trip_points = affected_thermal_trip_points - self._affected_trip_point_temp = affected_trip_point_temp - self._change_thermal_mode = change_thermal_mode - self._gpu_soc_name = gpu_soc_name - self._enable_apu = enable_apu - self._affected_cpuidle_count_state = self._extract_cpu_state_numbers() - - def set_performance_mode(self): - """ - Set performance mode by configuring CPU, GPU, APU, and thermal - settings. - - This method iterates over affected CPU policies, sets CPU governors to - "performance", toggles CPU idle state, sets GPU governor to - "performance", sets APU configuration, and adjusts thermal settings - based on provided parameters. - """ - # Configure CPU - for policy in self._affected_policies: - self._backup_cpu_governor(policy) - self._set_cpu_governor(governor_mode="performance", policy=policy) - for item in self._affected_cpuidle_count_state: - self._backup_cpuidle_state_disable(item[0], item[1]) - self._toggle_cpuidle_state_disable(item[0], item[1], 1) - # Configure GPU - if self._gpu_soc_name: - self._backup_gpu_governor(soc=self._gpu_soc_name) - self._set_gpu_governor( - governor_mode="performance", soc=self._gpu_soc_name - ) - # Configure APU - if self._enable_apu: - self._set_apusys(value="dvfs_debug 0") - # Configure Thermal - if self._change_thermal_mode: - self._backup_thermal_mode() - self._toggle_thermal_mode(self._affected_thermal_zone, "disabled") - if ( - self._affected_thermal_trip_points - and self._affected_trip_point_temp - ): - for trip_point in self._affected_thermal_trip_points: - self._backup_thermal_trip_point_temp( - thermal_zone=self._affected_thermal_zone, - trip_point_count=trip_point, - ) - self._set_thermal_trip_point_temp( - thermal_zone=self._affected_thermal_zone, - trip_point_count=trip_point, - temp_value=self._affected_trip_point_temp, - ) - - def restore_default_mode(self): - # Configure CPU - for policy in self._affected_policies: - self._restore_cpu_governor(policy) - for item in self._affected_cpuidle_count_state: - self._restore_cpuidle_state(item[0], item[1]) - # Configure GPU - if self._gpu_soc_name: - self._restore_gpu_governor(soc=self._gpu_soc_name) - # Configure Thermal - if self._change_thermal_mode: - self._restore_thermal_mode() - if ( - self._affected_thermal_trip_points - and self._affected_trip_point_temp - ): - for trip_point in self._affected_thermal_trip_points: - self._restore_thermal_trip_point_temp( - thermal_zone=self._affected_thermal_zone, - trip_point_count=trip_point, - ) - - def _backup_cpu_governor(self, policy): - governor = self._get_cpu_governor(policy) - self._set_node_value( - node_path=BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy), - value=governor, - ) - - def _restore_cpu_governor(self, policy): - origianl_value = self._get_node_value( - BACKUP_CPU_GOVERNOR_STR_TEMPLEATE.format(policy) - ) - self._set_cpu_governor(governor_mode=origianl_value, policy=policy) - - def _backup_cpuidle_state_disable(self, cpu_count, state_count): - value = self._get_node_value( - PATH_CPUIDLE_STATE_DISABLE.format(cpu_count, state_count) - ) - self._set_node_value( - BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( - cpu_count, state_count - ), - value, - ) - - def _restore_cpuidle_state(self, cpu_count, state_count): - origianl_value = self._get_node_value( - BACKUP_CPUIDEL_STATE_DISABLE_STR_TEMPLEATE.format( - cpu_count, state_count - ) - ) - self._toggle_cpuidle_state_disable( - cpu_count, state_count, int(origianl_value) - ) - - def _extract_cpu_state_numbers(self): - """ - Extracts CPU state numbers from files in the specified directory. - - Returns: - - List of tuples: - Each tuple contains two integers representing CPU numbers (X) and - state numbers (Y). - """ - pattern = re.compile(r"/cpu(\d+)/cpuidle/state(\d+)/disable") - results = [] - - # Walk through the directory and its subdirectories - for root, _, files in os.walk(SYS_DEVICES_CPU): - for file in files: - # Construct the full path for each file - full_path = os.path.join(root, file) - - # Use the regular expression pattern to directly extract - # numbers - match = pattern.search(full_path) - if match: - results.append((int(match.group(1)), int(match.group(2)))) - - return results - - def _backup_gpu_governor(self, soc): - governor = self._get_gpu_governor(soc) - self._set_node_value( - node_path=BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc), - value=governor, - ) - - def _restore_gpu_governor(self, soc): - origianl_value = self._get_node_value( - BACKUP_GPU_GOVERNOR_STR_TEMPLEATE.format(soc) - ) - self._set_gpu_governor(governor_mode=origianl_value, soc=soc) - - def _backup_thermal_mode(self): - value = self._get_node_value( - node_path=PATH_OF_THERMAL_MODE.format(self._affected_thermal_zone) - ) - self._set_node_value( - node_path=BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( - self._affected_thermal_zone - ), - value=value, - ) - - def _restore_thermal_mode(self): - origianl_value = self._get_node_value( - BACKUP_THERMAL_MODE_STR_TEMPLEATE.format( - self._affected_thermal_zone - ) - ) - self._toggle_thermal_mode(self._affected_thermal_zone, origianl_value) - - def _backup_thermal_trip_point_temp(self, thermal_zone, trip_point_count): - temp_value = self._get_thermal_trip_point_temp( - thermal_zone=thermal_zone, trip_point_count=trip_point_count - ) - self._set_node_value( - node_path=BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( - thermal_zone, trip_point_count - ), - value=temp_value, - ) - - def _restore_thermal_trip_point_temp(self, thermal_zone, trip_point_count): - origianl_temp = self._get_node_value( - BACKUP_THERMAL_TRIP_POINT_STR_TEMPLEATE.format( - thermal_zone, trip_point_count - ) - ) - self._set_thermal_trip_point_temp( - thermal_zone=thermal_zone, - trip_point_count=trip_point_count, - temp_value=origianl_temp, - ) - - -def get_testing_parameters(platform): - """ - Get testing parameters based on the specified platform. - - Args: - platform (str): The platform identifier. - - Returns: - dict: A dictionary containing testing parameters for the given - platform. - The keys and values may include: - - 'affected_policies' (list): List of affected CPU policies. - - 'affected_cpuidle_count_state' (list): - List containing CPU idle count and state. - - 'affected_thermal_zone' (int): Thermal zone to be affected. - - 'affected_thermal_trip_points' (list): - List of affected thermal trip points. - - 'affected_trip_point_temp' (int): - Temperature value for affected thermal trip points. - - 'change_thermal_mode' (bool): - Flag to indicate if thermal mode should be changed. - - 'gpu_soc_name' (str): Name of the GPU SoC to be affected. - - 'enable_apu' (bool): Flag to indicate if APU should be enabled. - """ - testing_params = {} - if platform == "genio-1200": - testing_params = { - "affected_policies": [0, 4], - "affected_thermal_zone": 0, - "affected_thermal_trip_points": [], - "affected_trip_point_temp": 0, - "change_thermal_mode": True, - "gpu_soc_name": "13000000.mali", - "enable_apu": True, - } - elif platform == "genio-700": - testing_params = { - "affected_policies": [0, 6], - "affected_thermal_zone": 0, - "affected_thermal_trip_points": [0, 1, 2], - "affected_trip_point_temp": 1115000, - "change_thermal_mode": True, - "gpu_soc_name": "13000000.mali", - "enable_apu": True, - } - elif platform == "genio-510": - testing_params = { - "affected_policies": [0, 4], - "affected_thermal_zone": 0, - "affected_thermal_trip_points": [0, 1, 2], - "affected_trip_point_temp": 1115000, - "change_thermal_mode": True, - "gpu_soc_name": "13000000.mali", - "enable_apu": True, - } - elif platform == "genio-350": - testing_params = { - "affected_policies": [0], - "affected_thermal_zone": 0, - "affected_thermal_trip_points": [], - "affected_trip_point_temp": 0, - "change_thermal_mode": True, - "gpu_soc_name": "13040000.mali", - "enable_apu": False, - } - - return testing_params - - -@contextlib.contextmanager -def performance_mode(**kwargs): - # Run Performance Mode - try: - logging.info("======== Enable Performance Mode ========") - testing_params = get_testing_parameters(**kwargs) - PerformanceModeManager(**testing_params).set_performance_mode() - yield - finally: - logging.info("======== Disable Performance Mode ========") - PerformanceModeManager(**testing_params).restore_default_mode() From 335d00e4486827f9cc514da02ce8d8442827437f Mon Sep 17 00:00:00 2001 From: baconyao Date: Mon, 12 Aug 2024 12:01:18 +0800 Subject: [PATCH 10/18] Refactor resource script --- .../bin/gst_resources_generator.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py index d4a46554b3..b102a5f13b 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py @@ -60,6 +60,8 @@ def register_arguments() -> argparse.Namespace: class GstResources: + # video_golden_samples is the name of folder in hardware_codec_testing_data + # repo. https://github.com/canonical/hardware_codec_testing_data VIDEO_GOLDEN_SAMPLES = "video_golden_samples" def __init__(self, args: argparse.Namespace) -> None: @@ -97,8 +99,11 @@ def _v4l2_video_decoder_md5_checksum_comparison_helper( gst_v4l2_video_decoder_md5_checksum_comparison scenario """ name = "{}x{}-{}-{}".format(width, height, decoder_plugin, color_space) - golden_sample_file = "{}/video_golden_samples/{}.{}".format( - self._args.video_codec_testing_data_path, name, source_format + golden_sample_file = "{}/{}/{}.{}".format( + self._args.video_codec_testing_data_path, + self.VIDEO_GOLDEN_SAMPLES, + name, + source_format, ) golden_md5_checkum_file = "{}/{}/golden_md5_checksum/{}/{}.md5".format( self._args.video_codec_testing_data_path, @@ -176,8 +181,9 @@ def gst_v4l2_video_decoder_performance_fakesink( "scenario": self._current_scenario_name, "decoder_plugin": item["decoder_plugin"], "minimum_fps": item["minimum_fps"], - "golden_sample_file": "{}/video_golden_samples/{}".format( + "golden_sample_file": "{}/{}/{}".format( self._args.video_codec_testing_data_path, + self.VIDEO_GOLDEN_SAMPLES, item["golden_sample_file"], ), # performance_target is "" means won't enable performance From 030c7f1b4d4e6a265c24fccfd8d7fedb62a8140f Mon Sep 17 00:00:00 2001 From: baconyao Date: Mon, 12 Aug 2024 12:02:01 +0800 Subject: [PATCH 11/18] Add platforms' json data --- .../video-codec-test-confs/genio-350.json | 34 ++++++++++++++++++- .../video-codec-test-confs/genio-510.json | 34 ++++++++++++++++++- .../video-codec-test-confs/genio-700.json | 34 ++++++++++++++++++- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-350.json b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-350.json index d9979a9f63..665b27f96d 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-350.json +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-350.json @@ -23,6 +23,38 @@ ] } ] - } + }, + "gst_v4l2_video_decoder_performance_fakesink": [ + { + "decoder_plugin": "v4l2h264dec", + "golden_sample_file": "H264_1920x1080_60fps_40Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2h265dec", + "golden_sample_file": "H265_1920x1080_60fps_40Mbps", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2mpeg4dec", + "golden_sample_file": "MPEG-4_1920x1080_60fps_40Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp8dec", + "golden_sample_file": "VP8_1920x1080_60fps_40Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp9dec", + "golden_sample_file": "VP9_1920x1080_60fps_40Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + } + ] } diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-510.json b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-510.json index b64c271c59..76ef71bba7 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-510.json +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-510.json @@ -28,6 +28,38 @@ ] } ] - } + }, + "gst_v4l2_video_decoder_performance_fakesink": [ + { + "decoder_plugin": "v4l2h264dec", + "golden_sample_file": "H264_3840x2160_75fps_160Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2h265dec", + "golden_sample_file": "H265_3840x2160_75fps_160Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2mpeg4dec", + "golden_sample_file": "MPEG-4_1920x1080_60fps_60Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp8dec", + "golden_sample_file": "VP8_1920x1080_60fps_40Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp9dec", + "golden_sample_file": "VP9_3840x2160_75fps_120Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + } + ] } diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-700.json b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-700.json index b64c271c59..d1435f03c3 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-700.json +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/video-codec-test-confs/genio-700.json @@ -28,6 +28,38 @@ ] } ] - } + }, + "gst_v4l2_video_decoder_performance_fakesink": [ + { + "decoder_plugin": "v4l2h264dec", + "golden_sample_file": "H264_3840x2160_75fps_160Mbps.mp4", + "minimum_fps": 75, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2h265dec", + "golden_sample_file": "H265_3840x2160_75fps_160Mbps.mp4", + "minimum_fps": 75, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2mpeg4dec", + "golden_sample_file": "MPEG-4_1920x1080_60fps_60Mbps.mp4", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp8dec", + "golden_sample_file": "VP8_1920x1080_60fps_40Mbps.webm", + "minimum_fps": 60, + "enable_performance_mode": true + }, + { + "decoder_plugin": "v4l2vp9dec", + "golden_sample_file": "VP9_3840x2160_75fps_120Mbps.webm", + "minimum_fps": 75, + "enable_performance_mode": true + } + ] } From c122c8e6f0f17724cac3bff5c87e8e5ab93e9ada Mon Sep 17 00:00:00 2001 From: baconyao Date: Mon, 12 Aug 2024 12:02:23 +0800 Subject: [PATCH 12/18] Fix the job and test plan --- .../checkbox-provider-ce-oem/units/video-codec/jobs.pxu | 3 +++ .../checkbox-provider-ce-oem/units/video-codec/test-plan.pxu | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu index 22e2a552bc..0e26084713 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/jobs.pxu @@ -55,6 +55,9 @@ _verification: command: gst_v4l2_audio_video_synchronization.py -dp {{decoder_plugin}} -gp {{golden_sample_file}} -vs {{video_sink}} -cp "{{capssetter_pipeline}}" +unit: template +template-engine: jinja2 +template-resource: video_codec_resource template-filter: video_codec_resource.scenario == "gst_v4l2_video_decoder_performance_fakesink" template-unit: job template-id: ce-oem-video-codec/gst_v4l2_video_decoder_performance_fakesink diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu index b82afa754e..3ae96c5604 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/video-codec/test-plan.pxu @@ -23,5 +23,5 @@ _description: Automated tests for Video Codec in before suspend and post suspend bootstrap_include: video_codec_resource include: - #ce-oem-video-codec/gst_v4l2_video_decoder_md5_checksum_comparison + ce-oem-video-codec/gst_v4l2_video_decoder_md5_checksum_comparison ce-oem-video-codec/gst_v4l2_video_decoder_performance_fakesink From 72e5d876a459c98b464c18272de7d6435a7ee976 Mon Sep 17 00:00:00 2001 From: baconyao Date: Wed, 14 Aug 2024 15:47:48 +0800 Subject: [PATCH 13/18] Support fpsdisplaysink_sync to control the value of sync --- .../bin/gst_v4l2_video_decoder_performance.py | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 9304acc79c..821ab18bfc 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -86,12 +86,28 @@ def register_arguments(): ), ) + parser.add_argument( + "-fpss", + "--fpsdisplaysink_sync", + default="true", + type=str, + help=( + "The property option of fpsdisplaysink. (Default: true)" + "https://gstreamer.freedesktop.org/documentation/debugutilsbad/" + "fpsdisplaysink.html?gi-language=python#fpsdisplaysink:sync" + ), + ) + args = parser.parse_args() return args def build_gst_command( - gst_bin: str, golden_sample_path: str, decoder: str, sink: str + gst_bin: str, + golden_sample_path: str, + decoder: str, + sink: str, + fpsdisplaysink_sync: str, ) -> str: """ Builds a GStreamer command to process the golden sample. @@ -106,6 +122,10 @@ def build_gst_command( The decoder to use for the video, e.g., "v4l2vp8dec", "v4l2vp9dec". :param sink: The desired sink option, e.g., "fakesink". + :param fpsdisplaysink_sync: + The property option of fpsdisplaysink." + Ref: https://gstreamer.freedesktop.org/documentation/debugutilsbad/ + fpsdisplaysink.html?gi-language=python#fpsdisplaysink:sync :returns: The GStreamer command to execute. @@ -114,8 +134,8 @@ def build_gst_command( "{} -v filesrc location={} ! parsebin ! queue ! {} ! queue ! " "v4l2convert output-io-mode=dmabuf-import capture-io-mode=dmabuf ! " 'queue ! fpsdisplaysink video-sink="{}"' - " text-overlay=false sync=false" - ).format(gst_bin, golden_sample_path, decoder, sink) + " text-overlay=false sync={}" + ).format(gst_bin, golden_sample_path, decoder, sink, fpsdisplaysink_sync) return cmd @@ -222,6 +242,7 @@ def main() -> None: golden_sample_path=args.golden_sample_path, decoder=args.decoder_plugin, sink=args.sink, + fpsdisplaysink_sync=args.fpsdisplaysink_sync ) output = "" From eedee03c6a9859e7b0d506f4f57b8cf7ecf038f4 Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 15 Aug 2024 12:51:31 +0800 Subject: [PATCH 14/18] Update resource --- .../bin/gst_resources_generator.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py index b102a5f13b..f61adbe5e5 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py @@ -98,18 +98,22 @@ def _v4l2_video_decoder_md5_checksum_comparison_helper( Generate a resource item dictionary for gst_v4l2_video_decoder_md5_checksum_comparison scenario """ - name = "{}x{}-{}-{}".format(width, height, decoder_plugin, color_space) - golden_sample_file = "{}/{}/{}.{}".format( + name = "{}x{}-{}-{}".format( + width, height, decoder_plugin, color_space + ) + name_with_format = "{}.{}".format(name, source_format) + golden_sample_file = os.path.join( self._args.video_codec_testing_data_path, self.VIDEO_GOLDEN_SAMPLES, - name, - source_format, + name_with_format, ) - golden_md5_checkum_file = "{}/{}/golden_md5_checksum/{}/{}.md5".format( + md5_name = "{}.md5".format(name) + golden_md5_checkum_file = os.path.join( self._args.video_codec_testing_data_path, self._current_scenario_name, + "golden_md5_checksum", self._conf_name, - name, + md5_name, ) returned_dict = { From 34fa7a34d401e4b5f6b6969f379943c6cc223d16 Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 15 Aug 2024 13:01:10 +0800 Subject: [PATCH 15/18] Fix blake --- .../bin/gst_v4l2_video_decoder_performance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index 821ab18bfc..fd58cb3f78 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -242,7 +242,7 @@ def main() -> None: golden_sample_path=args.golden_sample_path, decoder=args.decoder_plugin, sink=args.sink, - fpsdisplaysink_sync=args.fpsdisplaysink_sync + fpsdisplaysink_sync=args.fpsdisplaysink_sync, ) output = "" From 124465e9e6f1449a4997a4889fff105478512c36 Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 15 Aug 2024 13:03:05 +0800 Subject: [PATCH 16/18] Fix blake --- .../checkbox-provider-ce-oem/bin/gst_resources_generator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py index f61adbe5e5..e4f7281e43 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py @@ -98,9 +98,7 @@ def _v4l2_video_decoder_md5_checksum_comparison_helper( Generate a resource item dictionary for gst_v4l2_video_decoder_md5_checksum_comparison scenario """ - name = "{}x{}-{}-{}".format( - width, height, decoder_plugin, color_space - ) + name = "{}x{}-{}-{}".format(width, height, decoder_plugin, color_space) name_with_format = "{}.{}".format(name, source_format) golden_sample_file = os.path.join( self._args.video_codec_testing_data_path, From 61bf9b332f9dbc2a64587354deac751ce0beca3e Mon Sep 17 00:00:00 2001 From: baconyao Date: Thu, 15 Aug 2024 15:20:40 +0800 Subject: [PATCH 17/18] Update --- .../bin/gst_resources_generator.py | 2 +- .../bin/gst_v4l2_video_decoder_performance.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py index e4f7281e43..97d489585c 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_resources_generator.py @@ -183,7 +183,7 @@ def gst_v4l2_video_decoder_performance_fakesink( "scenario": self._current_scenario_name, "decoder_plugin": item["decoder_plugin"], "minimum_fps": item["minimum_fps"], - "golden_sample_file": "{}/{}/{}".format( + "golden_sample_file": os.path.join( self._args.video_codec_testing_data_path, self.VIDEO_GOLDEN_SAMPLES, item["golden_sample_file"], diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py index fd58cb3f78..d963f98e53 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/gst_v4l2_video_decoder_performance.py @@ -225,11 +225,12 @@ def main() -> None: """ args = register_arguments() logging.info( - "Pass Criteria" - "\n 1. All dropped frames must be 0" - "\n 2. All average fps values must greater than or equal to {}".format( - args.minimum_fps - ) + ( + "Pass Criteria \n" + " 1. All dropped frames must be 0\n" + " 2. All average fps values must greater than or equal to %s" + ), + args.minimum_fps, ) # Check the golden sample exixt if not os.path.exists(args.golden_sample_path): From 4ff0a111eade7a30227c6f97c0fe31aa2f2d69a0 Mon Sep 17 00:00:00 2001 From: Pei Yao-Chang Date: Fri, 16 Aug 2024 11:57:09 +0800 Subject: [PATCH 18/18] Update contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py Co-authored-by: stanley31huang --- .../bin/performance_mode_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py index e19583ad04..4c8b1e0f03 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/performance_mode_controller.py @@ -119,10 +119,10 @@ def get_function_from_a_module( """ try: logging.info( - ( - "Trying to load the '{}' module from '{}' to get '{}'" - " function" - ).format(module_name, module_path, function_name) + "Trying to load the '%s' module from '%s' to get '%s' function", + module_name, + module_path, + function_name, ) loader = SourceFileLoader(module_name, module_path) spec = importlib.util.spec_from_loader(module_name, loader)