From f1a3be0ef4ab50753a6c46c03a453443fee85079 Mon Sep 17 00:00:00 2001 From: Jiri Pavela Date: Wed, 12 Aug 2026 16:37:47 +0200 Subject: [PATCH 1/4] Rework the profile/ package structure The perun/profile package structure has been reworked such that it can contain sub-packages related to different profile representations. This will allow us to better structure code related to different profile types, e.g., folded, and dependencies , e.g., pandas, or polars. Moreover, thanks to lazy imports, we can cut down the Perun startup time and import only relevant modules when, e.g., generating reports. --- perun/check/detection_kit.py | 4 ++-- perun/check/factory.py | 16 ++++++++-------- perun/check/methods/abstract_base_checker.py | 2 +- perun/check/methods/average_amount_threshold.py | 4 ++-- perun/check/methods/best_model_order_equality.py | 2 +- perun/check/methods/exclusive_time_outliers.py | 4 ++-- perun/check/methods/fast_check.py | 2 +- perun/check/methods/integral_comparison.py | 2 +- perun/check/methods/linear_regression.py | 2 +- perun/check/methods/local_statistics.py | 2 +- perun/check/methods/polynomial_regression.py | 2 +- perun/check/nonparam_kit.py | 2 +- perun/cli.py | 2 +- perun/cli_groups/check_cli.py | 2 +- perun/cli_groups/collect_cli.py | 4 ++-- perun/cli_groups/import_cli.py | 2 +- perun/cli_groups/showdiff_cli.py | 10 ++++------ perun/collect/memory/parsing.py | 2 +- perun/collect/trace/run.py | 2 +- perun/collect/trace/systemtap/parse_compact.py | 2 +- perun/deltadebugging/factory.py | 2 +- perun/fuzz/evaluate/by_perun.py | 2 +- perun/fuzz/factory.py | 2 +- perun/logic/commands.py | 6 +++--- perun/logic/index.py | 2 +- perun/logic/runner.py | 4 ++-- perun/logic/stats.py | 2 +- perun/logic/store.py | 2 +- perun/meson.build | 2 +- perun/postprocess/kernel_regression/run.py | 2 +- perun/postprocess/moving_average/run.py | 2 +- .../regression_analysis/data_provider.py | 4 ++-- perun/postprocess/regression_analysis/run.py | 10 +++++++--- perun/postprocess/regression_analysis/tools.py | 2 +- perun/postprocess/regressogram/run.py | 10 +++++++--- perun/profiles/__init__.py | 8 ++++++++ perun/profiles/__init__.pyi | 1 + perun/profiles/meson.build | 13 +++++++++++++ perun/{profile => profiles/native}/__init__.py | 2 +- perun/{profile => profiles/native}/__init__.pyi | 0 perun/{profile => profiles/native}/convert.py | 8 ++++---- perun/{profile => profiles/native}/factory.py | 2 +- perun/{profile => profiles/native}/helpers.py | 2 +- perun/{profile => profiles/native}/imports.py | 2 +- perun/{profile => profiles/native}/meson.build | 8 ++++---- perun/{profile => profiles/native}/query.py | 2 +- perun/{profile => profiles/native}/stats.py | 0 perun/select/abstract_base_selection.py | 3 +-- perun/select/whole_repository_selection.py | 9 +++++---- perun/testing/utils.py | 2 +- perun/utils/common/cli_kit.py | 2 +- perun/utils/common/diff_kit.py | 2 +- perun/utils/common/view_kit.py | 6 +++--- perun/utils/structs/diff_structs.py | 4 ++-- perun/view/bars/factory.py | 4 ++-- perun/view/bars/run.py | 2 +- perun/view/flamegraph/run.py | 4 ++-- perun/view/flow/factory.py | 4 ++-- perun/view/flow/run.py | 2 +- perun/view/scatter/factory.py | 4 ++-- perun/view/scatter/run.py | 2 +- perun/view/tableof/run.py | 8 +++++--- perun/view_diff/flamegraph.py | 2 +- perun/view_diff/report_folded.py | 6 ++++-- perun/view_diff/report_native.py | 2 +- perun/view_diff/short.py | 4 ++-- perun/workload/generator.py | 4 ++-- tests-perf/test_performance.py | 2 +- tests/conftest.py | 4 ++-- tests/test_collect.py | 2 +- tests/test_log.py | 2 +- tests/test_profile.py | 4 ++-- tests/test_profile_converters.py | 2 +- tests/test_query.py | 2 +- tests/test_select.py | 2 +- tests/test_utils.py | 2 +- 76 files changed, 151 insertions(+), 119 deletions(-) create mode 100644 perun/profiles/__init__.py create mode 100644 perun/profiles/__init__.pyi create mode 100644 perun/profiles/meson.build rename perun/{profile => profiles/native}/__init__.py (87%) rename perun/{profile => profiles/native}/__init__.pyi (100%) rename perun/{profile => profiles/native}/convert.py (98%) rename perun/{profile => profiles/native}/factory.py (99%) rename perun/{profile => profiles/native}/helpers.py (99%) rename perun/{profile => profiles/native}/imports.py (99%) rename perun/{profile => profiles/native}/meson.build (51%) rename perun/{profile => profiles/native}/query.py (99%) rename perun/{profile => profiles/native}/stats.py (100%) diff --git a/perun/check/detection_kit.py b/perun/check/detection_kit.py index 4dae7c242..fa2da65e6 100644 --- a/perun/check/detection_kit.py +++ b/perun/check/detection_kit.py @@ -19,7 +19,7 @@ # Perun Imports from perun.check.methods import linear_regression, polynomial_regression, fast_check from perun.postprocess.regression_analysis import regression_models -from perun.profile import query +from perun.profiles.native import query from perun.utils.common import common_kit from perun.utils.structs.common_structs import ( PerformanceChange, @@ -29,7 +29,7 @@ ) if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile SAMPLES: int = 1000 diff --git a/perun/check/factory.py b/perun/check/factory.py index 219a9404a..6468a3e73 100644 --- a/perun/check/factory.py +++ b/perun/check/factory.py @@ -36,12 +36,12 @@ ) from perun.utils.common import common_kit from perun.utils.exceptions import UnsupportedModuleException -import perun.profile.helpers as profiles +from perun.profiles.native import helpers as profil_helpers import perun.select.factory as select if TYPE_CHECKING: - from perun.profile.factory import Profile - from perun.profile.helpers import ProfileInfo + from perun.profiles.native import Profile + from perun.profiles.native.helpers import ProfileInfo # Minimal confidence rate from both models to perform the detection _MIN_CONFIDENCE_RATE = 0.15 @@ -71,7 +71,7 @@ def profiles_to_queue( :param minor_version: minor version for which we are retrieving the profile queue :returns: dictionary mapping configurations of profiles to the actual profiles """ - minor_version_profiles = profiles.load_list_for_minor_version(minor_version) + minor_version_profiles = profil_helpers.load_list_for_minor_version(minor_version) return {profile.config_tuple: profile for profile in minor_version_profiles} @@ -133,7 +133,7 @@ def degradation_in_minor( for target_config, target_profile_info in profile_queue.items(): # Iterate through the profiles and check degradation between those of same configuration target_prof = store.load_profile_from_file(target_profile_info.realpath, False, True) - cmdstr = profiles.config_tuple_to_cmdstr(target_config) + cmdstr = profil_helpers.config_tuple_to_cmdstr(target_config) for baseline_info, baseline_profile_info in selection.get_profiles( minor_version_info, target_prof @@ -262,8 +262,8 @@ def degradation_between_files( """ log.major_info("Checking two compatible profiles") # First check if the configurations are compatible - baseline_config = profiles.to_config_tuple(baseline_file) - target_config = profiles.to_config_tuple(target_file) + baseline_config = profil_helpers.to_config_tuple(baseline_file) + target_config = profil_helpers.to_config_tuple(target_file) target_minor_version = target_file.get("origin", minor_version) if not force: if baseline_config != target_config: @@ -276,7 +276,7 @@ def degradation_between_files( for deg in degradation_between_profiles(baseline_file, target_file, models_strategy): if deg.result != PerformanceChange.NoChange: detected_changes.append( - (deg, profiles.config_tuple_to_cmdstr(baseline_config), target_minor_version) + (deg, profil_helpers.config_tuple_to_cmdstr(baseline_config), target_minor_version) ) # Store the detected changes for given minor version diff --git a/perun/check/methods/abstract_base_checker.py b/perun/check/methods/abstract_base_checker.py index 9d81f4098..a51228c70 100755 --- a/perun/check/methods/abstract_base_checker.py +++ b/perun/check/methods/abstract_base_checker.py @@ -15,7 +15,7 @@ # Perun Imports if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile from perun.utils.structs.common_structs import DegradationInfo diff --git a/perun/check/methods/average_amount_threshold.py b/perun/check/methods/average_amount_threshold.py index 956e1098f..d47ab7b63 100644 --- a/perun/check/methods/average_amount_threshold.py +++ b/perun/check/methods/average_amount_threshold.py @@ -44,12 +44,12 @@ # Perun Imports from perun.check.methods.abstract_base_checker import AbstractBaseChecker -from perun.profile import convert +from perun.profiles.native import convert from perun.utils.common import common_kit from perun.utils.structs.common_structs import DegradationInfo, PerformanceChange if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile DEGRADATION_THRESHOLD = 2.0 OPTIMIZATION_THRESHOLD = 0.5 diff --git a/perun/check/methods/best_model_order_equality.py b/perun/check/methods/best_model_order_equality.py index d89eb6567..50ae2d2ae 100644 --- a/perun/check/methods/best_model_order_equality.py +++ b/perun/check/methods/best_model_order_equality.py @@ -53,7 +53,7 @@ from perun.utils.structs.common_structs import DegradationInfo, PerformanceChange if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile CONFIDENCE_THRESHOLD = 0.9 diff --git a/perun/check/methods/exclusive_time_outliers.py b/perun/check/methods/exclusive_time_outliers.py index 840daf5f7..3bc810b64 100644 --- a/perun/check/methods/exclusive_time_outliers.py +++ b/perun/check/methods/exclusive_time_outliers.py @@ -78,11 +78,11 @@ # Perun Imports from perun.check.methods.abstract_base_checker import AbstractBaseChecker from perun.logic import config -from perun.profile import convert +from perun.profiles.native import convert from perun.utils.structs.common_structs import DegradationInfo, PerformanceChange if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile OldLocMap = dict[str, str] NewLocMap = dict[str, str] diff --git a/perun/check/methods/fast_check.py b/perun/check/methods/fast_check.py index 08a2c6fcd..9f4693502 100644 --- a/perun/check/methods/fast_check.py +++ b/perun/check/methods/fast_check.py @@ -20,7 +20,7 @@ from perun.utils.structs.common_structs import DegradationInfo, ClassificationMethod if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile class FastCheck(AbstractBaseChecker): diff --git a/perun/check/methods/integral_comparison.py b/perun/check/methods/integral_comparison.py index da216267c..1dd1944c6 100644 --- a/perun/check/methods/integral_comparison.py +++ b/perun/check/methods/integral_comparison.py @@ -20,7 +20,7 @@ from perun.utils.structs.common_structs import DegradationInfo, ModelRecord, DetectionChangeResult if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile # acceptable value of relative error between compared profiles to detect NO_CHANGE state diff --git a/perun/check/methods/linear_regression.py b/perun/check/methods/linear_regression.py index d253decc1..ad00ee958 100644 --- a/perun/check/methods/linear_regression.py +++ b/perun/check/methods/linear_regression.py @@ -24,7 +24,7 @@ import numpy import numpy.typing as npt - from perun.profile.factory import Profile + from perun.profiles.native import Profile class LinearRegression(AbstractBaseChecker): diff --git a/perun/check/methods/local_statistics.py b/perun/check/methods/local_statistics.py index a67f3ed9d..b9f80694f 100644 --- a/perun/check/methods/local_statistics.py +++ b/perun/check/methods/local_statistics.py @@ -15,7 +15,7 @@ # Perun Imports from perun import check as check from perun.check.methods.abstract_base_checker import AbstractBaseChecker -from perun.profile.factory import Profile +from perun.profiles.native import Profile from perun.utils.common import common_kit from perun.utils.structs.common_structs import DegradationInfo, ModelRecord, DetectionChangeResult diff --git a/perun/check/methods/polynomial_regression.py b/perun/check/methods/polynomial_regression.py index 8cca36dde..1ff3074d0 100644 --- a/perun/check/methods/polynomial_regression.py +++ b/perun/check/methods/polynomial_regression.py @@ -20,7 +20,7 @@ if TYPE_CHECKING: import numpy.typing as npt - from perun.profile.factory import Profile + from perun.profiles.native import Profile THRESHOLD = 100000000 diff --git a/perun/check/nonparam_kit.py b/perun/check/nonparam_kit.py index fd64184dc..acb900b67 100644 --- a/perun/check/nonparam_kit.py +++ b/perun/check/nonparam_kit.py @@ -15,7 +15,7 @@ import perun.postprocess.regressogram.methods as rg_methods if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile def classify_change( diff --git a/perun/cli.py b/perun/cli.py index f8638e404..3667a5d3c 100644 --- a/perun/cli.py +++ b/perun/cli.py @@ -79,7 +79,7 @@ import perun.deltadebugging.factory as delta if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile DEV_MODE = False diff --git a/perun/cli_groups/check_cli.py b/perun/cli_groups/check_cli.py index 774df90b6..5b7a90d54 100644 --- a/perun/cli_groups/check_cli.py +++ b/perun/cli_groups/check_cli.py @@ -16,7 +16,7 @@ from perun.utils.structs import check_structs if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile @click.group("check") diff --git a/perun/cli_groups/collect_cli.py b/perun/cli_groups/collect_cli.py index eece1a0c9..5224d6ae3 100644 --- a/perun/cli_groups/collect_cli.py +++ b/perun/cli_groups/collect_cli.py @@ -7,7 +7,7 @@ # Perun Imports from perun.utils.structs import collect_structs from perun.utils.common import cli_kit -from perun import profile +from perun.profiles.native import helpers as profile_helpers from perun.logic import commands, config as perun_config @@ -208,7 +208,7 @@ def collect(ctx: click.Context, **kwargs: Any) -> None: ``perun run job --help``. """ commands.try_init() - kwargs["profile_path"] = profile.ProfilePath( + kwargs["profile_path"] = profile_helpers.ProfilePath( kwargs.get("profile_name"), kwargs.get("profile_dir") ) ctx.obj = kwargs diff --git a/perun/cli_groups/import_cli.py b/perun/cli_groups/import_cli.py index 8755dcb53..712a0affc 100755 --- a/perun/cli_groups/import_cli.py +++ b/perun/cli_groups/import_cli.py @@ -10,7 +10,7 @@ # Perun Imports from perun.logic import commands, config -from perun import profile as profile +from perun.profiles import native as profile from perun.utils.common import cli_kit diff --git a/perun/cli_groups/showdiff_cli.py b/perun/cli_groups/showdiff_cli.py index 037d7929d..ef0bf2608 100644 --- a/perun/cli_groups/showdiff_cli.py +++ b/perun/cli_groups/showdiff_cli.py @@ -27,7 +27,7 @@ ) if TYPE_CHECKING: - from perun import profile + from perun.profiles.native import Profile def perun_profile_list_options(command: Callable[..., Any]) -> Callable[..., Any]: @@ -244,7 +244,7 @@ def showdiff_group(**_: Any) -> None: type=click.STRING, help="Names the each profile by its particular option (default=origin).", ) -def short(profile_list: tuple[profile.Profile, profile.Profile], *_: Any, **kwargs: Any) -> None: +def short(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: """Creates a difference table of profiles in the terminal. Supports only perun-native profiles. @@ -274,9 +274,7 @@ def short(profile_list: tuple[profile.Profile, profile.Profile], *_: Any, **kwar ) @common_html_options @common_flamegraph_options -def flamegraph( - profile_list: tuple[profile.Profile, profile.Profile], *_: Any, **kwargs: Any -) -> None: +def flamegraph(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: """Creates a flame graph (or icicle graph) difference grid from perun-native profiles. The grid consists of baseline, target, baseline-target diff, and target-baseline diff flame @@ -437,7 +435,7 @@ def report_group(ctx: click.Context, **kwargs: Any) -> None: @click.pass_context def native( ctx: click.Context, - profile_list: tuple[profile.Profile, profile.Profile], + profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any, ) -> None: diff --git a/perun/collect/memory/parsing.py b/perun/collect/memory/parsing.py index f0002d917..2fb15c312 100644 --- a/perun/collect/memory/parsing.py +++ b/perun/collect/memory/parsing.py @@ -12,7 +12,7 @@ # Perun Imports from perun.collect.memory import syscalls -from perun.profile import convert +from perun.profiles.native import convert from perun.utils.common import common_kit if TYPE_CHECKING: diff --git a/perun/collect/trace/run.py b/perun/collect/trace/run.py index 57b996d92..6b904f739 100644 --- a/perun/collect/trace/run.py +++ b/perun/collect/trace/run.py @@ -24,7 +24,7 @@ import perun.logic.runner as runner import perun.utils.log as stdout import perun.utils.metrics as metrics -from perun.profile.factory import Profile +from perun.profiles.native import Profile from perun.utils.structs.common_structs import CollectStatus diff --git a/perun/collect/trace/systemtap/parse_compact.py b/perun/collect/trace/systemtap/parse_compact.py index b9250a347..ddbf0506c 100644 --- a/perun/collect/trace/systemtap/parse_compact.py +++ b/perun/collect/trace/systemtap/parse_compact.py @@ -11,7 +11,7 @@ import perun.utils.metrics as metrics import perun.collect.trace.optimizations.resources.manager as resources import perun.logic.stats as stats -from perun.profile.factory import Profile +from perun.profiles.native import Profile from perun.collect.trace.watchdog import WATCH_DOG import perun.collect.trace.values as vals diff --git a/perun/deltadebugging/factory.py b/perun/deltadebugging/factory.py index a107d5661..da1a1a313 100644 --- a/perun/deltadebugging/factory.py +++ b/perun/deltadebugging/factory.py @@ -18,7 +18,7 @@ from perun.fuzz.structs import Mutation if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile from perun.utils.structs.common_structs import Executable, MinorVersion, CollectStatus, Job diff --git a/perun/fuzz/evaluate/by_perun.py b/perun/fuzz/evaluate/by_perun.py index a7700fe92..eb2f275d2 100644 --- a/perun/fuzz/evaluate/by_perun.py +++ b/perun/fuzz/evaluate/by_perun.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: from perun.fuzz.structs import Mutation - from perun.profile.factory import Profile + from perun.profiles.native import Profile from perun.utils.structs.common_structs import Executable, MinorVersion, CollectStatus, Job diff --git a/perun/fuzz/factory.py b/perun/fuzz/factory.py index c75929136..a42c90383 100644 --- a/perun/fuzz/factory.py +++ b/perun/fuzz/factory.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: import types - from perun.profile.factory import Profile + from perun.profiles.native import Profile from perun.utils.structs.common_structs import Executable, MinorVersion, CollectStatus, Job # to ignore numpy division warnings diff --git a/perun/logic/commands.py b/perun/logic/commands.py index ab462957c..0c4240886 100644 --- a/perun/logic/commands.py +++ b/perun/logic/commands.py @@ -46,11 +46,11 @@ from perun.utils.structs.common_structs import ProfileListConfig, MinorVersion from perun.vcs import vcs_kit from perun.vcs.git_repository import GitRepository -import perun.profile.helpers as profile +import perun.profiles.native.helpers as profile if TYPE_CHECKING: - from perun.profile.helpers import ProfileInfo - from perun.profile.factory import Profile + from perun.profiles.native.helpers import ProfileInfo + from perun.profiles.native import Profile UNTRACKED_REGEX: re.Pattern[str] = re.compile( r"([^\\]+)-([0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}).perf" diff --git a/perun/logic/index.py b/perun/logic/index.py index 535541c1c..015aa0c18 100644 --- a/perun/logic/index.py +++ b/perun/logic/index.py @@ -28,7 +28,7 @@ ) if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile # List of current versions of format and magic constants diff --git a/perun/logic/runner.py b/perun/logic/runner.py index 71cfa64a1..5e31a1838 100644 --- a/perun/logic/runner.py +++ b/perun/logic/runner.py @@ -30,13 +30,13 @@ Unit, ) from perun.workload.singleton_generator import SingletonGenerator -from perun import profile +from perun.profiles import native as profile import perun.workload as workloads if TYPE_CHECKING: import types - from perun.profile.factory import Profile + from perun.profiles.native import Profile def construct_job_matrix( diff --git a/perun/logic/stats.py b/perun/logic/stats.py index f80780893..6fa04e8ad 100644 --- a/perun/logic/stats.py +++ b/perun/logic/stats.py @@ -49,7 +49,7 @@ # Perun Imports from perun.logic import index, pcs, store -from perun.profile import helpers +from perun.profiles.native import helpers from perun.utils import exceptions, log as perun_log from perun.utils.common import common_kit from perun.utils.exceptions import SuppressedExceptions diff --git a/perun/logic/store.py b/perun/logic/store.py index ede3e2af8..8ab7473a4 100644 --- a/perun/logic/store.py +++ b/perun/logic/store.py @@ -19,7 +19,7 @@ # Third-Party Imports # Perun Imports -from perun import profile as profile +from perun.profiles import native as profile from perun.utils import log from perun.utils.common import common_kit from perun.utils.exceptions import IncorrectProfileFormatException diff --git a/perun/meson.build b/perun/meson.build index 459cfb9d9..2c56ae86c 100644 --- a/perun/meson.build +++ b/perun/meson.build @@ -16,7 +16,7 @@ subdir('fuzz') subdir('deltadebugging') subdir('logic') subdir('postprocess') -subdir('profile') +subdir('profiles') subdir('select') subdir('templates') subdir('testing') diff --git a/perun/postprocess/kernel_regression/run.py b/perun/postprocess/kernel_regression/run.py index e64fec671..3c114b92e 100644 --- a/perun/postprocess/kernel_regression/run.py +++ b/perun/postprocess/kernel_regression/run.py @@ -18,7 +18,7 @@ from perun.utils.structs.common_structs import PostprocessStatus if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile # Supported types of regression estimator: diff --git a/perun/postprocess/moving_average/run.py b/perun/postprocess/moving_average/run.py index b8cfbae07..9362372e2 100644 --- a/perun/postprocess/moving_average/run.py +++ b/perun/postprocess/moving_average/run.py @@ -19,7 +19,7 @@ from perun.utils.structs.common_structs import PostprocessStatus if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile # set the labels at the center of the window as default diff --git a/perun/postprocess/regression_analysis/data_provider.py b/perun/postprocess/regression_analysis/data_provider.py index 40087b9a2..116008b95 100644 --- a/perun/postprocess/regression_analysis/data_provider.py +++ b/perun/postprocess/regression_analysis/data_provider.py @@ -9,10 +9,10 @@ # Third-Party Imports # Perun Imports -from perun.profile import convert +from perun.profiles.native import convert if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile def resource_sort_key(resource: dict[str, Any]) -> str: diff --git a/perun/postprocess/regression_analysis/run.py b/perun/postprocess/regression_analysis/run.py index d48948f79..faf838c21 100644 --- a/perun/postprocess/regression_analysis/run.py +++ b/perun/postprocess/regression_analysis/run.py @@ -3,7 +3,7 @@ from __future__ import annotations # Standard Imports -from typing import Any +from typing import Any, TYPE_CHECKING # Third-Party Imports import click @@ -11,11 +11,15 @@ # Perun Imports from perun.logic import runner from perun.postprocess.regression_analysis import data_provider, methods, tools -from perun.profile.factory import pass_profile, Profile +from perun.profiles import native as profile_factory from perun.utils import metrics from perun.utils.common import cli_kit from perun.utils.structs import common_structs, postprocess_structs +if TYPE_CHECKING: + from perun.profiles.native.factory import Profile + + _DEFAULT_STEPS = 3 @@ -133,7 +137,7 @@ def store_model_counts(analysis: list[dict[str, Any]]) -> None: callback=cli_kit.process_resource_key_param, help="Sets key for which we are finding the model.", ) -@pass_profile +@profile_factory.pass_profile def regression_analysis(profile: Profile, **kwargs: Any) -> None: """Finds fitting regression models to estimate models of profiled resources. diff --git a/perun/postprocess/regression_analysis/tools.py b/perun/postprocess/regression_analysis/tools.py index 03413256d..e9c39027e 100644 --- a/perun/postprocess/regression_analysis/tools.py +++ b/perun/postprocess/regression_analysis/tools.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: import numpy.typing as npt - import perun.profile.factory as profiles + import perun.profiles.native as profiles # Minimum points count to perform the regression diff --git a/perun/postprocess/regressogram/run.py b/perun/postprocess/regressogram/run.py index 8fbf21607..4bcb2ef32 100644 --- a/perun/postprocess/regressogram/run.py +++ b/perun/postprocess/regressogram/run.py @@ -5,7 +5,7 @@ from __future__ import annotations # Standard Imports -from typing import Any +from typing import Any, TYPE_CHECKING # Third-Party Imports import click @@ -14,10 +14,14 @@ from perun.logic import runner from perun.postprocess.regression_analysis import data_provider, tools from perun.postprocess.regressogram import methods -from perun.profile.factory import pass_profile, Profile +from perun.profiles import native as profile_factory from perun.utils.common import cli_kit from perun.utils.structs.common_structs import PostprocessStatus +if TYPE_CHECKING: + from perun.profiles.native.factory import Profile + + _DEFAULT_BUCKETS_METHOD = "doane" _DEFAULT_STATISTIC = "mean" @@ -78,7 +82,7 @@ def postprocess( ), ) @cli_kit.resources_key_options -@pass_profile +@profile_factory.pass_profile def regressogram(profile: Profile, **kwargs: Any) -> None: """ Execution of the interleaving of profiled resources by **regressogram** models. diff --git a/perun/profiles/__init__.py b/perun/profiles/__init__.py new file mode 100644 index 000000000..34e32f1eb --- /dev/null +++ b/perun/profiles/__init__.py @@ -0,0 +1,8 @@ +"""Collective package for profile. + +Contains subpackages for different profile types and their conversions. +""" + +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/perun/profiles/__init__.pyi b/perun/profiles/__init__.pyi new file mode 100644 index 000000000..0994acfd1 --- /dev/null +++ b/perun/profiles/__init__.pyi @@ -0,0 +1 @@ +from . import native as native diff --git a/perun/profiles/meson.build b/perun/profiles/meson.build new file mode 100644 index 000000000..228bdfdc7 --- /dev/null +++ b/perun/profiles/meson.build @@ -0,0 +1,13 @@ +perun_profiles_dir = perun_dir / 'profiles' + +perun_profiles_files = files( + '__init__.py', + '__init__.pyi', +) + +py3.install_sources( + perun_profiles_files, + subdir: perun_profiles_dir, +) + +subdir('native') diff --git a/perun/profile/__init__.py b/perun/profiles/native/__init__.py similarity index 87% rename from perun/profile/__init__.py rename to perun/profiles/native/__init__.py index e48b2db44..119cfc95b 100644 --- a/perun/profile/__init__.py +++ b/perun/profiles/native/__init__.py @@ -1,4 +1,4 @@ -"""Collective package for profile. +"""A package for Perun-native profile. Contains queries over profiles, storage and loading of the profile in the filesystem, transforming the profiles, and converting profiles to different formats. diff --git a/perun/profile/__init__.pyi b/perun/profiles/native/__init__.pyi similarity index 100% rename from perun/profile/__init__.pyi rename to perun/profiles/native/__init__.pyi diff --git a/perun/profile/convert.py b/perun/profiles/native/convert.py similarity index 98% rename from perun/profile/convert.py rename to perun/profiles/native/convert.py index 6cf946a0f..d47315be0 100644 --- a/perun/profile/convert.py +++ b/perun/profiles/native/convert.py @@ -6,7 +6,7 @@ Run the following in the Python interpreter to extend the capabilities of Python to different formats of profiles:: - import perun.profile.convert + import perun.profiles.convert Combined with ``perun.profile.factory``, ``perun.profile.query`` and e.g. `pandas`_ library one can obtain efficient interpreter for executing more @@ -26,12 +26,12 @@ # Perun Imports from perun.postprocess.regression_analysis import transform -from perun.profile import query +from perun.profiles.native import query from perun.utils import log from perun.utils.common import common_kit if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile def resources_to_pandas_dataframe(profile: Profile) -> pandas.DataFrame: @@ -126,7 +126,7 @@ def to_flame_graph_format( Example of format is as follows:: - >>> print(''.join(convert.to_flame_graph_format(memprof))) + >>> print(''.join(convert.to_folded_format(memprof))) malloc()~unreachable~0;main()~/home/user/dev/test.c~45 4 valloc()~unreachable~0;main()~/home/user/dev/test.c~75;__libc_start_main()~unreachable~0 8 main()~/home/user/dev/test02.c~79 156 diff --git a/perun/profile/factory.py b/perun/profiles/native/factory.py similarity index 99% rename from perun/profile/factory.py rename to perun/profiles/native/factory.py index 1dbf10c02..0895a94da 100644 --- a/perun/profile/factory.py +++ b/perun/profiles/native/factory.py @@ -21,7 +21,7 @@ # Perun Imports from perun import check as check from perun.logic import config -from perun.profile import convert, query, stats, helpers +from perun.profiles.native import query, helpers, convert, stats from perun.utils import log from perun.utils.structs import postprocess_structs from perun.utils.common import common_kit diff --git a/perun/profile/helpers.py b/perun/profiles/native/helpers.py similarity index 99% rename from perun/profile/helpers.py rename to perun/profiles/native/helpers.py index ac8ab6f7e..435708fe6 100644 --- a/perun/profile/helpers.py +++ b/perun/profiles/native/helpers.py @@ -31,7 +31,7 @@ # Perun Imports from perun.logic import commands, config, index, pcs, store -from perun import profile as profiles +from perun.profiles import native as profiles from perun.utils import log as perun_log, streams from perun.utils.common import common_kit from perun.utils.external import environment, commands as external_commands diff --git a/perun/profile/imports.py b/perun/profiles/native/imports.py similarity index 99% rename from perun/profile/imports.py rename to perun/profiles/native/imports.py index 85af3960b..4cd1702e3 100755 --- a/perun/profile/imports.py +++ b/perun/profiles/native/imports.py @@ -17,7 +17,7 @@ import polars as pl # Perun Imports -from perun import profile as profile +from perun.profiles import native as profile from perun.collect.kperf import parser from perun.logic import config, pcs from perun.utils import log, streams diff --git a/perun/profile/meson.build b/perun/profiles/native/meson.build similarity index 51% rename from perun/profile/meson.build rename to perun/profiles/native/meson.build index 505e51ac2..c84136a81 100644 --- a/perun/profile/meson.build +++ b/perun/profiles/native/meson.build @@ -1,6 +1,6 @@ -perun_profile_dir = perun_dir / 'profile' +perun_native_profile_dir = perun_profiles_dir / 'native' -perun_profile_files = files( +perun_native_profile_files = files( '__init__.py', '__init__.pyi', 'convert.py', @@ -12,6 +12,6 @@ perun_profile_files = files( ) py3.install_sources( - perun_profile_files, - subdir: perun_profile_dir + perun_native_profile_files, + subdir: perun_native_profile_dir ) \ No newline at end of file diff --git a/perun/profile/query.py b/perun/profiles/native/query.py similarity index 99% rename from perun/profile/query.py rename to perun/profiles/native/query.py index 9fa137510..41e4793f1 100644 --- a/perun/profile/query.py +++ b/perun/profiles/native/query.py @@ -6,7 +6,7 @@ Run the following in the Python interpreter to extend the capabilities of profile to query over profiles, iterate over resources or models, etc.:: - import perun.profile.query + import perun.profiles.query Combined with ``perun.profile.factory``, ``perun.profile.convert`` and e.g. `Pandas library`_ one can obtain efficient interpreter for executing more diff --git a/perun/profile/stats.py b/perun/profiles/native/stats.py similarity index 100% rename from perun/profile/stats.py rename to perun/profiles/native/stats.py diff --git a/perun/select/abstract_base_selection.py b/perun/select/abstract_base_selection.py index a9879526a..c4df887d3 100755 --- a/perun/select/abstract_base_selection.py +++ b/perun/select/abstract_base_selection.py @@ -22,8 +22,7 @@ if TYPE_CHECKING: from perun.utils.structs.common_structs import MinorVersion - from perun.profile.factory import Profile - from perun.profile.helpers import ProfileInfo + from perun.profiles.native import Profile, ProfileInfo class AbstractBaseSelection(abc.ABC): diff --git a/perun/select/whole_repository_selection.py b/perun/select/whole_repository_selection.py index 625cb9e42..28baacea5 100755 --- a/perun/select/whole_repository_selection.py +++ b/perun/select/whole_repository_selection.py @@ -7,18 +7,19 @@ """ # Standard Imports -from typing import Iterator +from typing import Iterator, TYPE_CHECKING # Third-Party Imports # Perun Imports from perun.logic import pcs -from perun.profile import helpers as profile_helpers -from perun.profile.factory import Profile -from perun.profile.helpers import ProfileInfo +from perun.profiles.native import helpers as profile_helpers from perun.select.abstract_base_selection import AbstractBaseSelection from perun.utils.structs.common_structs import MinorVersion +if TYPE_CHECKING: + from perun.profiles.native import Profile, ProfileInfo + class WholeRepositorySelection(AbstractBaseSelection): """Implementation of Whole Repository Selection""" diff --git a/perun/testing/utils.py b/perun/testing/utils.py index 1f1bfa5a4..1922097d6 100644 --- a/perun/testing/utils.py +++ b/perun/testing/utils.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: from perun.logic.index import BasicIndexEntry - from perun.profile.factory import Profile + from perun.profiles.native import Profile def load_profilename(prof_directory: str, prof_filename: str) -> str: diff --git a/perun/utils/common/cli_kit.py b/perun/utils/common/cli_kit.py index 9d2d803ec..f6826032d 100644 --- a/perun/utils/common/cli_kit.py +++ b/perun/utils/common/cli_kit.py @@ -25,7 +25,7 @@ # Perun Imports import perun -from perun import profile as profile +from perun.profiles import native as profile from perun.logic import commands, store, stats, config, pcs from perun.utils import exceptions, streams, timestamps, log, metrics from perun.utils.common import common_kit diff --git a/perun/utils/common/diff_kit.py b/perun/utils/common/diff_kit.py index 31b985228..4d06237e3 100755 --- a/perun/utils/common/diff_kit.py +++ b/perun/utils/common/diff_kit.py @@ -11,7 +11,7 @@ # Third-Party Imports # Perun Imports -from perun import profile as profile +from perun.profiles import native as profile from perun.logic import config from perun.utils import log from perun.utils.common import common_kit diff --git a/perun/utils/common/view_kit.py b/perun/utils/common/view_kit.py index 7f05774b6..8bfaac7ae 100644 --- a/perun/utils/common/view_kit.py +++ b/perun/utils/common/view_kit.py @@ -13,15 +13,15 @@ import holoviews as hv # Perun Imports -from perun.utils import decorators, log -import perun.profile.helpers as profiles +from perun.utils import decorators +import perun.profiles.native as profiles if TYPE_CHECKING: import pandas as pd from collections.abc import MutableMapping, Iterable from types import ModuleType - from perun.profile.factory import Profile + from perun.profiles.native import Profile GRAPH_LR_PADDING: int = 0 diff --git a/perun/utils/structs/diff_structs.py b/perun/utils/structs/diff_structs.py index c95cb7bfc..527dc226a 100644 --- a/perun/utils/structs/diff_structs.py +++ b/perun/utils/structs/diff_structs.py @@ -10,7 +10,7 @@ # Perun Imports if TYPE_CHECKING: - from perun import profile + from perun.profiles.native import ProfileStat DEFAULT_AGGREGATE_FUNC: str = "median" @@ -83,7 +83,7 @@ def __init__(self) -> None: self.max_seen_trace: int = 0 self.max_per_resource: dict[str, float] = defaultdict(float) self.minimize: bool = False - self.profile_stats: dict[str, list[profile.ProfileStat]] = { + self.profile_stats: dict[str, list[ProfileStat]] = { "baseline": [], "target": [], } diff --git a/perun/view/bars/factory.py b/perun/view/bars/factory.py index e289cbd3f..5ff297bce 100644 --- a/perun/view/bars/factory.py +++ b/perun/view/bars/factory.py @@ -9,11 +9,11 @@ import holoviews as hv # Perun Imports -from perun.profile import convert +from perun.profiles.native import convert from perun.utils.common import view_kit if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile def create_from_params( diff --git a/perun/view/bars/run.py b/perun/view/bars/run.py index 8a91851a6..1959e2e0a 100644 --- a/perun/view/bars/run.py +++ b/perun/view/bars/run.py @@ -10,7 +10,7 @@ # Perun Imports import perun.view.bars.factory as bars_factory -import perun.profile.factory as profile_factory +from perun.profiles import native as profile_factory from perun.utils import log from perun.utils.common import cli_kit, common_kit, view_kit from perun.utils.exceptions import InvalidParameterException diff --git a/perun/view/flamegraph/run.py b/perun/view/flamegraph/run.py index accf7d8a6..ef35f9e15 100644 --- a/perun/view/flamegraph/run.py +++ b/perun/view/flamegraph/run.py @@ -9,9 +9,9 @@ import click # Perun Imports -from perun.profile import convert +from perun.profiles.native import convert import perun.view.flamegraph.flamegraph as flame -import perun.profile.factory as profile_factory +from perun.profiles import native as profile_factory def save_flamegraph(profile: profile_factory.Profile, filename: str) -> None: diff --git a/perun/view/flow/factory.py b/perun/view/flow/factory.py index 07bc15fa7..1cb8f3c44 100644 --- a/perun/view/flow/factory.py +++ b/perun/view/flow/factory.py @@ -9,13 +9,13 @@ import holoviews as hv # Perun Imports -from perun.profile import convert +from perun.profiles.native import convert from perun.utils.common import view_kit if TYPE_CHECKING: import pandas as pd - from perun.profile.factory import Profile + from perun.profiles.native import Profile class IntTableLike(Protocol): diff --git a/perun/view/flow/run.py b/perun/view/flow/run.py index 03cac975d..14f8c0c61 100644 --- a/perun/view/flow/run.py +++ b/perun/view/flow/run.py @@ -12,7 +12,7 @@ from perun.utils import log from perun.utils.common import cli_kit, common_kit, view_kit from perun.utils.exceptions import InvalidParameterException -import perun.profile.factory as profile_factory +from perun.profiles import native as profile_factory import perun.view.flow.factory as flow_factory diff --git a/perun/view/scatter/factory.py b/perun/view/scatter/factory.py index 7052a6e12..901ba02d6 100644 --- a/perun/view/scatter/factory.py +++ b/perun/view/scatter/factory.py @@ -14,14 +14,14 @@ # Perun Imports from perun.postprocess.regression_analysis import data_provider -from perun.profile import query, convert +from perun.profiles.native import query, convert from perun.utils.common import view_kit if TYPE_CHECKING: import numpy.typing as npt import pandas as pd - from perun.profile.factory import Profile + from perun.profiles.native import Profile ProfileModel = dict[str, Any] diff --git a/perun/view/scatter/run.py b/perun/view/scatter/run.py index 818863f30..736050259 100644 --- a/perun/view/scatter/run.py +++ b/perun/view/scatter/run.py @@ -10,7 +10,7 @@ # Perun Imports from perun.utils.common import cli_kit, common_kit, view_kit -import perun.profile.factory as profile_factory +from perun.profiles import native as profile_factory import perun.view.scatter.factory as scatter_factory diff --git a/perun/view/tableof/run.py b/perun/view/tableof/run.py index 2a9e1040f..ccfb42b12 100644 --- a/perun/view/tableof/run.py +++ b/perun/view/tableof/run.py @@ -4,7 +4,7 @@ # Standard Imports from itertools import groupby -from typing import Callable, Any +from typing import Callable, Any, TYPE_CHECKING import operator import os @@ -15,8 +15,10 @@ # Perun Imports from perun.utils import log -from perun.profile import convert, query, helpers -from perun.profile.factory import Profile +from perun.profiles.native import query, helpers, convert + +if TYPE_CHECKING: + from perun.profiles.native import Profile def get_headers(ctx: click.Context) -> list[str]: diff --git a/perun/view_diff/flamegraph.py b/perun/view_diff/flamegraph.py index 505a056d5..92a31ac2a 100755 --- a/perun/view_diff/flamegraph.py +++ b/perun/view_diff/flamegraph.py @@ -18,7 +18,7 @@ # Perun Imports import perun -from perun import profile as profile +from perun.profiles import native as profile from perun.logic import config from perun.templates import factory as templates from perun.utils import log, mapping diff --git a/perun/view_diff/report_folded.py b/perun/view_diff/report_folded.py index dc939b1d8..f95c7b518 100644 --- a/perun/view_diff/report_folded.py +++ b/perun/view_diff/report_folded.py @@ -49,9 +49,11 @@ # Perun Imports import perun -from perun import profile # TODO: restructure so that we do not fetch pandas, numpy, etc. + +# TODO: restructure so that we do not fetch pandas, numpy, etc. +from perun.profiles import native as profile from perun.logic import config -from perun.profile import imports +from perun.profiles.native import imports from perun.templates import factory as templates from perun.utils import log, streams from perun.utils.common import common_kit, diff_kit, script_kit diff --git a/perun/view_diff/report_native.py b/perun/view_diff/report_native.py index e2804ea50..7ddb49276 100755 --- a/perun/view_diff/report_native.py +++ b/perun/view_diff/report_native.py @@ -23,7 +23,7 @@ # Perun Imports import perun -from perun import profile as profile +from perun.profiles import native as profile from perun.logic import config from perun.templates import factory as templates from perun.utils import log, mapping, streams diff --git a/perun/view_diff/short.py b/perun/view_diff/short.py index 9a47150e4..65c0ece92 100755 --- a/perun/view_diff/short.py +++ b/perun/view_diff/short.py @@ -24,7 +24,7 @@ import tabulate # Perun Imports -from perun import profile as profile +from perun.profiles import native as profile from perun.utils import log PRECISION: int = 2 @@ -81,7 +81,7 @@ def print_header(lhs_profile: profile.Profile, rhs_profile: profile.Profile) -> ["command", command], ["collector command", log.collector_to_command(lhs_profile.get("collector_info", {}))], ] - print(tabulate.tabulate(data)) # type: ignore + print(tabulate.tabulate(data)) def get_top_n_records( diff --git a/perun/workload/generator.py b/perun/workload/generator.py index dd0fdad02..2fa2bf637 100644 --- a/perun/workload/generator.py +++ b/perun/workload/generator.py @@ -15,14 +15,14 @@ # Third-Party Imports # Perun Imports -from perun import profile +from perun.profiles import native as profile from perun.logic import config from perun.utils import log from perun.utils.structs.common_structs import CollectStatus, Job, Unit from perun.utils.common import common_kit if TYPE_CHECKING: - from perun.profile.factory import Profile + from perun.profiles.native import Profile class WorkloadGenerator: diff --git a/tests-perf/test_performance.py b/tests-perf/test_performance.py index 5e86be1bf..c45debc53 100644 --- a/tests-perf/test_performance.py +++ b/tests-perf/test_performance.py @@ -14,7 +14,7 @@ import tabulate import perun.logic.store as store from perun.utils.common import common_kit -import perun.profile.convert as convert +from perun.profiles.native import convert import perun.utils.log as log import perun.utils.streams as streams diff --git a/tests/conftest.py b/tests/conftest.py index 8dc761843..b1ed6ae8c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,7 @@ from perun.view_diff import report_native if TYPE_CHECKING: - from perun import profile + from perun.profiles.native import Profile @pytest.fixture(scope="session", autouse=True) @@ -184,7 +184,7 @@ def memory_profiles(): def load_all_profiles_in( directory: str, prof_filter: Optional[Callable[[str], bool]] = None -) -> Iterable[tuple[str, profile.Profile]]: +) -> Iterable[tuple[str, Profile]]: """Generates stream of loaded (i.e. dictionaries) profiles in the specified directory. :param directory: the name (not path!) of the profile directory diff --git a/tests/test_collect.py b/tests/test_collect.py index d07025683..7efe21943 100644 --- a/tests/test_collect.py +++ b/tests/test_collect.py @@ -15,7 +15,7 @@ from perun.cli_groups import collect_cli from perun.collect.complexity import makefiles, symbols, run as complexity, configurator from perun.logic import pcs, runner as run -from perun.profile.factory import Profile +from perun.profiles.native import Profile from perun.testing import asserts, utils as test_utils from perun.utils import log from perun.utils.common import common_kit diff --git a/tests/test_log.py b/tests/test_log.py index 0f5c9573f..117c80a82 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -16,7 +16,7 @@ # Perun Imports from perun.logic import commands, config -from perun.profile.helpers import ProfileInfo +from perun.profiles.native import ProfileInfo from perun.utils import decorators from perun.utils.common import common_kit from perun.utils.exceptions import ( diff --git a/tests/test_profile.py b/tests/test_profile.py index 84c85240b..312defb75 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -10,8 +10,8 @@ # Perun Imports from perun.logic import commands, config -from perun.profile.factory import Profile -import perun.profile.helpers as profiles +from perun.profiles.native import Profile +import perun.profiles.native as profiles import perun.testing.utils as test_utils diff --git a/tests/test_profile_converters.py b/tests/test_profile_converters.py index 247aa245f..669412534 100644 --- a/tests/test_profile_converters.py +++ b/tests/test_profile_converters.py @@ -12,7 +12,7 @@ import pytest # Perun Imports -from perun.profile import convert +from perun.profiles.native import convert from perun.utils import exceptions import perun.testing.utils as test_utils diff --git a/tests/test_query.py b/tests/test_query.py index edc4d9206..ec245692d 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -11,7 +11,7 @@ import pytest # Perun Imports -from perun.profile import query, helpers +from perun.profiles.native import query, helpers import perun.testing.utils as test_utils # number of expected resource fields in memory profile diff --git a/tests/test_select.py b/tests/test_select.py index e338c4242..685dee954 100755 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -10,7 +10,7 @@ # Perun Imports from perun.logic import pcs -from perun.profile import helpers as profile_helpers +from perun.profiles.native import helpers as profile_helpers from perun.select.abstract_base_selection import AbstractBaseSelection from perun.select.whole_repository_selection import WholeRepositorySelection diff --git a/tests/test_utils.py b/tests/test_utils.py index 34f96b07d..73859d2ad 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -20,7 +20,7 @@ from perun.collect.trace.optimizations.structs import Complexity from perun.fuzz import filetype from perun.logic import commands, config, locks -from perun.profile import convert +from perun.profiles.native import convert from perun.testing import asserts from perun.utils import log, mapping from perun.utils.common import common_kit, cli_kit, traces_kit, view_kit From 57ac6dac8d91f1ce49cc3c0b91dfbc13e02e02b5 Mon Sep 17 00:00:00 2001 From: Jiri Pavela Date: Tue, 8 Sep 2026 16:51:29 +0200 Subject: [PATCH 2/4] Unify the diff reports. Both Perun-native and external profiles now use the same core logic for generating the HTML reports. --- perun/check/factory.py | 22 +- .../check/methods/average_amount_threshold.py | 5 +- .../check/methods/exclusive_time_outliers.py | 7 +- perun/cli_groups/import_cli.py | 9 +- perun/cli_groups/meson.build | 1 + perun/cli_groups/shared_options.py | 145 + perun/cli_groups/showdiff_cli.py | 201 +- perun/collect/kperf/meson.build | 1 - perun/collect/kperf/parser.py | 40 - perun/collect/kperf/run.py | 7 +- perun/collect/memory/parsing.py | 8 +- perun/meson.build | 2 +- .../regression_analysis/data_provider.py | 10 +- .../postprocess/regression_analysis/tools.py | 7 +- perun/profiles/__init__.pyi | 35 + perun/profiles/conversions/__init__.py | 8 + perun/profiles/conversions/__init__.pyi | 23 + perun/profiles/conversions/elk_native.py | 177 + perun/profiles/conversions/folded_polars.py | 101 + perun/profiles/conversions/meson.build | 18 + perun/profiles/conversions/native_folded.py | 79 + perun/profiles/conversions/native_pandas.py | 90 + perun/profiles/conversions/native_polars.py | 53 + perun/profiles/conversions/perf_native.py | 187 + perun/profiles/conversions/polars_folded.py | 55 + perun/profiles/folded/__init__.py | 5 + perun/profiles/folded/__init__.pyi | 6 + perun/profiles/folded/meson.build | 13 + perun/profiles/folded/parser.py | 56 + perun/profiles/folded/postprocess.py | 85 + perun/profiles/imports.py | 259 + perun/profiles/meson.build | 7 + perun/profiles/native/__init__.py | 4 +- perun/profiles/native/__init__.pyi | 39 +- perun/profiles/native/convert.py | 245 - perun/profiles/native/factory.py | 39 +- perun/profiles/native/helpers.py | 106 +- perun/profiles/native/imports.py | 621 -- perun/profiles/native/meson.build | 3 - perun/profiles/native/query.py | 10 +- perun/profiles/polars/__init__.py | 5 + perun/profiles/polars/__init__.pyi | 18 + perun/profiles/polars/convert.py | 315 + perun/profiles/polars/meson.build | 14 + perun/profiles/polars/parser.py | 241 + perun/profiles/polars/structs.py | 258 + perun/profiles/{native => }/stats.py | 83 +- perun/profiles/structs.py | 183 + perun/profiles/utils.py | 23 + perun/{select => selection}/__init__.py | 2 +- .../abstract_base_selection.py | 0 perun/{select => selection}/factory.py | 2 +- perun/{select => selection}/meson.build | 2 +- .../whole_repository_selection.py | 2 +- .../section_sankey_graph.html.jinja2 | 38 - .../diff_views/flamegraph.html.jinja2 | 46 +- perun/templates/diff_views/report.html.jinja2 | 3 - .../scripts/sankey_graph_js.html.jinja2 | 683 -- perun/templates/style/sankey_graph.css | 89 - perun/utils/common/cli_kit.py | 22 +- perun/utils/common/common_kit.py | 207 +- perun/utils/common/diff_kit.py | 196 +- perun/utils/meson.build | 1 + perun/utils/streams.py | 26 +- perun/utils/structs/diff_structs.py | 54 +- perun/utils/structs/meson.build | 1 + perun/utils/structs/view_structs.py | 196 + perun/utils/type_hints.py | 17 + perun/view/bars/factory.py | 4 +- perun/view/bars/run.py | 6 +- perun/view/flamegraph/__init__.py | 14 +- perun/view/flamegraph/__init__.pyi | 20 + perun/view/flamegraph/core.py | 175 + perun/view/flamegraph/flamegraph.py | 287 - perun/view/flamegraph/grid.py | 555 + perun/view/flamegraph/meson.build | 4 +- perun/view/flamegraph/run.py | 59 +- perun/view/flow/factory.py | 4 +- perun/view/flow/run.py | 6 +- perun/view/scatter/factory.py | 15 +- perun/view/tableof/run.py | 9 +- perun/view_diff/__init__.pyi | 10 +- perun/view_diff/chatbot.py | 35 + perun/view_diff/flamegraph.py | 421 +- perun/view_diff/meson.build | 5 +- perun/view_diff/report/__init__.py | 23 + perun/view_diff/report/__init__.pyi | 16 + perun/view_diff/report/core.py | 396 + perun/view_diff/report/diffs.py | 155 + perun/view_diff/report/folded.py | 197 + perun/view_diff/report/meson.build | 15 + perun/view_diff/report/native.py | 150 + perun/view_diff/report_folded.py | 2472 ----- perun/view_diff/report_native.py | 891 -- perun/view_diff/short.py | 24 +- tests-perf/test_performance.py | 4 +- tests/conftest.py | 3 - .../kperf-baseline-stats-metadata.perf | 8905 ++--------------- .../diff_profiles/kperf-baseline.perf | 377 +- .../kperf-target-stats-metadata.perf | 8905 ++--------------- .../profiles/diff_profiles/kperf-target.perf | 650 +- tests/test_diff_views.py | 15 +- tests/test_flame_graph.py | 17 +- tests/test_pcs.py | 4 +- tests/test_profile_converters.py | 20 +- tests/test_select.py | 6 +- tests/test_utils.py | 45 +- 107 files changed, 6954 insertions(+), 23481 deletions(-) create mode 100644 perun/cli_groups/shared_options.py delete mode 100755 perun/collect/kperf/parser.py create mode 100644 perun/profiles/conversions/__init__.py create mode 100644 perun/profiles/conversions/__init__.pyi create mode 100644 perun/profiles/conversions/elk_native.py create mode 100644 perun/profiles/conversions/folded_polars.py create mode 100644 perun/profiles/conversions/meson.build create mode 100644 perun/profiles/conversions/native_folded.py create mode 100644 perun/profiles/conversions/native_pandas.py create mode 100644 perun/profiles/conversions/native_polars.py create mode 100644 perun/profiles/conversions/perf_native.py create mode 100644 perun/profiles/conversions/polars_folded.py create mode 100644 perun/profiles/folded/__init__.py create mode 100644 perun/profiles/folded/__init__.pyi create mode 100644 perun/profiles/folded/meson.build create mode 100755 perun/profiles/folded/parser.py create mode 100644 perun/profiles/folded/postprocess.py create mode 100644 perun/profiles/imports.py delete mode 100644 perun/profiles/native/convert.py delete mode 100755 perun/profiles/native/imports.py create mode 100644 perun/profiles/polars/__init__.py create mode 100644 perun/profiles/polars/__init__.pyi create mode 100644 perun/profiles/polars/convert.py create mode 100644 perun/profiles/polars/meson.build create mode 100644 perun/profiles/polars/parser.py create mode 100644 perun/profiles/polars/structs.py rename perun/profiles/{native => }/stats.py (86%) create mode 100644 perun/profiles/structs.py create mode 100644 perun/profiles/utils.py rename perun/{select => selection}/__init__.py (77%) rename perun/{select => selection}/abstract_base_selection.py (100%) rename perun/{select => selection}/factory.py (92%) rename perun/{select => selection}/meson.build (83%) rename perun/{select => selection}/whole_repository_selection.py (98%) delete mode 100644 perun/templates/diff_sections/section_sankey_graph.html.jinja2 delete mode 100644 perun/templates/scripts/sankey_graph_js.html.jinja2 delete mode 100644 perun/templates/style/sankey_graph.css create mode 100644 perun/utils/structs/view_structs.py create mode 100644 perun/utils/type_hints.py create mode 100644 perun/view/flamegraph/__init__.pyi create mode 100644 perun/view/flamegraph/core.py delete mode 100644 perun/view/flamegraph/flamegraph.py create mode 100644 perun/view/flamegraph/grid.py create mode 100644 perun/view_diff/chatbot.py mode change 100755 => 100644 perun/view_diff/flamegraph.py create mode 100644 perun/view_diff/report/__init__.py create mode 100644 perun/view_diff/report/__init__.pyi create mode 100644 perun/view_diff/report/core.py create mode 100644 perun/view_diff/report/diffs.py create mode 100644 perun/view_diff/report/folded.py create mode 100755 perun/view_diff/report/meson.build create mode 100644 perun/view_diff/report/native.py delete mode 100644 perun/view_diff/report_folded.py delete mode 100755 perun/view_diff/report_native.py diff --git a/perun/check/factory.py b/perun/check/factory.py index 6468a3e73..b5d6539cd 100644 --- a/perun/check/factory.py +++ b/perun/check/factory.py @@ -14,8 +14,6 @@ # Third-Party Imports # Perun Imports -from perun.logic import config, pcs, runner, store -from perun.select.abstract_base_selection import AbstractBaseSelection from perun.check.methods import ( average_amount_threshold, best_model_order_equality, @@ -26,7 +24,13 @@ local_statistics, polynomial_regression, ) +from perun.logic import config, pcs, runner, store +from perun.profiles.native import helpers as profile_helpers +from perun.selection.abstract_base_selection import AbstractBaseSelection +import perun.selection.factory as select from perun.utils import decorators, log +from perun.utils.common import common_kit +from perun.utils.exceptions import UnsupportedModuleException from perun.utils.structs.common_structs import ( DetectionChangeResult, DegradationInfo, @@ -34,10 +38,6 @@ MinorVersion, ModelRecord, ) -from perun.utils.common import common_kit -from perun.utils.exceptions import UnsupportedModuleException -from perun.profiles.native import helpers as profil_helpers -import perun.select.factory as select if TYPE_CHECKING: from perun.profiles.native import Profile @@ -71,7 +71,7 @@ def profiles_to_queue( :param minor_version: minor version for which we are retrieving the profile queue :returns: dictionary mapping configurations of profiles to the actual profiles """ - minor_version_profiles = profil_helpers.load_list_for_minor_version(minor_version) + minor_version_profiles = profile_helpers.load_list_for_minor_version(minor_version) return {profile.config_tuple: profile for profile in minor_version_profiles} @@ -133,7 +133,7 @@ def degradation_in_minor( for target_config, target_profile_info in profile_queue.items(): # Iterate through the profiles and check degradation between those of same configuration target_prof = store.load_profile_from_file(target_profile_info.realpath, False, True) - cmdstr = profil_helpers.config_tuple_to_cmdstr(target_config) + cmdstr = profile_helpers.config_tuple_to_cmdstr(target_config) for baseline_info, baseline_profile_info in selection.get_profiles( minor_version_info, target_prof @@ -262,8 +262,8 @@ def degradation_between_files( """ log.major_info("Checking two compatible profiles") # First check if the configurations are compatible - baseline_config = profil_helpers.to_config_tuple(baseline_file) - target_config = profil_helpers.to_config_tuple(target_file) + baseline_config = profile_helpers.to_config_tuple(baseline_file) + target_config = profile_helpers.to_config_tuple(target_file) target_minor_version = target_file.get("origin", minor_version) if not force: if baseline_config != target_config: @@ -276,7 +276,7 @@ def degradation_between_files( for deg in degradation_between_profiles(baseline_file, target_file, models_strategy): if deg.result != PerformanceChange.NoChange: detected_changes.append( - (deg, profil_helpers.config_tuple_to_cmdstr(baseline_config), target_minor_version) + (deg, profile_helpers.config_tuple_to_cmdstr(baseline_config), target_minor_version) ) # Store the detected changes for given minor version diff --git a/perun/check/methods/average_amount_threshold.py b/perun/check/methods/average_amount_threshold.py index d47ab7b63..40960e3aa 100644 --- a/perun/check/methods/average_amount_threshold.py +++ b/perun/check/methods/average_amount_threshold.py @@ -44,13 +44,14 @@ # Perun Imports from perun.check.methods.abstract_base_checker import AbstractBaseChecker -from perun.profiles.native import convert +from perun.profiles.conversions import native_pandas from perun.utils.common import common_kit from perun.utils.structs.common_structs import DegradationInfo, PerformanceChange if TYPE_CHECKING: from perun.profiles.native import Profile + DEGRADATION_THRESHOLD = 2.0 OPTIMIZATION_THRESHOLD = 0.5 @@ -61,7 +62,7 @@ def get_averages(profile: Profile) -> dict[str, float]: :param profile: dictionary representation of profile :returns: dictionary with averages for all uids """ - data_frame = convert.resources_to_pandas_dataframe(profile) + data_frame = native_pandas.resources_to_pandas_dataframe(profile) # Short fix for non-measured (static) profiles if "amount" not in data_frame: data_frame["amount"] = 0 diff --git a/perun/check/methods/exclusive_time_outliers.py b/perun/check/methods/exclusive_time_outliers.py index 3bc810b64..1f829ee4b 100644 --- a/perun/check/methods/exclusive_time_outliers.py +++ b/perun/check/methods/exclusive_time_outliers.py @@ -67,7 +67,7 @@ from __future__ import annotations # Standard Imports -from typing import Optional, Iterable, Any, TYPE_CHECKING +from typing import Any, Iterable, Optional, TYPE_CHECKING # Third-Party Imports import difflib @@ -78,12 +78,13 @@ # Perun Imports from perun.check.methods.abstract_base_checker import AbstractBaseChecker from perun.logic import config -from perun.profiles.native import convert +from perun.profiles.conversions import native_pandas from perun.utils.structs.common_structs import DegradationInfo, PerformanceChange if TYPE_CHECKING: from perun.profiles.native import Profile + OldLocMap = dict[str, str] NewLocMap = dict[str, str] @@ -366,7 +367,7 @@ def _prepare_profile(self, profile: Profile) -> pd.DataFrame: # Obtain "Uid (function name), exclusive time, location" DataFrame # and sum the exclusive times of individual functions df = ( - convert.resources_to_pandas_dataframe(profile)[columns] + native_pandas.resources_to_pandas_dataframe(profile)[columns] .groupby(["uid", "location"]) .sum() .reset_index() diff --git a/perun/cli_groups/import_cli.py b/perun/cli_groups/import_cli.py index 712a0affc..c2d4c7f97 100755 --- a/perun/cli_groups/import_cli.py +++ b/perun/cli_groups/import_cli.py @@ -11,6 +11,7 @@ # Perun Imports from perun.logic import commands, config from perun.profiles import native as profile +from perun.profiles.conversions import perf_native, elk_native from perun.utils.common import cli_kit @@ -186,7 +187,7 @@ def from_binary(ctx: click.Context, import_entries: list[str], **kwargs: Any) -> that combines the --stats-headers option and profile entries. """ kwargs.update(ctx.obj) - profile.import_perf_from_record(import_entries, **kwargs) + perf_native.import_perf_from_record(import_entries, **kwargs) @perf_group.command("script") @@ -213,7 +214,7 @@ def from_text(ctx: click.Context, import_entries: list[str], **kwargs: Any) -> N that combines the --stats-headers option and profile entries. """ kwargs.update(ctx.obj) - profile.import_perf_from_script(import_entries, **kwargs) + perf_native.import_perf_from_script(import_entries, **kwargs) @perf_group.command("stack") @@ -241,7 +242,7 @@ def from_stacks(ctx: click.Context, import_entries: list[str], **kwargs: Any) -> that combines the --stats-headers option and profile entries. """ kwargs.update(ctx.obj) - profile.import_perf_from_stack(import_entries, **kwargs) + perf_native.import_perf_from_stack(import_entries, **kwargs) @import_group.group("elk") @@ -271,4 +272,4 @@ def from_json(ctx: click.Context, import_entries: list[str], **kwargs: Any) -> N Each import entry may specify a JSON path 'file_path.json'. """ kwargs.update(ctx.obj) - profile.import_elk_from_json(import_entries, **kwargs) + elk_native.import_elk_from_json(import_entries, **kwargs) diff --git a/perun/cli_groups/meson.build b/perun/cli_groups/meson.build index aeeb8649f..de396c8e3 100644 --- a/perun/cli_groups/meson.build +++ b/perun/cli_groups/meson.build @@ -7,6 +7,7 @@ perun_cli_groups_files = files( 'config_cli.py', 'import_cli.py', 'run_cli.py', + 'shared_options.py', 'showdiff_cli.py', 'utils_cli.py', ) diff --git a/perun/cli_groups/shared_options.py b/perun/cli_groups/shared_options.py new file mode 100644 index 000000000..b335e98c8 --- /dev/null +++ b/perun/cli_groups/shared_options.py @@ -0,0 +1,145 @@ +"""Groups of options used by multiple CLI commands across different modules.""" + +from __future__ import annotations + +# Standard Imports +import functools +from typing import Any, Callable + +# Third-Party Imports +import click + +# Perun Imports +from perun.logic import config +from perun.utils.common import cli_kit, common_kit +from perun.utils.structs.view_structs import DEFAULT_SQUASH_RE, FlameGraphSettings + + +def flamegraph_cli_options(command: Callable[..., Any]) -> Callable[..., Any]: + """A set of common options for customizing generated flame graphs. + + :param command: a click command to extend with the options. + :return: the click command augmented with the flame graph options. + """ + + @click.option( + "--flamegraph-width", + type=int, + default=FlameGraphSettings.DefaultImageWidth, + help="Specifies the width of the flamegraph images in pixels. This option is forwarded to " + "the flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-height", + type=int, + help="Specifies the height of each flamegraph frame in pixels. This option is forwarded to " + "the flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-minwidth", + type=str, + default=FlameGraphSettings.DefaultMinWidth, + help="Filter out fast functions in flamegraphs. May be specified either in pixels (integer " + "or float value) or as a percentage of time if suffixed with '%'. This option is " + "forwarded to the flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-fonttype", + type=str, + help="Specifies the font type to use in flamegraphs. This option is forwarded to the " + "flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-fontsize", + type=int, + help="Specifies the font size of text in flamegraphs. This option is forwarded to the " + "flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-bgcolors", + type=str, + help="Specifies the background colors for flamegraphs. This option is forwarded to the " + "flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-colors", + type=str, + help="Specifies the color theme for flamegraphs. This option is forwarded to the " + "flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-inverted", + is_flag=True, + default=False, + help="Draws icicle graphs instead of flame graphs. This option is forwarded to the " + "flamegraph.pl/.py script.", + ) + @click.option( + "--flamegraph-use-perl-scripts", + is_flag=True, + default=False, + help="Use the canonical Perl scripts for generating flame graphs. Otherwise, our custom " + "Python scripts will be used.", + ) + @functools.wraps(command) + def wrapper_flamegraph_options(*args, **kwargs): + return command(*args, **kwargs) + + return wrapper_flamegraph_options + + +def profile_postprocess_cli_options(command: Callable[..., Any]) -> Callable[..., Any]: + """A set of common options for postprocessing Perun/folded profiles for visualisations. + + :param command: a click command to extend with the options. + :return: the click command augmented with the postprocessing options. + """ + + @click.option( + "--squash/--no-squash", + is_flag=True, + default=True, + help="Enables or disables squashing recursive function calls into a single stack frame " + "(default=True)", + ) + @click.option( + "--squash-regex", + type=str, + default=DEFAULT_SQUASH_RE, + help="A regex specifying function names to squash if squashing is enabled " + f"(default={DEFAULT_SQUASH_RE})", + ) + @click.option( + "--hide-generics", + is_flag=True, + default=False, + help="Hide generic types, e.g., template specifications, in function names (default=False).", + ) + @functools.wraps(command) + def wrapper_folded_postprocess_options(*args, **kwargs): + return command(*args, **kwargs) + + return wrapper_folded_postprocess_options + + +def profile_aggregation_cli_option(command: Callable[..., Any]) -> Callable[..., Any]: + """An aggregation option for input profiles. + + :param command: a click command to extend with the aggregation option. + :return: the click command augmented with the aggregation option. + """ + + @click.option( + "--aggregate-by", + "-a", + default=common_kit.Aggregations.default_name(), + type=click.Choice(common_kit.Aggregations.supported()), + callback=cli_kit.set_config_option_from_flag(config.runtime, "profile.aggregation"), + help=f"Aggregates the resources in profiles by given statistical function " + f"(default={common_kit.Aggregations.default_name()}).", + ) + @functools.wraps(command) + def wrapper_aggregation_option(*args, **kwargs): + return command(*args, **kwargs) + + return wrapper_aggregation_option diff --git a/perun/cli_groups/showdiff_cli.py b/perun/cli_groups/showdiff_cli.py index ef0bf2608..c771dcfe8 100644 --- a/perun/cli_groups/showdiff_cli.py +++ b/perun/cli_groups/showdiff_cli.py @@ -11,20 +11,18 @@ import click # Perun Imports +from perun.cli_groups import shared_options from perun.logic import config from perun.utils.common import cli_kit from perun.utils.structs.diff_structs import ( - HeaderDisplayStyle, - Config, - DEFAULT_AGGREGATE_FUNC, - FG_DEFAULT_IMAGE_WIDTH, - FG_DEFAULT_MIN_WIDTH, + DEFAULT_FUNCTION_THRESHOLD, DEFAULT_MAX_FUNCTION_TRACES, DEFAULT_TOP_DIFFS, - DEFAULT_FUNCTION_THRESHOLD, DEFAULT_TRACE_THRESHOLD, - DEFAULT_SQUASH_RE, + HeaderDisplayStyle, ) +from perun.view_diff import flamegraph, short +from perun.view_diff.report import native, folded if TYPE_CHECKING: from perun.profiles.native import Profile @@ -80,14 +78,12 @@ def common_html_options(command: Callable[..., Any]) -> Callable[..., Any]: default=False, help="Creates a self-contained output usable in offline environments (default=False).", ) - # TODO: Add support for color theme in flamegraph diff as well. @click.option( "--default-theme", "-th", type=click.Choice(["light", "dark", "mono"], case_sensitive=False), help="Determines which theme will be set as the default theme.", ) - # TODO: add support for a chatbot in flamegraph diff as well. @click.option( "--chatbot-url", "-c", @@ -111,66 +107,13 @@ def wrapper_common_flamegraph_options(*args, **kwargs): return wrapper_common_flamegraph_options -def common_flamegraph_options(command: Callable[..., Any]) -> Callable[..., Any]: - """A set of common options for customizing generated flame graphs. +def flamegraph_diff_cli_options(command: Callable[..., Any]) -> Callable[..., Any]: + """A set of options for generating diff flame graphs. :param command: a click command to extend with the options. :return: the click command augmented with the flame graph options. """ - @click.option( - "--flamegraph-width", - type=int, - default=FG_DEFAULT_IMAGE_WIDTH, - help="Specifies the width of the flamegraph images in pixels. This option is forwarded to " - "the flamegraph.pl script.", - ) - @click.option( - "--flamegraph-height", - type=int, - help="Specifies the height of each flamegraph frame in pixels. This option is forwarded to " - "the flamegraph.pl script.", - ) - # TODO: remove the default - @click.option( - "--flamegraph-minwidth", - type=str, - default=FG_DEFAULT_MIN_WIDTH, - help="Filter out fast functions in flamegraphs. May be specified either in pixels (integer " - "or float value) or as a percentage of time if suffixed with '%'. This option is " - "forwarded to the flamegraph.pl script.", - ) - @click.option( - "--flamegraph-fonttype", - type=str, - help="Specifies the font type to use in flamegraphs. This option is forwarded to the " - "flamegraph.pl script.", - ) - @click.option( - "--flamegraph-fontsize", - type=int, - help="Specifies the font size of text in flamegraphs. This option is forwarded to the " - "flamegraph.pl script.", - ) - @click.option( - "--flamegraph-bgcolors", - type=str, - help="Specifies the background colors for flamegraphs. This option is forwarded to the " - "flamegraph.pl script.", - ) - @click.option( - "--flamegraph-colors", - type=str, - help="Specifies the color theme for flamegraphs. This option is forwarded to the " - "flamegraph.pl script.", - ) - @click.option( - "--flamegraph-inverted", - is_flag=True, - default=False, - help="Draws icicle graphs instead of flame graphs. This option is forwarded to the " - "flamegraph.pl script.", - ) @click.option( "--flamegraph-normalize/--flamegraph-no-normalize", is_flag=True, @@ -178,7 +121,7 @@ def common_flamegraph_options(command: Callable[..., Any]) -> Callable[..., Any] help="Normalize the baseline sample counts when creating differential flame graphs using " "the formula '(baseline_count * target_sum / baseline_sum)'. This colors the flame graph " "frames with hues that respect the change in the total consumptions between two profiles. " - "This option is forwarded to the difffolded.pl script.", + "This option is forwarded to the difffolded.pl/.py script.", ) @click.option( "--flamegraph-parallelize/--flamegraph-no-parallelize", @@ -187,29 +130,15 @@ def common_flamegraph_options(command: Callable[..., Any]) -> Callable[..., Any] help="Generate flamegraph grids using multiple processes. Note that this may consume too" "much peak memory for very large perf profiles.", ) - @click.option( - "--flamegraph-use-perl-scripts", - is_flag=True, - default=False, - help="Use the canonical Perl scripts for generating flame graphs. Otherwise, our custom " - "Python scripts will be used.", - ) @functools.wraps(command) - def wrapper_common_flamegraph_options(*args, **kwargs): + def wrapper_flamegraph_diff_options(*args, **kwargs): return command(*args, **kwargs) - return wrapper_common_flamegraph_options + return wrapper_flamegraph_diff_options @click.group("showdiff") -@click.option( - "--aggregate-by", - "-a", - default=DEFAULT_AGGREGATE_FUNC, - type=click.Choice(["sum", "min", "max", "avg", "mean", "med", "median"]), - callback=cli_kit.set_config_option_from_flag(config.runtime, "profile.aggregation"), - help="Aggregates the resources in profiles by given statistical function (default=median).", -) +@shared_options.profile_aggregation_cli_option def showdiff_group(**_: Any) -> None: """Interprets the difference of baseline and target profiles. @@ -224,7 +153,7 @@ def showdiff_group(**_: Any) -> None: """ -@showdiff_group.command() +@showdiff_group.command("short") @perun_profile_list_options @click.option( "-n", "--top-n", type=click.INT, help="Prints top [INT] records (default=10).", default=10 @@ -244,37 +173,21 @@ def showdiff_group(**_: Any) -> None: type=click.STRING, help="Names the each profile by its particular option (default=origin).", ) -def short(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: +def short_diff(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: """Creates a difference table of profiles in the terminal. Supports only perun-native profiles. """ - # Lazy load the view_diff module and execute the command - from perun import view_diff - - view_diff.compare_profiles(*profile_list, **kwargs) + short.compare_profiles(*profile_list, **kwargs) -# TODO: split into 'native' and 'folded' similarly to report -@showdiff_group.command() +@showdiff_group.command("flamegraph") @perun_profile_list_options -# TODO: unify with 'hide-generics' -@click.option( - "--minimize", - "-m", - is_flag=True, - help="Minimizes the traces, folds the recursive calls, hides the generic types.", -) -# TODO: unify with 'squash' -@click.option( - "--no-squash-unknown", - is_flag=True, - default=False, - help="Do not squash [unknown] frames in flamegraph into a single frame (default=False).", -) @common_html_options -@common_flamegraph_options -def flamegraph(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: +@shared_options.flamegraph_cli_options +@flamegraph_diff_cli_options +@shared_options.profile_postprocess_cli_options +def flamegraph_diff(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> None: """Creates a flame graph (or icicle graph) difference grid from perun-native profiles. The grid consists of baseline, target, baseline-target diff, and target-baseline diff flame @@ -308,48 +221,10 @@ def flamegraph(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> Supports only perun-native profiles. """ - # Lazy load the view_diff module and execute the command - from perun import view_diff + flamegraph.generate_flamegraph_difference(*profile_list, **kwargs) - view_diff.generate_flamegraph_difference(*profile_list, **kwargs) - -# TODO: we still keep most of the old report options until we refactor 'report native' and can -# merge the old options with the new ones. @showdiff_group.group("report") -# TODO: replace with new filtering parameters. -@click.option( - "--filter-by-relative", - "-fr", - nargs=1, - type=click.FLOAT, - default=Config().DefaultRelativeThreshold, - help="Filters records based on the relative increase wrt the target. It filters values that " - f"are lesser or equal than [FLOAT] (default={Config().DefaultRelativeThreshold}).", -) -# TODO: replace with 'function-threshold' -@click.option( - "--top-n", - "-tn", - nargs=1, - type=click.INT, - default=Config().DefaultTopN, - help=f"Filters how many top traces will be recorded per uid (default={Config().DefaultTopN}). ", -) -# TODO: replace with 'hide-generics' -@click.option( - "--minimize", - "-m", - is_flag=True, - help="Minimizes the traces, folds the recursive calls, hides the generic types.", -) -# TODO: replace with new squash parameters -@click.option( - "--no-squash-unknown", - is_flag=True, - default=False, - help="Do not squash [unknown] frames in flamegraph into a single frame (default=False).", -) @click.option( "--function-threshold", "-ft", @@ -380,26 +255,6 @@ def flamegraph(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> help=f"Limit the number of overall most expensive traces and functions displayed in the " f"Overview (default={DEFAULT_TOP_DIFFS}).", ) -@click.option( - "--squash/--no-squash", - is_flag=True, - default=True, - help="Enables or disables squashing recursive function calls into a single flamegraph frame " - "(default=True)", -) -@click.option( - "--squash-regex", - type=str, - default=DEFAULT_SQUASH_RE, - help="A regex specifying function names to squash if squashing is enabled " - f"(default={DEFAULT_SQUASH_RE})", -) -@click.option( - "--hide-generics", - is_flag=True, - default=False, - help="Hide generic types, e.g., template specifications, in function names (default=False).", -) @click.option( "--display-style", "-d", @@ -418,7 +273,9 @@ def flamegraph(profile_list: tuple[Profile, Profile], *_: Any, **kwargs: Any) -> help="Attaches the URL address and its display name to the links section in the report.", ) @common_html_options -@common_flamegraph_options +@shared_options.flamegraph_cli_options +@flamegraph_diff_cli_options +@shared_options.profile_postprocess_cli_options @click.pass_context def report_group(ctx: click.Context, **kwargs: Any) -> None: """Creates a comprehensive interactive difference report of two profiles. @@ -433,7 +290,7 @@ def report_group(ctx: click.Context, **kwargs: Any) -> None: @report_group.command("native") @perun_profile_list_options @click.pass_context -def native( +def report_native( ctx: click.Context, profile_list: tuple[Profile, Profile], *_: Any, @@ -469,11 +326,8 @@ def native( perun showdiff report native -m HEAD~1 0@i 1@i """ - # Lazy load the view_diff module and execute the command - from perun import view_diff - kwargs.update(ctx.obj) - view_diff.generate_report_from_native(*profile_list, **kwargs) + native.generate_report_from_native_profiles(*profile_list, **kwargs) @report_group.command("folded") @@ -652,8 +506,5 @@ def report_folded(ctx: click.Context, baseline: str, target: str, **kwargs: Any) - 'description' is shown as a tooltip for the stat (default=comparison_type). """ - # Lazy load the view_diff module and execute the command - from perun import view_diff - kwargs.update(ctx.obj) - view_diff.generate_report_from_folded(baseline, target, **kwargs) + folded.generate_report_from_folded_profiles(baseline, target, **kwargs) diff --git a/perun/collect/kperf/meson.build b/perun/collect/kperf/meson.build index 507cd4556..618d053d3 100755 --- a/perun/collect/kperf/meson.build +++ b/perun/collect/kperf/meson.build @@ -2,7 +2,6 @@ perun_collect_kperf_dir = perun_collect_dir / 'kperf' perun_collect_kperf_files = files( '__init__.py', - 'parser.py', 'run.py', ) diff --git a/perun/collect/kperf/parser.py b/perun/collect/kperf/parser.py deleted file mode 100755 index 5438c6fb6..000000000 --- a/perun/collect/kperf/parser.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Contains helper functions for parsing information from perf -""" - -from __future__ import annotations - -# Standard Imports -from typing import Any - -# Third-Party Imports - -# Perun Imports -from perun.utils import log - - -def parse_events(perf_events: list[str]) -> list[dict[str, Any]]: - """Parses perf events into a list of resources - - Each resource is identified by its topmost called function (uid), - and contains traces and unit (the bottom function). For each - function we count the number of samples. - - :param perf_events: string with perf events - :return: list of resources - """ - resources = [] - for event in log.progress(perf_events, description="Parsing Events"): - if event.strip(): - *record, samples = event.split(" ") - parts = " ".join(record).split(";") - command, trace, uid = parts[0], parts[0:-1], parts[-1] - resources.append( - { - "amount": int(samples), - "uid": uid, - "command": command, - "trace": [{"func": f} for f in trace], - } - ) - return resources diff --git a/perun/collect/kperf/run.py b/perun/collect/kperf/run.py index f608597e1..3d52e32a7 100755 --- a/perun/collect/kperf/run.py +++ b/perun/collect/kperf/run.py @@ -12,11 +12,11 @@ import click # Perun Imports -from perun.collect.kperf import parser from perun.logic import runner +from perun.profiles.folded import parser from perun.utils import log from perun.utils.common import script_kit -from perun.utils.structs.common_structs import Executable, CollectStatus +from perun.utils.structs.common_structs import CollectStatus, Executable from perun.utils.external import commands from perun.utils.exceptions import SuppressedExceptions @@ -108,7 +108,8 @@ def collect(executable: Executable, **kwargs: Any) -> tuple[CollectStatus, str, def after(**kwargs: Any) -> tuple[CollectStatus, str, dict[str, Any]]: """Parses the raw data into performance profile""" log.major_info("Creating performance profile") - resources = parser.parse_events(kwargs["raw_data"]) + resources: list[dict[str, str | int]] = [] + parser.parse_resources_from_stream(kwargs["raw_data"], resources) if resources: log.minor_success("perf events", "parsed") diff --git a/perun/collect/memory/parsing.py b/perun/collect/memory/parsing.py index 2fb15c312..0e12eb4cf 100644 --- a/perun/collect/memory/parsing.py +++ b/perun/collect/memory/parsing.py @@ -3,16 +3,16 @@ from __future__ import annotations # Standard Imports -from decimal import Decimal -from typing import Any, TYPE_CHECKING import collections +from decimal import Decimal import re +from typing import Any, TYPE_CHECKING # Third-Party Imports # Perun Imports from perun.collect.memory import syscalls -from perun.profiles.native import convert +from perun.profiles.native import helpers from perun.utils.common import common_kit if TYPE_CHECKING: @@ -116,7 +116,7 @@ def parse_resources(allocation: list[str]) -> dict[str, Any]: data["uid"] = parse_allocation_location(trace) # update the resource number - flattened_uid = convert.flatten(data["uid"]) + flattened_uid = helpers.flatten(data["uid"]) UID_RESOURCE_MAP[flattened_uid] += 1 data["allocation_order"] = UID_RESOURCE_MAP[flattened_uid] diff --git a/perun/meson.build b/perun/meson.build index 2c56ae86c..1dd3dd459 100644 --- a/perun/meson.build +++ b/perun/meson.build @@ -17,7 +17,7 @@ subdir('deltadebugging') subdir('logic') subdir('postprocess') subdir('profiles') -subdir('select') +subdir('selection') subdir('templates') subdir('testing') subdir('thirdparty') diff --git a/perun/postprocess/regression_analysis/data_provider.py b/perun/postprocess/regression_analysis/data_provider.py index 116008b95..51b29e52a 100644 --- a/perun/postprocess/regression_analysis/data_provider.py +++ b/perun/postprocess/regression_analysis/data_provider.py @@ -9,7 +9,7 @@ # Third-Party Imports # Perun Imports -from perun.profiles.native import convert +from perun.profiles.native import helpers if TYPE_CHECKING: from perun.profiles.native import Profile @@ -21,7 +21,7 @@ def resource_sort_key(resource: dict[str, Any]) -> str: :param resource: profiling resource :return: key used for sorting """ - return convert.flatten(resource["uid"]) + return helpers.flatten(resource["uid"]) def generic_profile_provider( @@ -43,16 +43,16 @@ def generic_profile_provider( resources = sorted(resources, key=resource_sort_key) x_points_list: list[float] = [] y_points_list: list[float] = [] - function_name = convert.flatten(resources[0]["uid"]) + function_name = helpers.flatten(resources[0]["uid"]) # Store all the points until the function name changes for resource in resources: - if convert.flatten(resource["uid"]) != function_name: + if helpers.flatten(resource["uid"]) != function_name: if x_points_list: # Function name changed, yield the list of data points yield x_points_list, y_points_list, function_name x_points_list = [resource[per_key]] y_points_list = [resource[of_key]] - function_name = convert.flatten(resource["uid"]) + function_name = helpers.flatten(resource["uid"]) else: # Add the data points x_points_list.append(resource[per_key]) diff --git a/perun/postprocess/regression_analysis/tools.py b/perun/postprocess/regression_analysis/tools.py index e9c39027e..70819729f 100644 --- a/perun/postprocess/regression_analysis/tools.py +++ b/perun/postprocess/regression_analysis/tools.py @@ -4,14 +4,15 @@ # Standard Imports from operator import itemgetter -from typing import Any, Iterable, TYPE_CHECKING import random +from typing import Any, Iterable, TYPE_CHECKING # Third-Party Imports import numpy as np # Perun Imports from perun.utils import exceptions +from perun.utils.common import common_kit if TYPE_CHECKING: import numpy.typing as npt @@ -22,8 +23,6 @@ MIN_POINTS_COUNT: int = 3 # R^2 value if computation failed R_SQUARE_DEFAULT: float = 0.0 -# Zero approximation to avoid zero division etc. -APPROX_ZERO: float = 0.000001 def validate_dictionary_keys( @@ -133,7 +132,7 @@ def split_model_interval(start: int, end: int, steps: int) -> npt.NDArray[np.flo x_pts = np.linspace(start, end, steps) # Replace all zeros by zero approximation to prevent zero division errors # Result of linspace is array, not tuple, with these arguments - x_pts[np.abs(x_pts) < APPROX_ZERO] = APPROX_ZERO + x_pts[np.abs(x_pts) < common_kit.APPROX_ZERO] = common_kit.APPROX_ZERO return x_pts diff --git a/perun/profiles/__init__.pyi b/perun/profiles/__init__.pyi index 0994acfd1..e15dd6d92 100644 --- a/perun/profiles/__init__.pyi +++ b/perun/profiles/__init__.pyi @@ -1 +1,36 @@ +from . import conversions as conversions +from . import folded as folded from . import native as native +from . import polars as polars + +from .imports import ( + ImportProfileSpec as ImportProfileSpec, + parse_metadata as parse_metadata, + parse_machine_info as parse_machine_info, + parse_import_entries as parse_import_entries, +) + +from .stats import ( + ProfileStatComparison as ProfileStatComparison, + StatComparisonResult as StatComparisonResult, + StatComparableType as StatComparableType, + ProfileStat as ProfileStat, + ProfileStatAggregation as ProfileStatAggregation, + SingleValue as SingleValue, + StatisticalSummary as StatisticalSummary, + StringCollection as StringCollection, + aggregate_stats as aggregate_stats, + compare_stats as compare_stats, + merge_stats as merge_stats, + features_to_stats as features_to_stats, +) + +from .structs import ( + PostprocessParameters as PostprocessParameters, + FilterParameters as FilterParameters, + ProfileFeatures as ProfileFeatures, + ProfileHeaderTuple as ProfileHeaderTuple, + ProfileMetadataEntry as ProfileMetadataEntry, +) + +from .utils import hide_uid_generics as hide_uid_generics diff --git a/perun/profiles/conversions/__init__.py b/perun/profiles/conversions/__init__.py new file mode 100644 index 000000000..faa65ac85 --- /dev/null +++ b/perun/profiles/conversions/__init__.py @@ -0,0 +1,8 @@ +"""A package for conversions between profile formats. + +Each module is lazily loaded so that only the necessary dependencies are imported. +""" + +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/perun/profiles/conversions/__init__.pyi b/perun/profiles/conversions/__init__.pyi new file mode 100644 index 000000000..b5a9ed45a --- /dev/null +++ b/perun/profiles/conversions/__init__.pyi @@ -0,0 +1,23 @@ +from .elk_native import import_elk_from_json as import_elk_from_json + +from .folded_polars import ( + PolarsAggregate as PolarsAggregate, + folded_profiles_to_polars_lf as folded_profiles_to_polars_lf, +) + +from .native_folded import native_to_folded as native_to_folded + +from .native_pandas import ( + resources_to_pandas_dataframe as resources_to_pandas_dataframe, + models_to_pandas_dataframe as models_to_pandas_dataframe, +) + +from .native_polars import native_to_polars_lf as native_to_polars_lf + +from .perf_native import ( + import_perf_from_record as import_perf_from_record, + import_perf_from_script as import_perf_from_script, + import_perf_from_stack as import_perf_from_stack, +) + +from .polars_folded import store_polars_as_folded_profile as store_polars_as_folded_profile diff --git a/perun/profiles/conversions/elk_native.py b/perun/profiles/conversions/elk_native.py new file mode 100644 index 000000000..997b23fd4 --- /dev/null +++ b/perun/profiles/conversions/elk_native.py @@ -0,0 +1,177 @@ +"""Functions for importing ELK profiles.""" + +from __future__ import annotations + +# Standard Imports +from collections import defaultdict +from dataclasses import asdict +import json +from pathlib import Path +from typing import Any + +# Third-Party Imports + +# Perun Imports +from perun.logic import config, pcs +from perun.profiles import imports, native as profile, structs +from perun.utils import log, streams +from perun.utils.common import common_kit +from perun.utils.structs import common_structs +from perun.vcs import vcs_kit + + +@vcs_kit.lookup_minor_version +def import_elk_from_json( + import_entries: list[str], + metadata: tuple[str, ...], + minor_version: str, + **kwargs: Any, +) -> None: + """Imports the ELK stored data from JSON data. + + The loading expects the JSON files to be in form of `{'queries': []}`. + + :param import_entries: list of filenames with elk data. + :param metadata: CLI-supplied additional metadata. Metadata specified in JSON take precedence. + :param minor_version: minor version corresponding to the imported profiles. + :param kwargs: rest of the parameters. + """ + import_dir = Path(config.lookup_key_recursively("import.dir", Path.cwd())) + resources: list[dict[str, Any]] = [] + # Load the CLI-supplied metadata, if any + elk_metadata: dict[str, structs.ProfileMetadataEntry] = { + data.name: data for data in imports.parse_metadata(metadata, import_dir) + } + + for elk_file in import_entries: + elk_file_path = imports.massage_import_path(elk_file, import_dir) + with streams.safely_open_and_log(elk_file_path, "r", fatal_fail=True) as elk_handle: + imported_json = json.load(elk_handle) + assert ( + "queries" in imported_json.keys() + ), "expected the JSON to contain list of dictionaries in 'queries' key" + r, m = _extract_from_elk(imported_json["queries"]) + resources.extend(r) + # Possibly overwrite CLI-supplied metadata when identical keys are found + elk_metadata.update(m) + log.minor_success(log.path_style(str(elk_file_path)), "imported") + minor_version_info = pcs.vcs().get_minor_version_info(minor_version) + _import_elk_profile(resources, elk_metadata, minor_version_info, **kwargs) + + +def _import_elk_profile( + resources: list[dict[str, Any]], + metadata: dict[str, structs.ProfileMetadataEntry], + minor_version: common_structs.MinorVersion, + **kwargs: Any, +) -> None: + """Constructs the profile for elk-stored data and saves them to jobs or index. + + :param resources: list of parsed resources. + :param metadata: parts of the profiles that will be stored as metadata in the profile. + :param minor_version: minor version corresponding to the imported profiles. + :param kwargs: rest of the parameters. + """ + prof = profile.Profile( + { + "global": { + "time": "???", + "resources": resources, + }, + "origin": minor_version.checksum, + "metadata": [asdict(data) for data in metadata.values()], + "machine": _extract_machine_info_from_elk_metadata(metadata), + "header": { + "type": "time", + "cmd": kwargs.get("cmd", ""), + "exitcode": "?", + "workload": kwargs.get("workload", ""), + "label": kwargs.get("profile_label", ""), + "units": {"time": "sample"}, + }, + "collector_info": { + "name": "???", + "params": {}, + }, + "postprocessors": [], + } + ) + profile.save_profile(prof, kwargs.get("profile_path"), minor_version) + + +def _extract_from_elk( + elk_query: list[dict[str, Any]], +) -> tuple[list[dict[str, Any]], dict[str, structs.ProfileMetadataEntry]]: + """For the given elk query, extracts resources and metadata. + + For metadata, we consider any key that has only single value through the profile, + and is not linked to keywords `metric` or `benchmarking`. + For resources, we consider anything that is not identified as metadata. + + :param elk_query: query from the elk in form of list of resource. + + :return: list of resources and metadata. + """ + res_counter = defaultdict(set) + for res in elk_query: + for key, val in res.items(): + res_counter[key].add(val) + metadata_keys = { + k + for (k, v) in res_counter.items() + if not k.startswith("metric") and not k.startswith("benchmarking") and len(v) == 1 + } + + metadata = {k: structs.ProfileMetadataEntry(k, res_counter[k].pop()) for k in metadata_keys} + resources = [ + { + k: common_kit.try_convert(v, [int, float, str]) + for k, v in res.items() + if k not in metadata_keys + } + for res in elk_query + ] + # We register uid + for res in resources: + res["uid"] = res["metric.name"] + res["benchmarking.time"] = res["benchmarking.end-ts"] - res["benchmarking.start-ts"] + res.pop("benchmarking.end-ts") + res.pop("benchmarking.start-ts") + return resources, metadata + + +def _extract_machine_info_from_elk_metadata( + metadata: dict[str, structs.ProfileMetadataEntry], +) -> dict[str, Any]: + """Extracts the parts of the profile that correspond to machine info. + + Note that not many is collected from the ELK formats, and it can vary greatly, + hence, most of the machine specification and environment should be in metadata instead. + + :param metadata: metadata extracted from the ELK profiles. + + :return: machine info extracted from the profiles. + """ + machine_info: dict[str, Any] = { + "architecture": metadata.get("machine.arch", structs.ProfileMetadataEntry("", "?")).value, + "system": str( + metadata.get("machine.os", structs.ProfileMetadataEntry("", "?")).value + ).capitalize(), + "release": metadata.get( + "extra.machine.platform", structs.ProfileMetadataEntry("", "?") + ).value, + "host": metadata.get("machine.hostname", structs.ProfileMetadataEntry("", "?")).value, + "cpu": { + "physical": "?", + "total": metadata.get("machine.cpu-cores", structs.ProfileMetadataEntry("", "?")).value, + "frequency": "?", + }, + "memory": { + "total_ram": metadata.get("machine.ram", structs.ProfileMetadataEntry("", "?")).value, + "swap": "?", + }, + "boot_info": "?", + "mem_details": {}, + "cpu_details": [], + } + return machine_info diff --git a/perun/profiles/conversions/folded_polars.py b/perun/profiles/conversions/folded_polars.py new file mode 100644 index 000000000..90301955e --- /dev/null +++ b/perun/profiles/conversions/folded_polars.py @@ -0,0 +1,101 @@ +"""Functions for converting folded profiles into Polars profiles.""" + +from __future__ import annotations + +# Standard Imports +from collections.abc import Sequence +import pathlib +from typing import Protocol + +# Third-Party Imports +import polars as pl + +# Perun Imports +from perun.profiles.folded import parser +from perun.profiles.polars import parser as pl_parser, structs +from perun.profiles.structs import PostprocessParameters, ProfileFeatures +from perun.utils import log, streams +from perun.utils.common.common_kit import Aggregations + + +class PolarsAggregate(Protocol): + """Polars aggregation callable type.""" + + def __call__(self, *args: str) -> pl.Expr: ... + + +def folded_profiles_to_polars_lf( + folded_profiles: Sequence[pathlib.Path], + postprocess_params: PostprocessParameters, + maps: structs.FunctionMaps, +) -> tuple[pl.LazyFrame, ProfileFeatures]: + """Load folded profile(s) into a Polars LazyFrame according to the parse parameters. + + If multiple folded profiles are given, we aggregate them into a single LazyFrame according to + the aggregation function specified in the parse parameters. + + Note that the returned profile features contain only those features that can be computed + cheaply during the parsing. + + :param folded_profiles: possibly a collection of folded profile paths + :param postprocess_params: parsing parameters and configuration + :param maps: function maps + + :return: a (possibly aggregated) LazyFrame and incomplete profile features + """ + features: ProfileFeatures = ProfileFeatures() + # Check whether we should squash recursion and select the parse function accordingly. + parse_func = ( + pl_parser.parse_polars_squash + if postprocess_params.squash_recursion + else pl_parser.parse_polars_no_squash + ) + parsed_traces: list[pl.LazyFrame] = [] + for profile_path in folded_profiles: + with streams.open_folded_profile(profile_path) as folded_handle: + parsed_traces.append( + parse_func( + parser.parse_events_from_stream(folded_handle), + maps, + postprocess_params, + features, + ) + ) + log.minor_success(log.path_style(str(profile_path)), "parsed") + + if len(parsed_traces) == 1: + # No need to aggregate data for a single input profile. + return parsed_traces[0], features + # We have parsed multiple profiles. Hence, each trace might have more than 1 value that we + # need to aggregate. We do that by concatenating the profiles, grouping the same traces and + # then aggregating the values. + agg_callable = _get_aggregation_polars_callable(postprocess_params.agg_func) + return ( + pl.concat(parsed_traces) + .group_by("func", "trace") + .agg([agg_callable("inclusive").cast(pl.Int64), agg_callable("exclusive").cast(pl.Int64)]), + features, + ) + + +def _get_aggregation_polars_callable(agg_func: Aggregations) -> PolarsAggregate: + """Maps an aggregation function to a polars callables. + + If the function name does not have a valid mapping, the default function callable is used. + + :param agg_func: aggregation function + :return: a polars callable + """ + callable_map: dict[Aggregations, PolarsAggregate] = { + Aggregations.SUM: pl.sum, + Aggregations.MIN: pl.min, + Aggregations.MAX: pl.max, + Aggregations.COUNT: pl.count, + Aggregations.NUNIQUE: pl.n_unique, + Aggregations.MEAN: pl.mean, + Aggregations.MEDIAN: pl.median, + } + try: + return callable_map[agg_func] + except KeyError: + return callable_map[Aggregations.default()] diff --git a/perun/profiles/conversions/meson.build b/perun/profiles/conversions/meson.build new file mode 100644 index 000000000..3d9946ced --- /dev/null +++ b/perun/profiles/conversions/meson.build @@ -0,0 +1,18 @@ +perun_conversions_profile_dir = perun_profiles_dir / 'conversions' + +perun_conversions_profile_files = files( + '__init__.py', + '__init__.pyi', + 'elk_native.py', + 'folded_polars.py', + 'native_folded.py', + 'native_pandas.py', + 'native_polars.py', + 'perf_native.py', + 'polars_folded.py', +) + +py3.install_sources( + perun_conversions_profile_files, + subdir: perun_conversions_profile_dir +) \ No newline at end of file diff --git a/perun/profiles/conversions/native_folded.py b/perun/profiles/conversions/native_folded.py new file mode 100644 index 000000000..9f4e6aa84 --- /dev/null +++ b/perun/profiles/conversions/native_folded.py @@ -0,0 +1,79 @@ +"""Functions for converting native profiles to folded profiles.""" + +from __future__ import annotations + +# Standard Imports +from typing import Any, Iterator, TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports +from perun.utils.common import common_kit + +if TYPE_CHECKING: + from perun.profiles.native import Profile + + +def native_to_folded( + profile: Profile, + profile_key: str = "amount", + agg_func: common_kit.Aggregations = common_kit.Aggregations.default(), +) -> Iterator[tuple[str, int]]: + """Transforms the **memory** or **time** profile w.r.t. :ref:`profile-spec` into the + folded format used, e.g., by flame graph scripts. + + .. _Brendan Gregg's homepage: https://www.brendangregg.com/index.html + + :ref:`views-flame-graph` can be used to visualize the inclusive consumption + of resources w.r.t. the call trace of the resource. It is useful for fast + detection, which point at the trace is the hotspot (or bottleneck) in the + computation. Refer to :ref:`views-flame-graph` for full capabilities of our + Wrapper. For more information about flame graphs itself, please check + `Brendan Gregg's homepage`_. + + Example of format is as follows:: + + >>> print(''.join(native_to_folded(memprof))) + malloc()~unreachable~0;main()~/home/user/dev/test.c~45 4 + valloc()~unreachable~0;main()~/home/user/dev/test.c~75;__libc_start_main()~unreachable~0 8 + main()~/home/user/dev/test02.c~79 156 + + Each line corresponds to some collected resource (in this case amount of + allocated memory) preceded by its trace (i.e. functions or other unique + identifiers joined using ``;`` character). + + :param profile: the memory/time profile + :param profile_key: the key under which the measured consumption is stored + :param agg_func: an aggregation to use when multiple measurements are present in time profiles + + :returns: an iterator over folded profile records of (stack_trace, consumption) + """ + if profile["header"]["type"] == "memory": + for _, snapshot in profile.all_snapshots(): + for alloc in snapshot: + if "subtype" not in alloc.keys() or alloc["subtype"] != "free": + # Workaround for total time used in some collectors, so it is not outputted + if alloc["uid"] == "%TOTAL_TIME%" or profile_key not in alloc: + continue + stack: list[dict[str, Any]] = alloc.get("trace", alloc["uid"]) + folded_record = ";".join((_to_string_line(frame) for frame in reversed(stack))) + yield folded_record, alloc[profile_key] + else: + agg_callable = common_kit.get_aggregation_callable(agg_func) + for resource in profile.get_kperf_resources(): + yield resource["uid"], int(agg_callable(resource[profile_key])) + + +def _to_string_line(frame: dict[str, Any] | str) -> str: + """Create string representing call stack's frame + + :param frame: call stack's frame + :return: line representing call stack's frame + """ + if isinstance(frame, str): + return frame + elif "function" in frame.keys() and "source" in frame.keys() and "line" in frame.keys(): + return f"{frame['function']}()~{frame['source']}~{frame['line']}" + else: + assert "func" in frame.keys() + return f"{frame['func']}" diff --git a/perun/profiles/conversions/native_pandas.py b/perun/profiles/conversions/native_pandas.py new file mode 100644 index 000000000..7c5a0a470 --- /dev/null +++ b/perun/profiles/conversions/native_pandas.py @@ -0,0 +1,90 @@ +"""Functions for converting native profiles to Pandas representations.""" + +from __future__ import annotations + +# Standard Imports +import array +from typing import Any, TYPE_CHECKING + +# Third-Party Imports +import numpy +import pandas + +# Perun Imports +from perun.profiles.native import query +from perun.utils import log + +if TYPE_CHECKING: + from perun.profiles.native import Profile + + +def resources_to_pandas_dataframe(profile: Profile) -> pandas.DataFrame: + """Converts the profile (w.r.t :ref:`profile-spec`) to format supported by + `pandas`_ library. + + Queries through the resources in the `profile`, and flattens each + key and value to the tabular representation. Refer to `pandas`_ library for + more possibilities how to work with the tabular representation of collected + resources. + + E.g. given `time` and `memory` profiles ``tprof`` and ``mprof`` + respectively, one can obtain the following formats:: + + >>> convert.resources_to_pandas_dataframe(tprof) + amount snapshots uid + 0 0.616s 0 real + 1 0.500s 0 user + 2 0.125s 0 sys + + >>> convert.resources_to_pandas_dataframe(mmprof) + address amount snapshots subtype trace type + 0 19284560 4 0 malloc malloc:unreachabl... memory + 1 19284560 0 0 free free:unreachable:... memory + + uid uid:function uid:line uid:source + 0 main:../memo...:22 main 22 ../memory_collect_test.c + 1 main:../memo...:27 main 27 ../memory_collect_test.c + + :param profile: dictionary with profile w.r.t. :ref:`profile-spec` + :returns: converted profile to ``pandas.DataFramelist`` with resources + flattened as a pandas dataframe + """ + # Since some keys may be missing in the resources, we consider the possible fields + resource_keys = list(profile.all_resource_fields()) + values: dict[str, list[Any] | array.array[float] | array.array[int]] = { + key: [] for key in resource_keys + } + values["snapshots"] = array.array("I") + + # All resources at this point should be flat + for snapshot, resource in log.progress( + profile.all_resources(flatten_values=True), "Converting To Pandas" + ): + values["snapshots"].append(snapshot) + for resource_key in resource_keys: + values[resource_key].append(resource.get(resource_key, numpy.nan)) + + return pandas.DataFrame(values) + + +def models_to_pandas_dataframe(profile: Profile) -> pandas.DataFrame: + """Converts the models of profile (w.r.t :ref:`profile-spec`) to format + supported by `pandas`_ library. + + Queries through all the models in the `profile`, and flattens each + key and value to the tabular representation. Refer to `pandas`_ library for + more possibilities how to work with the tabular representation of models. + + :param profile: dictionary with profile w.r.t. :ref:`profile-spec` + :returns: converted models of profile to ``pandas.DataFramelist`` + """ + # Note that we need to this inefficiently, because some keys can be missing in resources + model_keys = list(query.all_model_fields_of(profile)) + values: dict[str, list[Any]] = {key: [] for key in model_keys} + + for _, model in log.progress(profile.all_models(), description="Converting To Pandas"): + flattened_resources = dict(list(query.all_items_of(model))) + for model_key in model_keys: + values[model_key].append(flattened_resources.get(model_key, numpy.nan)) + + return pandas.DataFrame(values) diff --git a/perun/profiles/conversions/native_polars.py b/perun/profiles/conversions/native_polars.py new file mode 100644 index 000000000..62319444d --- /dev/null +++ b/perun/profiles/conversions/native_polars.py @@ -0,0 +1,53 @@ +"""Functions for converting native profiles to Polars representations.""" + +from __future__ import annotations + +# Standard Imports +from typing import TYPE_CHECKING + +# Third-Party Imports +import polars as pl + +# Perun Imports +from perun.profiles import structs +from perun.profiles.conversions import native_folded +from perun.profiles.polars import parser, structs as pl_structs + +if TYPE_CHECKING: + from perun.profiles.native import Profile + + +def native_to_polars_lf( + native_profile: Profile, + postprocess_params: structs.PostprocessParameters, + maps: pl_structs.FunctionMaps, +) -> tuple[pl.LazyFrame, structs.ProfileFeatures]: + """Load and postprocess a Perun profile into a Polars LazyFrame. + + The resource consumption for each UID are aggregated into a single value according to the + aggregation function specified in the postprocessing parameters. + + Note that the returned profile features contain only those features that can be computed + cheaply during the parsing. + + :param native_profile: the Perun profile to convert to Polars + :param postprocess_params: postprocessing parameters and configuration + :param maps: function maps + + :return: a LazyFrame and incomplete profile features structure + """ + features: structs.ProfileFeatures = structs.ProfileFeatures() + # Check whether we should squash recursion and select the parse function accordingly. + parse_func = ( + parser.parse_polars_squash + if postprocess_params.squash_recursion + else parser.parse_polars_no_squash + ) + # We re-use the native-to-folded conversion which takes care of aggregating the resource values. + polars_prof: pl.LazyFrame = parse_func( + native_folded.native_to_folded(native_profile, "amount", postprocess_params.agg_func), + maps, + postprocess_params, + features, + ) + return polars_prof, features diff --git a/perun/profiles/conversions/perf_native.py b/perun/profiles/conversions/perf_native.py new file mode 100644 index 000000000..cbea5b105 --- /dev/null +++ b/perun/profiles/conversions/perf_native.py @@ -0,0 +1,187 @@ +"""Functions for importing Perf profiles as native profiles.""" + +from __future__ import annotations + +# Standard Imports +from dataclasses import asdict +import os +from pathlib import Path +import subprocess +import sys +from typing import Any + +# Third-Party Imports + +# Perun Imports +from perun.profiles import imports, native as profile, stats +from perun.profiles.folded import parser +from perun.logic import config, pcs +from perun.utils import log, streams +from perun.utils.common import script_kit +from perun.utils.external import commands as external_commands +from perun.utils.structs.common_structs import MinorVersion +from perun.vcs import vcs_kit + + +@vcs_kit.lookup_minor_version +def import_perf_from_record( + import_entries: list[str], + stats_headers: str | None, + minor_version: str, + with_sudo: bool = False, + **kwargs: Any, +) -> None: + """Imports profiles collected by `perf record` command. + + First, the function parses all the perf import entries and stats headers, and then it runs + the perf script + parser script for each entry to generate the profile. + + :param import_entries: a collection of import entries (profiles or CSV files). + :param stats_headers: CLI-specified stats headers. + :param minor_version: minor version corresponding to the imported profiles. + :param with_sudo: indication whether the data were collected with sudo. + :param kwargs: rest of the parameters. + """ + # TODO: tag commands with (platform, system, ...) requirements and have a unified mechanism + # to detect failures + # TODO: can be optimized a lot. + if sys.platform == "darwin": + log.error("Import from perf record is not supported on macOS platform.") + parse_script = script_kit.get_script("stackcollapse-perf.pl") + import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) + profiles, prof_stats = imports.parse_import_entries(import_entries, import_dir, stats_headers) + resources: list[dict[str, str | int]] = [] + + for imported_file in profiles: + perf_script_command = ( + f"{'sudo ' if with_sudo else ''}perf script -i {imported_file.path} | {parse_script}" + ) + try: + out, _ = external_commands.run_safely_external_command(perf_script_command) + log.minor_success( + f"Raw data from {log.path_style(str(imported_file.path))}", "collected" + ) + except subprocess.CalledProcessError as err: + log.minor_fail( + f"Raw data from {log.path_style(str(imported_file.path))}", "not collected" + ) + log.error(f"Cannot load data due to: {err}") + parser.parse_resources_from_stream(out.decode("utf-8").splitlines(), resources) + log.minor_success(log.path_style(str(imported_file.path)), "imported") + minor_version_info = pcs.vcs().get_minor_version_info(minor_version) + _import_perf_profile( + profiles, prof_stats, resources, minor_version_info, with_sudo=with_sudo, **kwargs + ) + + +@vcs_kit.lookup_minor_version +def import_perf_from_script( + import_entries: list[str], + stats_headers: str | None, + minor_version: str, + **kwargs: Any, +) -> None: + """Imports profiles collected by `perf record | perf script` command. + + First, the function parses all the perf import entries and stats headers, and then it runs + the parser script for each entry to generate the profile. + + :param import_entries: a collection of import entries (profiles or CSV files). + :param stats_headers: CLI-specified stats headers. + :param minor_version: minor version corresponding to the imported profiles. + :param kwargs: rest of the parameters. + """ + # TODO: can be optimized a lot. + parse_script = script_kit.get_script("stackcollapse-perf.pl") + import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) + profiles, prof_stats = imports.parse_import_entries(import_entries, import_dir, stats_headers) + resources: list[dict[str, str | int]] = [] + + for imported_file in profiles: + perf_script_command = f"cat {imported_file.path} | {parse_script}" + out, _ = external_commands.run_safely_external_command(perf_script_command) + log.minor_success(f"Raw data from {log.path_style(str(imported_file.path))}", "collected") + parser.parse_resources_from_stream(out.decode("utf-8").splitlines(), resources) + log.minor_success(log.path_style(str(imported_file.path)), "imported") + minor_version_info = pcs.vcs().get_minor_version_info(minor_version) + _import_perf_profile(profiles, prof_stats, resources, minor_version_info, **kwargs) + + +@vcs_kit.lookup_minor_version +def import_perf_from_stack( + import_entries: list[str], + stats_headers: str | None, + minor_version: str, + **kwargs: Any, +) -> None: + """Imports profiles collected by `perf record | perf script | stackcollapse-perf.pl` command. + + First, the function parses all the perf import entries and stats headers, and then it parses + each entry to generate the profile. + + :param import_entries: a collection of import entries (profiles or CSV files). + :param stats_headers: CLI-specified stats headers. + :param minor_version: minor version corresponding to the imported profiles. + :param kwargs: rest of the parameters. + """ + import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) + profiles, prof_stats = imports.parse_import_entries(import_entries, import_dir, stats_headers) + resources: list[dict[str, str | int]] = [] + + for imported_profile in profiles: + with streams.open_folded_profile(imported_profile.path) as folded_stream: + parser.parse_resources_from_stream(folded_stream, resources) + log.minor_success(log.path_style(str(imported_profile.path)), "imported") + minor_version_info = pcs.vcs().get_minor_version_info(minor_version) + _import_perf_profile(profiles, prof_stats, resources, minor_version_info, **kwargs) + + +def _import_perf_profile( + profiles: list[imports.ImportProfileSpec], + prof_stats: list[stats.ProfileStat], + resources: list[dict[str, Any]], + minor_version: MinorVersion, + **kwargs: Any, +) -> None: + """Constructs the profile for perf-collected data and saves them to jobs or index. + + :param profiles: a collection of specifications of the profiles that are being imported. + :param prof_stats: a collection of stats that should be associated with the profile. + :param resources: a collection of parsed resources. + :param minor_version: minor version corresponding to the imported profiles. + :param kwargs: rest of the parameters. + """ + import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) + prof = profile.Profile( + { + "global": { + "time": "???", + "resources": resources, + }, + "origin": minor_version.checksum, + "machine": imports.parse_machine_info(kwargs.get("machine_info", ""), import_dir), + "metadata": [ + asdict(data) + for data in imports.parse_metadata(kwargs.get("metadata", tuple()), import_dir) + ], + "stats": [asdict(stat) for stat in prof_stats], + "header": { + "type": "time", + "cmd": kwargs.get("cmd", ""), + "exitcode": [p.exit_code for p in profiles], + "workload": kwargs.get("workload", ""), + "label": kwargs.get("profile_label", ""), + "units": {"time": "sample"}, + }, + "collector_info": { + "name": "kperf", + "params": { + "with_sudo": kwargs.get("with_sudo", False), + "warmup": kwargs.get("warmup", 0), + "repeat": len(profiles), + }, + }, + "postprocessors": [], + } + ) + profile.save_profile(prof, kwargs.get("profile_path"), minor_version) diff --git a/perun/profiles/conversions/polars_folded.py b/perun/profiles/conversions/polars_folded.py new file mode 100644 index 000000000..42a974102 --- /dev/null +++ b/perun/profiles/conversions/polars_folded.py @@ -0,0 +1,55 @@ +"""Functions for converting Polars profiles to folded profiles.""" + +from __future__ import annotations + +# Standard Imports +from typing import TextIO, TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports + +if TYPE_CHECKING: + from perun.profiles.polars import structs + from perun.utils.type_hints import TempTextIO + + +def store_polars_as_folded_profile( + trace_profile: structs.PolarsTraceProfile, + folded_file: TextIO | TempTextIO, + min_width_threshold: float = 0.0, +) -> int: + """Store a Polars trace profile into a folded profile. + + As a by-product, we also compute the maximum length of traces that will be rendered in + flamegraphs of this profile given the input width threshold. + + :param trace_profile: a Polars trace profile + :param folded_file: a handle to the output file + :param min_width_threshold: the rendering threshold for flamegraphs + + :return: the maximum trace length w.r.t. the filtering threshold + """ + # Optimize dot operator access + str_split = str.split + str_join = str.join + func_id_reverse_map = trace_profile.maps.func_id_reverse_map + + max_filtered_len: int = 0 + + # Only keep the relevant columns + for trace_str, exclusive, inclusive in trace_profile.profile.select( + ["trace", "exclusive", "inclusive"] + ).iter_rows(): + trace_parts = str_split(trace_str, ";") + if exclusive: + # Translate compact trace format to the verbose one, e.g., "1;2;10" -> "main;foo;bar" + translated = str_join( + ";", (func_id_reverse_map[int(func_id)] for func_id in trace_parts) + ) + folded_file.write(f"{translated} {exclusive}\n") + # Update the maximum trace length + if inclusive >= min_width_threshold: + max_filtered_len = max(max_filtered_len, len(trace_parts)) + folded_file.flush() + return max_filtered_len diff --git a/perun/profiles/folded/__init__.py b/perun/profiles/folded/__init__.py new file mode 100644 index 000000000..5369fbfed --- /dev/null +++ b/perun/profiles/folded/__init__.py @@ -0,0 +1,5 @@ +"""A folded profile type representation.""" + +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/perun/profiles/folded/__init__.pyi b/perun/profiles/folded/__init__.pyi new file mode 100644 index 000000000..a49f6062e --- /dev/null +++ b/perun/profiles/folded/__init__.pyi @@ -0,0 +1,6 @@ +from .parser import ( + parse_resources_from_stream as parse_resources_from_stream, + parse_events_from_stream as parse_events_from_stream, +) + +from .postprocess import postprocess_folded_records as postprocess_folded_records diff --git a/perun/profiles/folded/meson.build b/perun/profiles/folded/meson.build new file mode 100644 index 000000000..c421b449a --- /dev/null +++ b/perun/profiles/folded/meson.build @@ -0,0 +1,13 @@ +perun_folded_profile_dir = perun_profiles_dir / 'folded' + +perun_folded_profile_files = files( + '__init__.py', + '__init__.pyi', + 'parser.py', + 'postprocess.py', +) + +py3.install_sources( + perun_folded_profile_files, + subdir: perun_folded_profile_dir +) \ No newline at end of file diff --git a/perun/profiles/folded/parser.py b/perun/profiles/folded/parser.py new file mode 100755 index 000000000..3a47397d8 --- /dev/null +++ b/perun/profiles/folded/parser.py @@ -0,0 +1,56 @@ +"""A collection of functions for parsing folded profiles.""" + +from __future__ import annotations + +# Standard Imports +from collections.abc import Iterable, Iterator +from typing import Callable, TextIO + +# Third-Party Imports + +# Perun Imports + + +def parse_resources_from_stream( + folded_stream: TextIO | Iterable[str], resources: list[dict[str, str | int]] +) -> None: + """Parses a stream of folded perf events into a list of resources. + + Each resource is identified by its trace (uid), which consists of semicolon-delimited stack + frames, and the consumed resources. + + :param folded_stream: a stream of folded perf events + :param resources: the output list of resources + """ + # Optimize dot operator access. + str_rsplit = str.rsplit + # Mypy cannot infer the type variable of the list.append method on its own. + list_append: Callable[[list[dict[str, str | int]], dict[str, str | int]], None] = list.append + + for line in folded_stream: + try: + stack, count_str = str_rsplit(line, maxsplit=1) + # resources.append({"amount": int(count_str), "uid": stack}) + list_append(resources, {"amount": int(count_str), "uid": stack}) + except ValueError: + # Ignore invalid rows. + pass + + +def parse_events_from_stream(folded_stream: TextIO) -> Iterator[tuple[str, int]]: + """Parses a file containing folded profile into a stream of folded records. + + :param folded_stream: a file containing folded perf events. + + :return: a stream of folded records. + """ + # Optimize dot operator access. + str_rsplit = str.rsplit + + for record in folded_stream: + try: + # Parse the line, obtain the performance value and the trace. + trace, count = str_rsplit(record, " ", maxsplit=1) + yield trace, int(count) + except ValueError: + pass diff --git a/perun/profiles/folded/postprocess.py b/perun/profiles/folded/postprocess.py new file mode 100644 index 000000000..44a847248 --- /dev/null +++ b/perun/profiles/folded/postprocess.py @@ -0,0 +1,85 @@ +"""Functions for postprocessing of folded profiles.""" + +from __future__ import annotations + +# Standard Imports +from collections.abc import Iterator +import re +from typing import TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports +from perun.profiles import utils + +if TYPE_CHECKING: + from perun.profiles.structs import PostprocessParameters + + +def postprocess_folded_records( + input_stream: Iterator[tuple[str, int]], + params: PostprocessParameters, +) -> Iterator[tuple[str, int]]: + """Postprocess a stream of folded records. + + The postprocessing includes hiding generics in function names and/or squashing recursive calls. + + :param input_stream: a stream of folded records (stack trace, resource consumption) + :param params: a structure containing the postprocessing parameters + + :return: an iterator over postprocessed folded records + """ + + # Optimize dot operator access. + re_search = re.search + str_split = str.split + squash_pattern = params.squash_pattern + squash_recursion_flag = params.squash_recursion + hide_generics_flag = params.hide_generics + + # Fast path if no postprocessing is requested. + if not hide_generics_flag and not squash_recursion_flag: + yield from input_stream + return + + # Slow path if either squashing or generics hiding is requested. + for trace, count in input_stream: + # Split the trace into individual frames. + frames: list[str] = str_split(trace, ";") + + # Transform the frames in-place. + if hide_generics_flag: + frames[:] = [utils.hide_uid_generics(frame) for frame in frames] + + # The squashing algorithm is implemented using two indices over the frame stack: the `iter` + # index iterates over the entire stack to detect recursive calls, and the `stack` index + # overwrites frames in-place when squashing happens. + if squash_recursion_flag: + stack_idx: int = 0 + iter_idx: int = 1 + end: int = len(frames) + recursive_count: int = 1 + while iter_idx < end: + # We want to merge the frames if they represent the same function, and they match + # the squash pattern. Regex matching is done only once for each sequence of + # identical frames, and only for non-default patterns (default pattern matches + # everything). + if frames[stack_idx] == frames[iter_idx] and ( + squash_pattern is None + or (recursive_count > 1 or re_search(squash_pattern, frames[iter_idx])) + ): + # This frame is part of a recursive call chain. + recursive_count += 1 + iter_idx += 1 + continue + elif recursive_count > 1: + # A recursive call chain has ended. We update the name of the squashed function. + frames[stack_idx] = f";{frames[stack_idx]}{{x{recursive_count}}}" + recursive_count = 1 + stack_idx += 1 + iter_idx += 1 + # We must still update the last frame if it was part of a recursive call. + if recursive_count > 1: + frames[stack_idx] = f";{frames[stack_idx]}{{x{recursive_count}}}" + stack_idx += 1 + yield ";".join(frames[:stack_idx]), count diff --git a/perun/profiles/imports.py b/perun/profiles/imports.py new file mode 100644 index 000000000..9683b59e6 --- /dev/null +++ b/perun/profiles/imports.py @@ -0,0 +1,259 @@ +"""Structures and helper functions for importing profiles.""" + +from __future__ import annotations + +# Standard Imports +import dataclasses +import json +from pathlib import Path +from typing import Any, Sequence + +# Third-Party Imports +import polars as pl # Used for advanced CSV parsing compared to the stdlib csv. + +# Perun Imports +from perun.profiles import stats, structs +from perun.profiles.native import query +from perun.utils import log, streams +from perun.utils.common import common_kit +from perun.utils.external import environment + + +@dataclasses.dataclass +class ImportProfileSpec: + """A representation of a profile record to import. + + :ivar path: an absolute path to the profile. + :ivar exit_code: the exit code of the profile collection process. + """ + + path: Path + exit_code: str = "?" + + +def parse_metadata( + metadata: tuple[str, ...], import_dir: Path +) -> list[structs.ProfileMetadataEntry]: + """Parse metadata entries from CLI and convert them to our internal representation. + + :param import_dir: the import directory to use for relative metadata file paths. + :param metadata: a collection of metadata entries or JSON files. + + :return: a collection of parsed and converted metadata objects. + """ + p_metadata: list[structs.ProfileMetadataEntry] = [] + # Normalize the metadata string for parsing and/or opening the file + for metadata_str in map(str.strip, metadata): + if metadata_str.lower().endswith(".json"): + # Update the metadata collection with entries from the json file + p_metadata.extend(_parse_metadata_json(massage_import_path(metadata_str, import_dir))) + else: + # Add a single metadata entry parsed from its string representation + try: + p_metadata.append(structs.ProfileMetadataEntry.from_string(metadata_str)) + except TypeError: + log.warn(f"Ignoring invalid profile metadata string '{metadata_str}'.") + return p_metadata + + +def parse_machine_info(machine_info: str, import_dir: Path) -> dict[str, Any]: + """Returns machine info either from an input file or constructs it from the environment. + + :param machine_info: relative or absolute path to machine specification JSON file. In case of + an empty string, the machine info will be constructed from the environment. + :param import_dir: import directory where to look for the machine info file if the provided + path is relative. + + :return: parsed or constructed machine specification. + """ + if machine_info: + # Some machine info path has been provided. + info_path = massage_import_path(machine_info, import_dir) + with streams.safely_open_and_log( + info_path, "r", fail_msg="not found, generating info from environment instead" + ) as info_handle: + if info_handle is not None: + json_data = json.load(info_handle) + log.minor_success(log.path_style(str(info_path)), "parsed") + return json_data + # No machine info file might have been provided, or an invalid path was specified. + # Construct the machine info from the current machine. + return environment.get_machine_specification() + + +def massage_import_path(path_str: str, import_dir: Path) -> Path: + """Massages path strings into a unified path format. + + First, the path string is stripped of leading and trailing whitespaces. + Next, absolute paths are kept as is, while relative paths are prepended with the + provided import directory. + + :param import_dir: the import directory to use for relative paths. + :param path_str: the path string to massage. + + :return: the massaged path. + """ + path: Path = Path(path_str.strip()) + if path.is_absolute(): + return path + return import_dir / path + + +def parse_import_entries( + import_entries: list[str], import_dir: Path, cli_stats_headers: str | None +) -> tuple[list[ImportProfileSpec], list[stats.ProfileStat]]: + """Parses import entries and stats. + + An import entry is either a profile entry + + 'profile_path[,[,]+]' + + where each stat value corresponds to a stats header specified in the cli_stats_headers, or + a CSV file entry + + 'file_path.csv' + + where the CSV file is in the format + + Profile,Exit_code[,stat-header1]+ + profile_path[,[,]+] + ... + + that combines the --stats-headers option and profile entries. Stats specified in a CSV file + apply only to profile entries in the JSON file. Similarly, CLI-specified stats apply only to + profile entries specified directly in CLI. + + :param import_entries: the import entries to parse. + :param import_dir: the import directory to use for relative profile paths. + :param cli_stats_headers: the stats headers specified in CLI. + + :return: parsed profiles and stats. + """ + prof_stats = [] + if cli_stats_headers is not None: + prof_stats = [ + stats.ProfileStat.from_string(*stat.split("|")) for stat in cli_stats_headers.split(",") + ] + cli_stats_len = len(prof_stats) + profiles: list[ImportProfileSpec] = [] + + for record in import_entries: + if record.strip().lower().endswith(".csv"): + # The input is a csv file + _parse_import_csv(record, import_dir, profiles, prof_stats) + elif ( + profile_spec := _parse_import_entry( + record.split(","), import_dir, prof_stats[:cli_stats_len] + ) + ) is not None: + # The input is a string profile spec + profiles.append(profile_spec) + return profiles, prof_stats + + +def _parse_metadata_json(metadata_path: Path) -> list[structs.ProfileMetadataEntry]: + """Parse a metadata JSON file into the metadata objects. + + If the JSON file contains nested dictionaries, the hierarchical keys will be flattened. + + :param metadata_path: the path to the metadata JSON. + + :return: a collection of parsed metadata objects. + """ + with streams.safely_open_and_log( + metadata_path, "r", fail_msg="not found, skipping" + ) as metadata_handle: + if metadata_handle is None: + return [] + # Make sure we flatten the input + metadata_list = [ + structs.ProfileMetadataEntry(k, v) + for k, v in query.all_items_of(json.load(metadata_handle)) + ] + log.minor_success(log.path_style(str(metadata_path)), "parsed") + return metadata_list + + +def _parse_import_csv( + csv_file: str, + import_dir: Path, + profiles: list[ImportProfileSpec], + prof_stats: list[stats.ProfileStat], +) -> None: + """Parse stats headers and import entries in a CSV file. + + :param csv_file: the CSV file to parse. + :param import_dir: the import directory to use for relative profile file paths. + :param profiles: profile specifications that will be extended with the parsed profiles. + :param prof_stats: profile stats that will be merged with the CSV stats. + """ + csv_path = massage_import_path(csv_file, import_dir) + with streams.safely_open_and_log(csv_path, "r", fatal_fail=True) as csvfile_handle: + try: + import_csv = pl.read_csv(csvfile_handle, comment_prefix="#") + except pl.exceptions.NoDataError: + # Empty CSV file, skip + log.warn(f"Empty file {csv_path}. Skipping.") + return + except pl.exceptions.ComputeError as e: + # The CSV file contains spurious columns, or is otherwise weirdly formatted. + # We log the issue so the user is aware of it, and we retry parsing it less strictly. + log.warn( + f"Encountered error when processing {csv_path}: {str(e).splitlines()[0]}." + " Attempting recovery." + ) + import_csv = pl.read_csv(csvfile_handle, comment_prefix="#", truncate_ragged_lines=True) + # Parse the stats headers + csv_stats: list[stats.ProfileStat] = [ + stats.ProfileStat.from_string(*stat_definition.split("|")) + for stat_definition in import_csv.columns[2:] + ] + # Parse the remaining rows that represent profile specifications and filter invalid ones + profiles.extend( + record + for row in import_csv.iter_rows() + if (record := _parse_import_entry(row, import_dir, csv_stats)) is not None + ) + # Merge CSV stats with the other stats + for csv_stat in csv_stats: + stats.merge_stats(csv_stat, prof_stats) + log.minor_success(log.path_style(str(csv_path)), "parsed") + + +def _parse_import_entry( + entry: Sequence[str | float], import_dir: Path, prof_stats: list[stats.ProfileStat] +) -> ImportProfileSpec | None: + """Parse a single profile import entry. + + :param entry: the import entry to parse. + :param import_dir: the import directory to use for relative profile file paths. + :param prof_stats: the profile stats associated with this profile. + + :return: the parsed profile, or None if the import entry is invalid. + """ + # Attempt to parse the profile specification. + if len(entry) == 0 or entry[0] is None or str(entry[0]).strip() == "": + # Empty profile specification, warn and skip. + log.warn("Empty profile specification. Skipping.") + return None + profile_path = massage_import_path(str(entry[0]), import_dir) + # Attempt to parse the exit code, if provided. + exit_code = ImportProfileSpec.exit_code + try: + exit_code = str(entry[1]) + except (IndexError, TypeError): + # No exit code was provided. Either there is no value, or the value is None. + log.warn( + f"No exit code provided for profile '{profile_path}', using the default code " + f"'{exit_code}'." + ) + if exit_code != "0": + log.warn("Loading a profile with non-zero exit code.") + profile_info = ImportProfileSpec(profile_path, exit_code) + + # Parse the stat values and add them to respective stats + for stat_value, stat_obj in zip(entry[2:], prof_stats): + # Filter out missing values; supported values are floats and strings. + if stat_value: + stat_obj.value.append(common_kit.try_convert(stat_value, (float, str))) + return profile_info diff --git a/perun/profiles/meson.build b/perun/profiles/meson.build index 228bdfdc7..1c19cf07b 100644 --- a/perun/profiles/meson.build +++ b/perun/profiles/meson.build @@ -3,6 +3,10 @@ perun_profiles_dir = perun_dir / 'profiles' perun_profiles_files = files( '__init__.py', '__init__.pyi', + 'imports.py', + 'stats.py', + 'structs.py', + 'utils.py', ) py3.install_sources( @@ -10,4 +14,7 @@ py3.install_sources( subdir: perun_profiles_dir, ) +subdir('conversions') +subdir('folded') subdir('native') +subdir('polars') diff --git a/perun/profiles/native/__init__.py b/perun/profiles/native/__init__.py index 119cfc95b..c34269827 100644 --- a/perun/profiles/native/__init__.py +++ b/perun/profiles/native/__init__.py @@ -1,7 +1,7 @@ """A package for Perun-native profile. -Contains queries over profiles, storage and loading of the profile in the filesystem, transforming -the profiles, and converting profiles to different formats. +Contains queries over profiles, storage and loading of the profile in the filesystem, +and transforming the profiles. """ import lazy_loader as lazy diff --git a/perun/profiles/native/__init__.pyi b/perun/profiles/native/__init__.pyi index c38531818..2eedf0776 100644 --- a/perun/profiles/native/__init__.pyi +++ b/perun/profiles/native/__init__.pyi @@ -3,22 +3,20 @@ from .factory import ( pass_profile as pass_profile, ) -from .convert import ( - resources_to_pandas_dataframe as resources_to_pandas_dataframe, - models_to_pandas_dataframe as models_to_pandas_dataframe, - to_flame_graph_format as to_flame_graph_format, - to_uid as to_uid, - to_string_line as to_string_line, - plot_data_from_coefficients_of as plot_data_from_coefficients_of, - flatten as flatten, -) - from .helpers import ( + ProfilePath as ProfilePath, + flatten as flatten, + lookup_value as lookup_value, + lookup_param as lookup_param, save_profile as save_profile, generate_profile_name as generate_profile_name, load_list_for_minor_version as load_list_for_minor_version, get_nth_profile_of as get_nth_profile_of, find_profile_entry as find_profile_entry, + generate_units as generate_units, + generate_header_for_profile as generate_header_for_profile, + generate_collector_info as generate_collector_info, + generate_postprocessor_info as generate_postprocessor_info, finalize_profile_for_job as finalize_profile_for_job, to_string as to_string, to_config_tuple as to_config_tuple, @@ -29,19 +27,7 @@ from .helpers import ( merge_resources_of as merge_resources_of, get_default_independent_variable as get_default_independent_variable, get_default_dependent_variable as get_default_dependent_variable, - ProfilePath as ProfilePath, ProfileInfo as ProfileInfo, - ProfileHeaderEntry as ProfileHeaderEntry, - ProfileHeaderTuple as ProfileHeaderTuple, -) - -from .imports import ( - import_perf_from_record as import_perf_from_record, - import_perf_from_script as import_perf_from_script, - import_perf_from_stack as import_perf_from_stack, - import_elk_from_json as import_elk_from_json, - parse_perf_import_entries as parse_perf_import_entries, - parse_metadata as parse_metadata, ) from .query import ( @@ -53,12 +39,3 @@ from .query import ( all_key_values_of as all_key_values_of, unique_model_values_of as unique_model_values_of, ) - -from .stats import ( - ProfileStatComparison as ProfileStatComparison, - StatComparisonResult as StatComparisonResult, - ProfileStat as ProfileStat, - ProfileStatAggregation as ProfileStatAggregation, - aggregate_stats as aggregate_stats, - compare_stats as compare_stats, -) diff --git a/perun/profiles/native/convert.py b/perun/profiles/native/convert.py deleted file mode 100644 index d47315be0..000000000 --- a/perun/profiles/native/convert.py +++ /dev/null @@ -1,245 +0,0 @@ -"""``perun.profile.convert`` is a module which specifies interface for -conversion of profiles from :ref:`profile-spec` to other formats. - -.. _pandas: https://pandas.pydata.org/ - -Run the following in the Python interpreter to extend the capabilities of -Python to different formats of profiles:: - - import perun.profiles.convert - -Combined with ``perun.profile.factory``, ``perun.profile.query`` and e.g. -`pandas`_ library one can obtain efficient interpreter for executing more -complex queries and statistical tests over the profiles. -""" - -from __future__ import annotations - -# Standard Imports -from typing import TYPE_CHECKING, Any -import array -import operator - -# Third-Party Imports -import numpy -import pandas - -# Perun Imports -from perun.postprocess.regression_analysis import transform -from perun.profiles.native import query -from perun.utils import log -from perun.utils.common import common_kit - -if TYPE_CHECKING: - from perun.profiles.native import Profile - - -def resources_to_pandas_dataframe(profile: Profile) -> pandas.DataFrame: - """Converts the profile (w.r.t :ref:`profile-spec`) to format supported by - `pandas`_ library. - - Queries through the resources in the `profile`, and flattens each - key and value to the tabular representation. Refer to `pandas`_ library for - more possibilities how to work with the tabular representation of collected - resources. - - E.g. given `time` and `memory` profiles ``tprof`` and ``mprof`` - respectively, one can obtain the following formats:: - - >>> convert.resources_to_pandas_dataframe(tprof) - amount snapshots uid - 0 0.616s 0 real - 1 0.500s 0 user - 2 0.125s 0 sys - - >>> convert.resources_to_pandas_dataframe(mmprof) - address amount snapshots subtype trace type - 0 19284560 4 0 malloc malloc:unreachabl... memory - 1 19284560 0 0 free free:unreachable:... memory - - uid uid:function uid:line uid:source - 0 main:../memo...:22 main 22 ../memory_collect_test.c - 1 main:../memo...:27 main 27 ../memory_collect_test.c - - :param profile: dictionary with profile w.r.t. :ref:`profile-spec` - :returns: converted profile to ``pandas.DataFramelist`` with resources - flattened as a pandas dataframe - """ - # Since some keys may be missing in the resources, we consider the possible fields - resource_keys = list(profile.all_resource_fields()) - values: dict[str, list[Any] | array.array[float] | array.array[int]] = { - key: [] for key in resource_keys - } - values["snapshots"] = array.array("I") - - # All resources at this point should be flat - for snapshot, resource in log.progress( - profile.all_resources(flatten_values=True), "Converting To Pandas" - ): - values["snapshots"].append(snapshot) - for resource_key in resource_keys: - values[resource_key].append(resource.get(resource_key, numpy.nan)) - - return pandas.DataFrame(values) - - -def models_to_pandas_dataframe(profile: Profile) -> pandas.DataFrame: - """Converts the models of profile (w.r.t :ref:`profile-spec`) to format - supported by `pandas`_ library. - - Queries through all the models in the `profile`, and flattens each - key and value to the tabular representation. Refer to `pandas`_ library for - more possibilities how to work with the tabular representation of models. - - :param profile: dictionary with profile w.r.t. :ref:`profile-spec` - :returns: converted models of profile to ``pandas.DataFramelist`` - """ - # Note that we need to this inefficiently, because some keys can be missing in resources - model_keys = list(query.all_model_fields_of(profile)) - values: dict[str, list[Any]] = {key: [] for key in model_keys} - - for _, model in log.progress(profile.all_models(), description="Converting To Pandas"): - flattened_resources = dict(list(query.all_items_of(model))) - for model_key in model_keys: - values[model_key].append(flattened_resources.get(model_key, numpy.nan)) - - return pandas.DataFrame(values) - - -def to_flame_graph_format( - profile: Profile, - profile_key: str = "amount", - minimize: bool = False, - squash_unknown: bool = True, -) -> list[str]: - """Transforms the **memory** profile w.r.t. :ref:`profile-spec` into the - format supported by perl script of Brendan Gregg. - - .. _Brendan Gregg's homepage: https://www.brendangregg.com/index.html - - :ref:`views-flame-graph` can be used to visualize the inclusive consumption - of resources w.r.t. the call trace of the resource. It is useful for fast - detection, which point at the trace is the hotspot (or bottleneck) in the - computation. Refer to :ref:`views-flame-graph` for full capabilities of our - Wrapper. For more information about flame graphs itself, please check - `Brendan Gregg's homepage`_. - - Example of format is as follows:: - - >>> print(''.join(convert.to_folded_format(memprof))) - malloc()~unreachable~0;main()~/home/user/dev/test.c~45 4 - valloc()~unreachable~0;main()~/home/user/dev/test.c~75;__libc_start_main()~unreachable~0 8 - main()~/home/user/dev/test02.c~79 156 - - Each line corresponds to some collected resource (in this case amount of - allocated memory) preceeded by its trace (i.e. functions or other unique - identifiers joined using ``;`` character). - - :param profile: the memory profile - :param profile_key: key that is used to obtain the data - :param minimize: minimizes the uids - :param squash_unknown: whether recursive [unknown] frames should be squashed into a single one - :returns: list of lines, each representing one allocation call stack - """ - stacks = [] - for _, snapshot in profile.all_snapshots(): - for alloc in snapshot: - if "subtype" not in alloc.keys() or alloc["subtype"] != "free": - # Workaround for total time used in some collectors, so it is not outputted - if alloc["uid"] != "%TOTAL_TIME%" and profile_key in alloc: - stack = alloc.get("trace", []) - stack.append(alloc["uid"]) - stack_str = "" - unknown_cnt = 0 - for frame in reversed(stack): - line = to_uid(frame, minimize) - if squash_unknown: - # Fold unknown towers - if line == "[unknown]": - unknown_cnt += 1 - continue - elif unknown_cnt > 0: - stack_str += "[unknown[squashed]];" - unknown_cnt = 0 - stack_str += f"{line};" - # The unknown frame might have been the last one, in which case the frame was - # not yet added to the string. - if unknown_cnt > 0: - stack_str += "[unknown[squashed]];" - if stack_str: - stacks.append(f"{stack_str[:-1]} {alloc[profile_key]}\n") - return stacks - - -def to_uid(record: dict[str, Any] | str, minimize: bool = False) -> str: - """Retrieves uid from record - - :param record: record for which we are retrieving uid - :return: single string representing uid - """ - if isinstance(record, str): - result = record - else: - result = to_string_line(record) - return common_kit.hide_generics(result) if minimize else result - - -def to_string_line(frame: dict[str, Any] | str) -> str: - """Create string representing call stack's frame - - :param frame: call stack's frame - :return: line representing call stack's frame - """ - if isinstance(frame, str): - return frame - elif "function" in frame.keys() and "source" in frame.keys() and "line" in frame.keys(): - return f"{frame['function']}()~{frame['source']}~{frame['line']}" - else: - assert "func" in frame.keys() - return f"{frame['func']}" - - -def plot_data_from_coefficients_of(model: dict[str, Any]) -> dict[str, Any]: - """Transform coefficients computed by - :ref:`postprocessors-regression-analysis` into dictionary of points, - plotable as a function or curve. This function serves as a public wrapper - over regression analysis transformation function. - - :param model: the models dictionary from profile (refer to - :pkey:`models`) - :return: updated models dictionary extended with `plot_x` and - `plot_y` lists - """ - model.update(transform.coefficients_to_points(**model)) - return model - - -def flatten(flattened_value: Any) -> Any: - """Converts the value to something that can be used as one value. - - Flattens the value to single level, lists are processed to comma separated representation and - rest is left as it is. - TODO: Add caching - - :param flattened_value: value that is flattened - :returns: either decimal, string, or something else - """ - # Dictionary is processed recursively according to the all items that are nested - if isinstance(flattened_value, dict): - nested_values = [] - for key, value in query.all_items_of(flattened_value): - # Add one level of hierarchy with ':' - nested_values.append((key, value)) - # Return the overall key as joined values of its nested stuff, - # only if root is not a list (i.e. root key is not int = index)! - nested_values.sort(key=common_kit.uid_getter) - return ":".join(map(str, map(operator.itemgetter(1), nested_values))) - # Lists are merged as comma separated keys - elif isinstance(flattened_value, list): - return ",".join( - ":".join(str(nested_value[1]) for nested_value in query.flattened_values(i, lv)) - for (i, lv) in enumerate(flattened_value) - ) - # Rest of the values are left as they are - else: - return flattened_value diff --git a/perun/profiles/native/factory.py b/perun/profiles/native/factory.py index 0895a94da..b024d31ca 100644 --- a/perun/profiles/native/factory.py +++ b/perun/profiles/native/factory.py @@ -9,22 +9,23 @@ from __future__ import annotations # Standard Imports -from collections.abc import MutableMapping -from typing import Any, Iterator, Iterable, TYPE_CHECKING, Literal import collections +from collections.abc import MutableMapping import itertools import operator +from typing import Any, Iterable, Iterator, TYPE_CHECKING # Third-Party Imports import click # Perun Imports -from perun import check as check +from perun import check from perun.logic import config -from perun.profiles.native import query, helpers, convert, stats +from perun.profiles import stats, structs +from perun.profiles.native import helpers, query from perun.utils import log -from perun.utils.structs import postprocess_structs from perun.utils.common import common_kit +from perun.utils.structs import postprocess_structs if TYPE_CHECKING: from perun.utils.structs.common_structs import ModelRecord @@ -96,14 +97,15 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self._storage[key] = value config.runtime().append("context.profiles", self) - def apply(self, agg: Literal["sum", "min", "max", "avg", "mean", "med", "median"]) -> None: + def apply(self, agg: common_kit.Aggregations) -> None: """Applies aggregation function to each counted resource - :param agg: name of the aggreagation function + :param agg: the aggregation function to use """ + aggregation: common_kit.AggregationFunc = common_kit.get_aggregation_callable(agg) for res in self._storage["resources"].values(): for item, val in res.items(): - res[item] = [common_kit.aggregate_list(val, agg)] + res[item] = [aggregation(val)] def update_resources( self, @@ -193,8 +195,8 @@ def register_resource_type(self, uid: str, persistent_properties: tuple[Any, ... :param persistent_properties: tuple of persistent properties :return: uid corresponding to the tuple of persistent properties """ - property_key = str(convert.flatten(persistent_properties)) - uid_key = convert.flatten(uid) + property_key = str(helpers.flatten(persistent_properties)) + uid_key = helpers.flatten(uid) if property_key not in self._tuple_to_resource_type_map.keys(): new_type = f"{uid_key}#{self._uid_counter[uid_key]}" self._tuple_to_resource_type_map[property_key] = new_type @@ -327,6 +329,19 @@ def all_resources(self, flatten_values: bool = False) -> Iterable[tuple[int, dic # In case we have only persistent properties yield persistent_properties.get("snapshot", 0), persistent_properties + def get_kperf_resources(self) -> Iterable[dict[str, Any]]: + """A generator of kperf resources. + + A simplified version of `all_resources` which does not aggregates amounts for later + custom aggregation. + + :return: a generator of kperf resources. + """ + for resource_type, resources in self._storage["resources"].items(): + persistent_properties = self._storage["resource_type_map"][resource_type] + persistent_properties.update(resources) + yield persistent_properties + def all_resource_fields(self) -> set[str]: """Generator for iterating through all the fields (both flattened and original) that are occurring in the resources. @@ -462,13 +477,13 @@ def all_stats(self) -> Iterable[stats.ProfileStat]: for stat in self._storage.get("stats", {}): yield stats.ProfileStat.from_profile(stat) - def all_metadata(self) -> Iterable[helpers.ProfileHeaderEntry]: + def all_metadata(self) -> Iterable[structs.ProfileMetadataEntry]: """Iterates through all the metadata records in the profile. :return: iterable of all metadata records """ for entry in self._storage.get("metadata", {}): - yield helpers.ProfileHeaderEntry.from_profile(entry) + yield structs.ProfileMetadataEntry.from_profile(entry) # TODO: discuss the intent of __len__ and possibly merge? def resources_size(self) -> int: diff --git a/perun/profiles/native/helpers.py b/perun/profiles/native/helpers.py index 435708fe6..0f55f17c3 100644 --- a/perun/profiles/native/helpers.py +++ b/perun/profiles/native/helpers.py @@ -1,53 +1,41 @@ -"""``perun.profile.factory`` specifies collective interface for basic -manipulation with profiles. - -The format of profiles is w.r.t. :ref:`profile-spec`. This module contains -helper functions for loading and storing of the profiles either in the -persistent memory or in filesystem (in this case, the profile is in -uncompressed format). +"""This module contains helper functions for loading and storing of native profiles. .. _Python JSON library: https://docs.python.org/3.7/library/json.html -For further manipulations refer either to :ref:`profile-conversion-api` -(implemented in ``perun.profile.convert`` module) or :ref:`profile-query-api` -(implemented in ``perun.profile.query module``). For full specification how to -handle the JSON objects in Python refer to `Python JSON library`_. +For full specification how to handle the JSON objects in Python refer to `Python JSON library`_. """ from __future__ import annotations # Standard Imports import contextlib -import dataclasses import json import operator import os from pathlib import Path import re import time -from typing import Any, TYPE_CHECKING, Union +from typing import Any, TYPE_CHECKING # Third-Party Imports # Perun Imports from perun.logic import commands, config, index, pcs, store from perun.profiles import native as profiles +from perun.profiles.native import query from perun.utils import log as perun_log, streams from perun.utils.common import common_kit -from perun.utils.external import environment, commands as external_commands from perun.utils.exceptions import ( InvalidParameterException, TagOutOfRangeException, ) -from perun.utils.structs.common_structs import Unit, Executable, Job, SortOrder, MinorVersion +from perun.utils.external import commands as external_commands, environment +from perun.utils.structs.common_structs import Executable, Job, MinorVersion, SortOrder, Unit from perun.vcs import vcs_kit if TYPE_CHECKING: import types -# A tuple representation of the ProfileHeaderEntry object -ProfileHeaderTuple = tuple[str, Union[str, float], str, dict[str, Union[str, float]]] - PROFILE_COUNTER: int = 0 DEFAULT_SORT_KEYS: list[str] = ["time", "stem", "copy"] @@ -150,6 +138,37 @@ def finalize(self, profile: profiles.Profile) -> ProfilePath: return self +def flatten(flattened_value: Any) -> Any: + """Converts the value to something that can be used as one value. + + Flattens the value to single level, lists are processed to comma separated representation and + rest is left as it is. + TODO: Add caching + + :param flattened_value: value that is flattened + :returns: either decimal, string, or something else + """ + # Dictionary is processed recursively according to the all items that are nested + if isinstance(flattened_value, dict): + nested_values = [] + for key, value in query.all_items_of(flattened_value): + # Add one level of hierarchy with ':' + nested_values.append((key, value)) + # Return the overall key as joined values of its nested stuff, + # only if root is not a list (i.e. root key is not int = index)! + nested_values.sort(key=common_kit.uid_getter) + return ":".join(map(str, map(operator.itemgetter(1), nested_values))) + # Lists are merged as comma separated keys + elif isinstance(flattened_value, list): + return ",".join( + ":".join(str(nested_value[1]) for nested_value in query.flattened_values(i, lv)) + for (i, lv) in enumerate(flattened_value) + ) + # Rest of the values are left as they are + else: + return flattened_value + + def lookup_value(container: dict[str, str] | profiles.Profile, key: str, missing: str) -> str: """Helper function for getting the key from the container. If it is not present in the container, or it is empty string or empty object, the function should return the missing @@ -764,54 +783,3 @@ def is_compatible_with_profile(self, profile: profiles.Profile) -> bool: "checksum", "source", ] - - -@dataclasses.dataclass -class ProfileHeaderEntry: - """A representation of a single profile header entry. - - :ivar name: the name (key) of the header entry - :ivar value: the value of the header entry - :ivar description: detailed description of the header entry - :ivar details: nested key: value data - """ - - name: str - value: str | float - description: str = "" - details: dict[str, str | float] = dataclasses.field(default_factory=dict) - - @classmethod - def from_string(cls, header: str) -> ProfileHeaderEntry: - """Constructs a `ProfileHeaderRecord` object from a string representation. - - :param header: the string representation of a header entry - - :return: the constructed ProfileHeaderRecord object - """ - split = header.split("|") - name = split[0] - value = common_kit.try_convert(split[1] if len(split) > 1 else "[empty]", [float, str]) - desc = split[2] if len(split) > 2 else ProfileHeaderEntry.description - details: dict[str, str | float] = {} - for detail in split[4:]: - detail_key, detail_value = detail.split(maxsplit=1) - details[detail_key] = common_kit.try_convert(detail_value, [float, str]) - return cls(name, value, desc, details) - - @classmethod - def from_profile(cls, header: dict[str, Any]) -> ProfileHeaderEntry: - """Constructs a ProfileHeaderEntry object from a dictionary representation used in Profile. - - :param header: the dictionary representation of a header entry - - :return: the constructed ProfileHeaderEntry object - """ - return cls(**header) - - def as_tuple(self) -> ProfileHeaderTuple: - """Converts the header object into a tuple. - - :return: the tuple representation of a header entry - """ - return self.name, self.value, self.description, self.details diff --git a/perun/profiles/native/imports.py b/perun/profiles/native/imports.py deleted file mode 100755 index 4cd1702e3..000000000 --- a/perun/profiles/native/imports.py +++ /dev/null @@ -1,621 +0,0 @@ -"""Functions for importing Profile from different formats""" - -from __future__ import annotations - -# Standard Imports -from collections import defaultdict -from dataclasses import asdict, dataclass -import gzip -import json -import os -from pathlib import Path -import subprocess -import sys -from typing import Any, Sequence - -# Third-Party Imports -import polars as pl - -# Perun Imports -from perun.profiles import native as profile -from perun.collect.kperf import parser -from perun.logic import config, pcs -from perun.utils import log, streams -from perun.utils.common import script_kit, common_kit -from perun.utils.external import commands as external_commands, environment -from perun.utils.structs.common_structs import MinorVersion -from perun.vcs import vcs_kit - - -@dataclass -class PerfProfileSpec: - """A representation of a perf profile record to import. - - :ivar path: the absolute path to the perf - :ivar exit_code: the exit code of the profile collection process. - """ - - path: Path - exit_code: str = "?" - - -@vcs_kit.lookup_minor_version -def import_perf_from_record( - import_entries: list[str], - stats_headers: str | None, - minor_version: str, - with_sudo: bool = False, - **kwargs: Any, -) -> None: - """Imports profiles collected by `perf record` command. - - First, the function parses all the perf import entries and stats headers, and then it runs - the perf script + parser script for each entry to generate the profile. - - :param import_entries: a collection of import entries (profiles or CSV files). - :param stats_headers: CLI-specified stats headers. - :param minor_version: minor version corresponding to the imported profiles. - :param with_sudo: indication whether the data were collected with sudo. - :param kwargs: rest of the parameters. - """ - # TODO: tag commands with (platform, system, ...) requirements and have a unified mechanism - # to detect failures - if sys.platform == "darwin": - log.error("Import from perf record is not supported on macOS platform.") - parse_script = script_kit.get_script("stackcollapse-perf.pl") - import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) - profiles, stats = parse_perf_import_entries(import_entries, import_dir, stats_headers) - resources = [] - - for imported_file in profiles: - perf_script_command = ( - f"{'sudo ' if with_sudo else ''}perf script -i {imported_file.path} | {parse_script}" - ) - try: - out, _ = external_commands.run_safely_external_command(perf_script_command) - log.minor_success( - f"Raw data from {log.path_style(str(imported_file.path))}", "collected" - ) - except subprocess.CalledProcessError as err: - log.minor_fail( - f"Raw data from {log.path_style(str(imported_file.path))}", "not collected" - ) - log.error(f"Cannot load data due to: {err}") - resources.extend(parser.parse_events(out.decode("utf-8").split("\n"))) - log.minor_success(log.path_style(str(imported_file.path)), "imported") - minor_version_info = pcs.vcs().get_minor_version_info(minor_version) - import_perf_profile( - profiles, stats, resources, minor_version_info, with_sudo=with_sudo, **kwargs - ) - - -@vcs_kit.lookup_minor_version -def import_perf_from_script( - import_entries: list[str], - stats_headers: str | None, - minor_version: str, - **kwargs: Any, -) -> None: - """Imports profiles collected by `perf record | perf script` command. - - First, the function parses all the perf import entries and stats headers, and then it runs - the parser script for each entry to generate the profile. - - :param import_entries: a collection of import entries (profiles or CSV files). - :param stats_headers: CLI-specified stats headers. - :param minor_version: minor version corresponding to the imported profiles. - :param kwargs: rest of the parameters. - """ - parse_script = script_kit.get_script("stackcollapse-perf.pl") - import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) - profiles, stats = parse_perf_import_entries(import_entries, import_dir, stats_headers) - resources = [] - - for imported_file in profiles: - perf_script_command = f"cat {imported_file.path} | {parse_script}" - out, _ = external_commands.run_safely_external_command(perf_script_command) - log.minor_success(f"Raw data from {log.path_style(str(imported_file.path))}", "collected") - resources.extend(parser.parse_events(out.decode("utf-8").split("\n"))) - log.minor_success(log.path_style(str(imported_file.path)), "imported") - minor_version_info = pcs.vcs().get_minor_version_info(minor_version) - import_perf_profile(profiles, stats, resources, minor_version_info, **kwargs) - - -@vcs_kit.lookup_minor_version -def import_perf_from_stack( - import_entries: list[str], - stats_headers: str | None, - minor_version: str, - **kwargs: Any, -) -> None: - """Imports profiles collected by `perf record | perf script | stackcollapse-perf.pl` command. - - First, the function parses all the perf import entries and stats headers, and then it parses - each entry to generate the profile. - - :param import_entries: a collection of import entries (profiles or CSV files). - :param stats_headers: CLI-specified stats headers. - :param minor_version: minor version corresponding to the imported profiles. - :param kwargs: rest of the parameters. - """ - import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) - profiles, stats = parse_perf_import_entries(import_entries, import_dir, stats_headers) - resources = [] - - for imported_profile in profiles: - out = load_perf_file(imported_profile.path) - resources.extend(parser.parse_events(out.split("\n"))) - log.minor_success(log.path_style(str(imported_profile.path)), "imported") - minor_version_info = pcs.vcs().get_minor_version_info(minor_version) - import_perf_profile(profiles, stats, resources, minor_version_info, **kwargs) - - -@vcs_kit.lookup_minor_version -def import_elk_from_json( - import_entries: list[str], - metadata: tuple[str, ...], - minor_version: str, - **kwargs: Any, -) -> None: - """Imports the ELK stored data from the json data. - - The loading expects the json files to be in form of `{'queries': []}`. - - :param import_entries: list of filenames with elk data. - :param metadata: CLI-supplied additional metadata. Metadata specified in JSON take precedence. - :param minor_version: minor version corresponding to the imported profiles. - :param kwargs: rest of the parameters. - """ - import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) - resources: list[dict[str, Any]] = [] - # Load the CLI-supplied metadata, if any - elk_metadata: dict[str, profile.ProfileHeaderEntry] = { - data.name: data for data in parse_metadata(metadata, import_dir) - } - - for elk_file in import_entries: - elk_file_path = _massage_import_path(elk_file, import_dir) - with streams.safely_open_and_log(elk_file_path, "r", fatal_fail=True) as elk_handle: - imported_json = json.load(elk_handle) - assert ( - "queries" in imported_json.keys() - ), "expected the JSON to contain list of dictionaries in 'queries' key" - r, m = extract_from_elk(imported_json["queries"]) - resources.extend(r) - # Possibly overwrite CLI-supplied metadata when identical keys are found - elk_metadata.update(m) - log.minor_success(log.path_style(str(elk_file_path)), "imported") - minor_version_info = pcs.vcs().get_minor_version_info(minor_version) - import_elk_profile(resources, elk_metadata, minor_version_info, **kwargs) - - -def import_perf_profile( - profiles: list[PerfProfileSpec], - stats: list[profile.ProfileStat], - resources: list[dict[str, Any]], - minor_version: MinorVersion, - **kwargs: Any, -) -> None: - """Constructs the profile for perf-collected data and saves them to jobs or index. - - :param profiles: a collection of specifications of the profiles that are being imported. - :param stats: a collection of profiles statistics that should be associated with the profile. - :param resources: a collection of parsed resources. - :param minor_version: minor version corresponding to the imported profiles. - :param kwargs: rest of the parameters. - """ - import_dir = Path(config.lookup_key_recursively("import.dir", os.getcwd())) - prof = profile.Profile( - { - "global": { - "time": "???", - "resources": resources, - }, - "origin": minor_version.checksum, - "machine": get_machine_info(kwargs.get("machine_info", ""), import_dir), - "metadata": [ - asdict(data) for data in parse_metadata(kwargs.get("metadata", tuple()), import_dir) - ], - "stats": [asdict(stat) for stat in stats], - "header": { - "type": "time", - "cmd": kwargs.get("cmd", ""), - "exitcode": [p.exit_code for p in profiles], - "workload": kwargs.get("workload", ""), - "label": kwargs.get("profile_label", ""), - "units": {"time": "sample"}, - }, - "collector_info": { - "name": "kperf", - "params": { - "with_sudo": kwargs.get("with_sudo", False), - "warmup": kwargs.get("warmup", 0), - "repeat": len(profiles), - }, - }, - "postprocessors": [], - } - ) - profile.save_profile(prof, kwargs.get("profile_path"), minor_version) - - -def import_elk_profile( - resources: list[dict[str, Any]], - metadata: dict[str, profile.ProfileHeaderEntry], - minor_version: MinorVersion, - **kwargs: Any, -) -> None: - """Constructs the profile for elk-stored data and saves them to jobs or index. - - :param resources: list of parsed resources. - :param metadata: parts of the profiles that will be stored as metadata in the profile. - :param minor_version: minor version corresponding to the imported profiles. - :param kwargs: rest of the parameters. - """ - prof = profile.Profile( - { - "global": { - "time": "???", - "resources": resources, - }, - "origin": minor_version.checksum, - "metadata": [asdict(data) for data in metadata.values()], - "machine": extract_machine_info_from_elk_metadata(metadata), - "header": { - "type": "time", - "cmd": kwargs.get("cmd", ""), - "exitcode": "?", - "workload": kwargs.get("workload", ""), - "label": kwargs.get("profile_label", ""), - "units": {"time": "sample"}, - }, - "collector_info": { - "name": "???", - "params": {}, - }, - "postprocessors": [], - } - ) - profile.save_profile(prof, kwargs.get("profile_path"), minor_version) - - -def load_perf_file(filepath: Path) -> str: - """Tests if the file is packed by gzip and unpacks it, otherwise reads it as a text file. - - :param filepath: path to the perf file. - - :return: the content of the file. - """ - if filepath.suffix.lower() == ".gz": - with streams.safely_open_and_log(filepath, "rb", fatal_fail=True) as gz_handle: - header = gz_handle.read(2) - gz_handle.seek(0) - assert header == b"\x1f\x8b" - with gzip.GzipFile(fileobj=gz_handle) as gz: - return gz.read().decode("utf-8") - with streams.safely_open_and_log( - filepath, "r", fatal_fail=True, encoding="utf-8" - ) as txt_handle: - return txt_handle.read() - - -def extract_from_elk( - elk_query: list[dict[str, Any]], -) -> tuple[list[dict[str, Any]], dict[str, profile.ProfileHeaderEntry]]: - """For the given elk query, extracts resources and metadata. - - For metadata, we consider any key that has only single value through the profile, - and is not linked to keywords `metric` or `benchmarking`. - For resources, we consider anything that is not identified as metadata. - - :param elk_query: query from the elk in form of list of resource. - - :return: list of resources and metadata. - """ - res_counter = defaultdict(set) - for res in elk_query: - for key, val in res.items(): - res_counter[key].add(val) - metadata_keys = { - k - for (k, v) in res_counter.items() - if not k.startswith("metric") and not k.startswith("benchmarking") and len(v) == 1 - } - - metadata = {k: profile.ProfileHeaderEntry(k, res_counter[k].pop()) for k in metadata_keys} - resources = [ - { - k: common_kit.try_convert(v, [int, float, str]) - for k, v in res.items() - if k not in metadata_keys - } - for res in elk_query - ] - # We register uid - for res in resources: - res["uid"] = res["metric.name"] - res["benchmarking.time"] = res["benchmarking.end-ts"] - res["benchmarking.start-ts"] - res.pop("benchmarking.end-ts") - res.pop("benchmarking.start-ts") - return resources, metadata - - -def get_machine_info(machine_info: str, import_dir: Path) -> dict[str, Any]: - """Returns machine info either from an input file or constructs it from the environment. - - :param machine_info: relative or absolute path to machine specification JSON file. In case of - an empty string, the machine info will be constructed from the environment. - :param import_dir: import directory where to look for the machine info file if the provided - path is relative. - - :return: parsed or constructed machine specification. - """ - if machine_info: - # Some machine info path has been provided. - info_path = _massage_import_path(machine_info, import_dir) - with streams.safely_open_and_log( - info_path, "r", fail_msg="not found, generating info from environment instead" - ) as info_handle: - if info_handle is not None: - json_data = json.load(info_handle) - log.minor_success(log.path_style(str(info_path)), "parsed") - return json_data - # No machine info file might have been provided, or an invalid path was specified. - # Construct the machine info from the current machine. - return environment.get_machine_specification() - - -def extract_machine_info_from_elk_metadata( - metadata: dict[str, profile.ProfileHeaderEntry], -) -> dict[str, Any]: - """Extracts the parts of the profile that correspond to machine info. - - Note that not many is collected from the ELK formats, and it can vary greatly, - hence, most of the machine specification and environment should be in metadata instead. - - :param metadata: metadata extracted from the ELK profiles. - - :return: machine info extracted from the profiles. - """ - machine_info: dict[str, Any] = { - "architecture": metadata.get("machine.arch", profile.ProfileHeaderEntry("", "?")).value, - "system": str( - metadata.get("machine.os", profile.ProfileHeaderEntry("", "?")).value - ).capitalize(), - "release": metadata.get( - "extra.machine.platform", profile.ProfileHeaderEntry("", "?") - ).value, - "host": metadata.get("machine.hostname", profile.ProfileHeaderEntry("", "?")).value, - "cpu": { - "physical": "?", - "total": metadata.get("machine.cpu-cores", profile.ProfileHeaderEntry("", "?")).value, - "frequency": "?", - }, - "memory": { - "total_ram": metadata.get("machine.ram", profile.ProfileHeaderEntry("", "?")).value, - "swap": "?", - }, - "boot_info": "?", - "mem_details": {}, - "cpu_details": [], - } - - return machine_info - - -def parse_metadata(metadata: tuple[str, ...], import_dir: Path) -> list[profile.ProfileHeaderEntry]: - """Parse the metadata entries from CLI and convert them to our internal representation. - - :param import_dir: the import directory to use for relative metadata file paths. - :param metadata: a collection of metadata entries or JSON files. - - :return: a collection of parsed and converted metadata objects - """ - p_metadata: list[profile.ProfileHeaderEntry] = [] - # Normalize the metadata string for parsing and/or opening the file - for metadata_str in map(str.strip, metadata): - if metadata_str.lower().endswith(".json"): - # Update the metadata collection with entries from the json file - p_metadata.extend(_parse_metadata_json(_massage_import_path(metadata_str, import_dir))) - else: - # Add a single metadata entry parsed from its string representation - try: - p_metadata.append(profile.ProfileHeaderEntry.from_string(metadata_str)) - except TypeError: - log.warn(f"Ignoring invalid profile metadata string '{metadata_str}'.") - return p_metadata - - -def _parse_metadata_json(metadata_path: Path) -> list[profile.ProfileHeaderEntry]: - """Parse a metadata JSON file into the metadata objects. - - If the JSON file contains nested dictionaries, the hierarchical keys will be flattened. - - :param metadata_path: the path to the metadata JSON. - - :return: a collection of parsed metadata objects. - """ - with streams.safely_open_and_log( - metadata_path, "r", fail_msg="not found, skipping" - ) as metadata_handle: - if metadata_handle is None: - return [] - # Make sure we flatten the input - metadata_list = [ - profile.ProfileHeaderEntry(k, v) - for k, v in profile.all_items_of(json.load(metadata_handle)) - ] - log.minor_success(log.path_style(str(metadata_path)), "parsed") - return metadata_list - - -def parse_perf_import_entries( - import_entries: list[str], import_dir: Path, cli_stats_headers: str | None -) -> tuple[list[PerfProfileSpec], list[profile.ProfileStat]]: - """Parses perf import entries and stats. - - An import entry is either a profile entry - - 'profile_path[,[,]+]' - - where each stat value corresponds to a stats header specified in the cli_stats_headers, or - a CSV file entry - - 'file_path.csv' - - where the CSV file is in the format - - Profile,Exit_code[,stat-header1]+ - profile_path[,[,]+] - ... - - that combines the --stats-headers option and profile entries. Stats specified in a CSV file - apply only to profile entries in the JSON file. Similarly, CLI-specified stats apply only to - profile entries specified directly in CLI. - - :param import_entries: the perf import entries to parse. - :param import_dir: the import directory to use for relative profile paths. - :param cli_stats_headers: the stats headers specified in CLI. - - :return: parsed profiles and stats. - """ - stats = [] - if cli_stats_headers is not None: - stats = [ - profile.ProfileStat.from_string(*stat.split("|")) - for stat in cli_stats_headers.split(",") - ] - cli_stats_len = len(stats) - profiles: list[PerfProfileSpec] = [] - - for record in import_entries: - if record.strip().lower().endswith(".csv"): - # The input is a csv file - _parse_perf_import_csv(record, import_dir, profiles, stats) - elif ( - profile_spec := _parse_perf_entry(record.split(","), import_dir, stats[:cli_stats_len]) - ) is not None: - # The input is a string profile spec - profiles.append(profile_spec) - return profiles, stats - - -def _parse_perf_import_csv( - csv_file: str, - import_dir: Path, - profiles: list[PerfProfileSpec], - stats: list[profile.ProfileStat], -) -> None: - """Parse stats headers and perf import entries in a CSV file. - - :param csv_file: the CSV file to parse. - :param import_dir: the import directory to use for relative profile file paths. - :param profiles: profile specifications that will be extended with the parsed profiles. - :param stats: profile stats that will be merged with the CSV stats. - """ - csv_path = _massage_import_path(csv_file, import_dir) - with streams.safely_open_and_log(csv_path, "r", fatal_fail=True) as csvfile_handle: - try: - import_csv = pl.read_csv(csvfile_handle, comment_prefix="#") - except pl.exceptions.NoDataError: - # Empty CSV file, skip - log.warn(f"Empty file {csv_path}. Skipping.") - return - except pl.exceptions.ComputeError as e: - # The CSV file contains spurious columns, or is otherwise weirdly formatted. - # We log the issue so the user is aware of it, and we retry parsing it less strictly. - log.warn( - f"Encountered error when processing {csv_path}: {str(e).splitlines()[0]}." - " Attempting recovery." - ) - import_csv = pl.read_csv(csvfile_handle, comment_prefix="#", truncate_ragged_lines=True) - # Parse the stats headers - csv_stats: list[profile.ProfileStat] = [ - profile.ProfileStat.from_string(*stat_definition.split("|")) - for stat_definition in import_csv.columns[2:] - ] - # Parse the remaining rows that represent profile specifications and filter invalid ones - profiles.extend( - record - for row in import_csv.iter_rows() - if (record := _parse_perf_entry(row, import_dir, csv_stats)) is not None - ) - # Merge CSV stats with the other stats - for csv_stat in csv_stats: - _merge_stats(csv_stat, stats) - log.minor_success(log.path_style(str(csv_path)), "parsed") - - -def _parse_perf_entry( - entry: Sequence[str | float], import_dir: Path, stats: list[profile.ProfileStat] -) -> PerfProfileSpec | None: - """Parse a single perf profile import entry. - - :param entry: the perf import entry to parse. - :param import_dir: the import directory to use for relative profile file paths. - :param stats: the profile stats associated with this profile. - - :return: the parsed profile, or None if the import entry is invalid. - """ - # Attempt to parse the profile specification. - if len(entry) == 0 or entry[0] is None or str(entry[0]).strip() == "": - # Empty profile specification, warn and skip. - log.warn("Empty profile specification. Skipping.") - return None - profile_path = _massage_import_path(str(entry[0]), import_dir) - # Attempt to parse the exit code, if provided. - exit_code = PerfProfileSpec.exit_code - try: - exit_code = str(entry[1]) - except (IndexError, TypeError): - # No exit code was provided. Either there is no value, or the value is None. - log.warn( - f"No exit code provided for profile '{profile_path}', using the default code " - f"'{exit_code}'." - ) - if exit_code != "0": - log.warn("Loading a profile with non-zero exit code.") - profile_info = PerfProfileSpec(profile_path, exit_code) - - # Parse the stat values and add them to respective stats - for stat_value, stat_obj in zip(entry[2:], stats): - # Filter out missing values; supported values are floats and strings. - if stat_value: - stat_obj.value.append(common_kit.try_convert(stat_value, (float, str))) - return profile_info - - -def _merge_stats(new_stat: profile.ProfileStat, into_stats: list[profile.ProfileStat]) -> None: - """Merge a new profile stat values into the current profile stats. - - If an existing stat with the same name exists, the values of both stats are merged. If no such - stat is found, the new stat is added to the collection of current stats. - - :param new_stat: the new profile stat to merge. - :param into_stats: the current collection of profile stats. - """ - for stat in into_stats: - if new_stat.name == stat.name: - # We found a stat with a matching name, merge - stat.merge_with(new_stat) - return - # There is no stat to merge with, extend the current collection of stats - into_stats.append(new_stat) - - -def _massage_import_path(path_str: str, import_dir: Path) -> Path: - """Massages path strings into a unified path format. - - First, the path string is stripped of leading and trailing whitespaces. - Next, absolute paths are kept as is, while relative paths are prepended with the - provided import directory. - - :param import_dir: the import directory to use for relative paths. - :param path_str: the path string to massage. - - :return: the massaged path. - """ - path: Path = Path(path_str.strip()) - if path.is_absolute(): - return path - return import_dir / path diff --git a/perun/profiles/native/meson.build b/perun/profiles/native/meson.build index c84136a81..acff0aab4 100644 --- a/perun/profiles/native/meson.build +++ b/perun/profiles/native/meson.build @@ -3,12 +3,9 @@ perun_native_profile_dir = perun_profiles_dir / 'native' perun_native_profile_files = files( '__init__.py', '__init__.pyi', - 'convert.py', 'factory.py', 'helpers.py', - 'imports.py', 'query.py', - 'stats.py', ) py3.install_sources( diff --git a/perun/profiles/native/query.py b/perun/profiles/native/query.py index 41e4793f1..61d615111 100644 --- a/perun/profiles/native/query.py +++ b/perun/profiles/native/query.py @@ -1,4 +1,4 @@ -"""``perun.profile.query`` is a module which specifies interface for issuing +"""``perun.profiles.native.query`` is a module which specifies interface for issuing queries over the profiles w.r.t :ref:`profile-spec`. .. _Pandas library: https://docs.python.org/3.7/library/json.html @@ -6,9 +6,9 @@ Run the following in the Python interpreter to extend the capabilities of profile to query over profiles, iterate over resources or models, etc.:: - import perun.profiles.query + import perun.profiles.native.query -Combined with ``perun.profile.factory``, ``perun.profile.convert`` and e.g. +Combined with ``perun.profiles.native.factory``, ``perun.profile.conversions`` and e.g. `Pandas library`_ one can obtain efficient interpreter for executing more complex queries and statistical tests over the profiles. """ @@ -16,9 +16,9 @@ from __future__ import annotations # Standard Imports -from typing import Any, TYPE_CHECKING, Iterable import numbers import operator +from typing import Any, Iterable, TYPE_CHECKING # Third-Party Imports @@ -26,7 +26,7 @@ from perun.utils.common import common_kit if TYPE_CHECKING: - from perun.profile import Profile + from perun.profiles.native import Profile def flattened_values(root_key: Any, root_value: Any) -> Iterable[tuple[str, str | float]]: diff --git a/perun/profiles/polars/__init__.py b/perun/profiles/polars/__init__.py new file mode 100644 index 000000000..5acd1449b --- /dev/null +++ b/perun/profiles/polars/__init__.py @@ -0,0 +1,5 @@ +"""Profile representation using Polars LazyFrames and DataFrames.""" + +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/perun/profiles/polars/__init__.pyi b/perun/profiles/polars/__init__.pyi new file mode 100644 index 000000000..ca7897d00 --- /dev/null +++ b/perun/profiles/polars/__init__.pyi @@ -0,0 +1,18 @@ +from .convert import ( + create_polars_pair_profile as create_polars_pair_profile, + merge_and_filter_polars_profiles as merge_and_filter_polars_profiles, + polars_merged_to_tabular_profiles as polars_merged_to_tabular_profiles, +) + +from .parser import ( + parse_polars_squash as parse_polars_squash, + parse_polars_no_squash as parse_polars_no_squash, +) + +from .structs import ( + FunctionMaps as FunctionMaps, + PolarsTraceProfile as PolarsTraceProfile, + PolarsTraceProfilePair as PolarsTraceProfilePair, + PolarsMergedTraceProfiles as PolarsMergedTraceProfiles, + PolarsTabularTraceProfiles as PolarsTabularTraceProfiles, +) diff --git a/perun/profiles/polars/convert.py b/perun/profiles/polars/convert.py new file mode 100644 index 000000000..0c81f0015 --- /dev/null +++ b/perun/profiles/polars/convert.py @@ -0,0 +1,315 @@ +"""Conversion functions between various Polars representations.""" + +from __future__ import annotations + +# Standard Imports + +# Third-Party Imports +import polars as pl + +# Perun Imports +from perun.profiles import structs +from perun.profiles.polars import structs as pl_structs +from perun.utils import log + + +def create_polars_pair_profile( + baseline_profile: pl.LazyFrame, + baseline_features: structs.ProfileFeatures, + target_profile: pl.LazyFrame, + target_features: structs.ProfileFeatures, + function_maps: pl_structs.FunctionMaps, +) -> pl_structs.PolarsTraceProfilePair: + """Transform simple baseline and target LazyFrame profiles into the polars pair profile. + + Prefer this function to constructing the pair profile directly: this function makes sure that + all auxiliary data, e.g., profile features, function maps, or the common traces and functions, + are computed correctly. + + :param baseline_profile: a collection of baseline profiles to aggregate into a single + baseline trace profile. + :param baseline_features: the incomplete baseline profile features object. + :param target_profile: a collection of target profiles to aggregate into a single + target trace profile. + :param target_features: the incomplete target profile features object. + :param function_maps: function name and ID maps. + + :return: a polars trace profile pair. + """ + # We need to reuse the parsed profiles in a lot of computations, hence we use the collect_all + # to avoid repeated materialization of the same LazyFrames, and to optimize and parallelize the + # computations as much as possible. + baseline_df, target_df, common_traces, common_funcs, *features = pl.collect_all( + [ + # Materialize the baseline trace profile. + baseline_profile, + # Materialize the target trace profile. + target_profile, + # Find traces that appear in both profiles; this includes traces with 0 exclusive costs. + baseline_profile.select("trace").join( + target_profile.select("trace"), on="trace", how="inner" + ), + # Find functions that appear in both profiles; this includes functions with 0 exclusive + # costs. + baseline_profile.select("func") + .unique() + .join(target_profile.select("func").unique(), on="func", how="inner"), + # Compute the number of unique traces seen in the baseline profile. + baseline_profile.select("trace").count(), + # Compute the number of unique functions seen in the baseline profile. + baseline_profile.select("func").unique().count(), + # Compute the number of unique baseline traces that have non-zero exclusive cost. + baseline_profile.filter(pl.col("exclusive") != 0).select("trace").count(), + # Compute the number of unique baseline functions that have non-zero exclusive cost. + baseline_profile.filter(pl.col("exclusive") != 0).select("func").unique().count(), + # Compute the number of unique traces seen in the target profile. + target_profile.select("trace").count(), + # Compute the number of unique functions seen in the target profile. + target_profile.select("func").unique().count(), + # Compute the number of unique target traces that have non-zero exclusive cost. + target_profile.filter(pl.col("exclusive") != 0).select("trace").count(), + # Compute the number of unique target functions that have non-zero exclusive cost. + target_profile.filter(pl.col("exclusive") != 0).select("func").unique().count(), + ] + ) + if baseline_df.height == 0: + log.error("Baseline profile is empty. Terminating.") + elif target_df.height == 0: + log.error("Target profile is empty. Terminating.") + + # So far, only some of the baseline and target features are computed as computing them eagerly + # during the parsing is too expensive. Instead, we can compute them more efficiently now. + _update_profile_features( + baseline_features, + baseline_df, + *features[:4], + ) + _update_profile_features( + target_features, + target_df, + *features[4:], + ) + return pl_structs.PolarsTraceProfilePair( + pl_structs.PolarsTraceProfile(baseline_df, baseline_features, function_maps), + pl_structs.PolarsTraceProfile(target_df, target_features, function_maps), + common_traces, + common_funcs, + ) + + +def _update_profile_features( + features: structs.ProfileFeatures, + prof: pl.DataFrame, + seen_traces: pl.DataFrame, + seen_functions: pl.DataFrame, + measured_traces: pl.DataFrame, + measured_functions: pl.DataFrame, +) -> None: + """Update the incomplete profile features with the missing features. + + The features object is initialized only partially during the parsing. The missing features + should be supplied after they are computed more cheaply after postprocessing the profiles a bit. + + :param features: an incomplete profile features object + :param prof: a profile DataFrame without traces with 0 exclusive cost + :param seen_traces: a 1x1 DataFrame containing the number of all the unique traces that + appeared in the profile + :param seen_functions: a 1x1 DataFrame containing the number of all the unique functions that + appeared in the profile + :param measured_traces: a 1x1 DataFrame containing the number of the unique traces that have + non-zero exclusive cost + :param measured_functions: a 1x1 DataFrame containing the number of the unique functions that + have non-zero exclusive cost + """ + # Obtain the single value in the DataFrames as a scalar integer. + features.seen_traces_count = seen_traces.item() + features.seen_functions_count = seen_functions.item() + # The number of unique traces with non-zero exclusive cost is determined by the height of the + # profile DataFrame. + features.measured_traces_count = measured_traces.item() + features.measured_functions_count = measured_functions.item() + # If we aggregated multiple folded profiles into one, and the values were not simply summed, + # we need to recompute the total number of consumed resources. + features.total_resources = int(prof["exclusive"].sum()) + + +def merge_and_filter_polars_profiles( + baseline: pl_structs.PolarsTraceProfile, + target: pl_structs.PolarsTraceProfile, + filter_params: structs.FilterParameters, +) -> pl_structs.PolarsMergedTraceProfiles: + """Merge two Polars trace profiles into per-trace and per-function Polars merged profiles. + + During the merge process, we also filter out functions and traces that have 0 exclusive cost + and do not meet thresholds for inclusive resource consumption specified in the filtering + parameters. + + :param baseline: the baseline trace profile + :param target: the target trace profile + :param filter_params: filtering parameters + + :return: merged per-trace and per-function Polars profiles + """ + baseline_total = baseline.features.total_resources + target_total = target.features.total_resources + # Compute the function and trace thresholds + base_traces_threshold = filter_params.traces_threshold * baseline_total + tar_traces_threshold = filter_params.traces_threshold * target_total + base_func_threshold = filter_params.function_threshold * baseline_total + tar_func_threshold = filter_params.function_threshold * target_total + + # Merge the baseline and target DataFrames such that each unique trace has exactly one row. + merged_traces: pl.LazyFrame = ( + baseline.profile.lazy() + .filter(pl.col("exclusive") != 0) + .join( + target.profile.lazy().filter(pl.col("exclusive") != 0), + on=("func", "trace"), + how="full", + coalesce=True, + suffix="_target", + ) + # Traces that were measured only in baseline or target will have 0 exclusive and inclusive + # resource consumption + .fill_null(0) + ) + + # Polars expressions for computing the proportional inclusive and exclusive diffs. + proportional_diff_expressions: tuple[pl.Expr, pl.Expr] = ( + ((pl.col("inclusive_target") / target_total - pl.col("inclusive") / baseline_total) * 100) + .cast(pl.Float32) + .alias("prop_diff_incl"), + ((pl.col("exclusive_target") / target_total - pl.col("exclusive") / baseline_total) * 100) + .cast(pl.Float32) + .alias("prop_diff_excl"), + ) + + # Create the per-trace merged profile. + filtered_merged_funcs: pl.LazyFrame = ( + merged_traces.select(pl.exclude("trace")) + .group_by("func") + .sum() + # Remove functions that consume too few resources and are likely uninteresting. + .filter( + (pl.col("inclusive") >= base_func_threshold) + | (pl.col("inclusive_target") >= tar_func_threshold) + ) + # Compute the proportional diffs. + .with_columns(*proportional_diff_expressions) + ) + + filtered_merged_traces: pl.LazyFrame = ( + # Keep only traces of functions that made it to the per-function merged profile after + # filtering out the cheap functions. + merged_traces.join(filtered_merged_funcs, on="func", how="semi") + # Additionally remove traces that consume too few resources and are likely uninteresting. + .filter( + (pl.col("inclusive") >= base_traces_threshold) + | (pl.col("inclusive_target") >= tar_traces_threshold) + ) + # Compute the proportional diffs. + .with_columns(*proportional_diff_expressions) + ) + + return pl_structs.PolarsMergedTraceProfiles( + *pl.collect_all([filtered_merged_traces, filtered_merged_funcs]) + ) + + +def polars_merged_to_tabular_profiles( + merged: pl_structs.PolarsMergedTraceProfiles, max_traces_per_func: int +) -> pl_structs.PolarsTabularTraceProfiles: + """Transform Polars merged profiles to a format suitable for trace table. + + The trace table profile will have only up to top 'max_traces_per_func' traces per each function + w.r.t. the inclusive proportional diff metric. Furthermore, both profiles will be sorted by the + function IDs in the ascending order. + + Both DataFrames will contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', + 'exclusive_target', 'prop_diff_incl', 'prop_diff_excl', 'abs_diff_incl', 'abs_diff_excl', + 'rel_diff_incl', and 'rel_diff_excl' columns. The traces profile will additionally contain the + 'trace' column. See the module docstring for description of the difference metrics. + + Both function and trace table profiles will have additional 'abs_diff_incl', 'abs_diff_excl', + 'rel_diff_incl', and 'rel_diff_excl' diff columns + + :param merged: the Polars merged profiles to transform + :param max_traces_per_func: the maximum number of top traces to keep per function + + :return: a pair of trace and function table profiles + """ + # Polars expressions for computing the absolute and relative diffs for both inclusive and + # exclusive consumption. + abs_rel_diff_expressions: tuple[pl.Expr, pl.Expr, pl.Expr, pl.Expr] = ( + # Absolute inclusive diff. + (pl.col("inclusive_target") - pl.col("inclusive")).alias("abs_diff_incl").cast(pl.Int64), + # Absolute exclusive diff. + (pl.col("exclusive_target") - pl.col("exclusive")).alias("abs_diff_excl").cast(pl.Int64), + # Relative inclusive diff. + ( + (pl.col("inclusive_target") - pl.col("inclusive")) + / pl.max_horizontal("inclusive", "inclusive_target") + * 100 + ) + .cast(pl.Float32) + .alias("rel_diff_incl"), + # Relative inclusive diff. + ( + (pl.col("exclusive_target") - pl.col("exclusive")) + / pl.max_horizontal("exclusive", "exclusive_target") + * 100 + ) + .cast(pl.Float32) + .alias("rel_diff_excl"), + ) + + # Build merged func profile with additional absolute and relative diff metrics, and sort the + # DataFrame rows from the lowest function IDs to the highest ones. + table_funcs_lf = ( + merged.funcs.lazy().with_columns(*abs_rel_diff_expressions).sort("func", descending=False) + ) + + # Build merged trace profile with additional absolute and relative diff metrics, where each + # function keeps only up to top 'max_traces_per_func' traces w.r.t. both inclusive and + # exclusive proportional diff metric. + # TODO: calculate the top traces also for other metrics, e.g., absolute delta or relative + # delta. This will, however, require some hook on sort operation in our trace tables to + # always retrieve the correct set of functions. + top_incl_traces = ( + # This groups traces on a per-function basis and then filters up to top N rows (traces) + # for each function w.r.t. the inclusive proportional delta. + merged.traces.lazy() + .group_by("func") + .agg( + pl.struct(pl.exclude("func")) + .top_k_by("prop_diff_incl", k=max_traces_per_func) + .alias("rows") + ) + .explode("rows") + .unnest("rows") + ) + + top_excl_traces = ( + # Similar to the previous step, but filters based on th exclusive proportional delta. + merged.traces.lazy() + .group_by("func") + .agg( + pl.struct(pl.exclude("func")) + .top_k_by("prop_diff_excl", k=max_traces_per_func) + .alias("rows") + ) + .explode("rows") + .unnest("rows") + ) + + # Concatenate and deduplicate both tables. For example, if `max_traces_per_func` = 10 and some + # function has a total of 15 traces, but only 13 of them appear in both top selections, the + # resulting table will keep only the 13 traces instead of 20 (7 of which would be duplicated). + table_traces_lf = ( + pl.concat([top_incl_traces, top_excl_traces]) + .unique(subset=["func", "trace"]) + .with_columns(*abs_rel_diff_expressions) + .sort("func", descending=False) + ) + + return pl_structs.PolarsTabularTraceProfiles(*pl.collect_all([table_traces_lf, table_funcs_lf])) diff --git a/perun/profiles/polars/meson.build b/perun/profiles/polars/meson.build new file mode 100644 index 000000000..91afeb9c0 --- /dev/null +++ b/perun/profiles/polars/meson.build @@ -0,0 +1,14 @@ +perun_polars_profile_dir = perun_profiles_dir / 'polars' + +perun_polars_profile_files = files( + '__init__.py', + '__init__.pyi', + 'convert.py', + 'parser.py', + 'structs.py', +) + +py3.install_sources( + perun_polars_profile_files, + subdir: perun_polars_profile_dir +) \ No newline at end of file diff --git a/perun/profiles/polars/parser.py b/perun/profiles/polars/parser.py new file mode 100644 index 000000000..cd93bf7dd --- /dev/null +++ b/perun/profiles/polars/parser.py @@ -0,0 +1,241 @@ +"""A collection of parsing functions. + +The functions transform, and optionally postprocess, a stream of folded-like record +`(stack_trace, consumed_resources)` into a Polars LazyFrame. +""" + +from __future__ import annotations + +# Standard Imports +from collections import defaultdict +from collections.abc import Iterator, KeysView, Mapping +import re +from typing import Optional + +# Third-Party Imports +import polars as pl + +# Perun Imports +from perun.profiles import structs, utils +from perun.profiles.polars import structs as pl_structs + + +def parse_polars_no_squash( + record_stream: Iterator[tuple[str, int]], + maps: pl_structs.FunctionMaps, + params: structs.PostprocessParameters, + profile_features: structs.ProfileFeatures, +) -> pl.LazyFrame: + """Parse a folded profile into a polars frame without squashing recursive calls. + + We implement two separate parsing functions for squash/no-squash variants. The no-squash + variant is simpler, and we want to keep it as fast as possible. + + :param record_stream: a stream of folded-like records to process + :param maps: function name and ID maps + :param params: parsing parameters + :param profile_features: profile features that describe the parsed profile + + :return: a parsed profile lazyframe with 'func', 'trace', 'inclusive', and 'exclusive' columns + """ + # trace -> inclusive resource consumption. + inclusive: defaultdict[str, int] = defaultdict(int) + # trace -> exclusive resource consumption. + exclusive: defaultdict[str, int] = defaultdict(int) + max_trace_len = 0 + total = 0 + + # Optimize dot operator access. + str_split = str.split + hide_generics_func = utils.hide_uid_generics + func_id_map = maps.func_id_map + hide_generics_flag = params.hide_generics + + for trace, count in record_stream: + # Split the trace into individual frames. + frames: list[str] = str_split(trace, ";") + max_trace_len = max(max_trace_len, len(frames)) + total += count + + # Process the first frame of the trace. + trace = str(func_id_map[frames[0]]) + inclusive[trace] += count + # Process the rest of the trace. + for frame in frames[1:]: + if hide_generics_flag: + frame = hide_generics_func(frame) + trace += f";{func_id_map[frame]}" + inclusive[trace] += count + exclusive[trace] += count + # Update the profile features. + profile_features.total_resources += total + profile_features.max_trace_len = max(max_trace_len, profile_features.max_trace_len) + return _build_trace_profile(inclusive, exclusive, None) + + +def parse_polars_squash( + record_stream: Iterator[tuple[str, int]], + maps: pl_structs.FunctionMaps, + params: structs.PostprocessParameters, + profile_features: structs.ProfileFeatures, +) -> pl.LazyFrame: + """Parse a folded profile into a polars frame while squashing recursive calls. + + We implement two separate parsing functions for squash/no-squash variants. The squash variant + is more complicated and expensive, so we keep it separately to not slow down the no-squash + implementation. + + :param record_stream: a stream of folded-like records to process + :param maps: a collection of parsing maps + :param params: parsing parameters + :param profile_features: profile features that describe the parsed profile + + :return: a parsed profile lazyframe with 'func', 'trace', 'inclusive', and 'exclusive' columns + """ + # trace -> inclusive resource consumption. + inclusive: defaultdict[str, int] = defaultdict(int) + # trace -> exclusive resource consumption. + exclusive: defaultdict[str, int] = defaultdict(int) + max_trace_len = 0 + total = 0 + + # Optimize dot operator access. + re_search = re.search + str_split = str.split + hide_generics_func = utils.hide_uid_generics + func_id_map = maps.func_id_map + squashed_id_map = maps.squashed_id_map + squash_pattern = params.squash_pattern + hide_generics = params.hide_generics + + for trace, count in record_stream: + # Split the trace into individual frames. + frames: list[str] = str_split(trace, ";") + max_trace_len = max(max_trace_len, len(frames)) + total += count + + # The algorithm does not immediately write the processed frame into the compact trace + # string. Instead, it remembers the last processed frame and either writes it in the + # next step, or merges it with the successor if squash conditions are satisfied. + last_frame = func_id_map[frames[0]] + trace = "" + recursive_count = 1 + for idx, frame in enumerate(frames[1:]): + if hide_generics: + frame = hide_generics_func(frame) + func_id = func_id_map[frame] + + # We want to merge the frames if they represent the same function, and they match + # the squash pattern. Regex matching is done only once for each sequence of + # identical frames, and only for non-default patterns (default pattern matches + # everything). + if func_id == last_frame and ( + squash_pattern is None or (recursive_count > 1 or re_search(squash_pattern, frame)) + ): + # This frame is part of a recursive call chain. + recursive_count += 1 + elif recursive_count == 1: + # The previous frame was not part of a recursive call chain; write it. + if trace: + trace += f";{last_frame}" + else: + trace = str(last_frame) + inclusive[trace] += count + last_frame = func_id + else: + # A recursive call chain has ended. We create a new ID for the squashed + # function with the number of recursive calls. + squashed_id = func_id_map[f"{frames[idx]}{{x{recursive_count}}}"] + trace += f";{squashed_id}" + squashed_id_map[squashed_id] = last_frame + inclusive[trace] += count + last_frame = func_id + recursive_count = 1 + # We must still process the last frame. + if recursive_count > 1: + squashed_id = func_id_map[f"{frames[-1]}{{x{recursive_count}}}"] + trace += f";{squashed_id}" + squashed_id_map[squashed_id] = last_frame + elif trace: + trace += f";{last_frame}" + else: + trace = str(last_frame) + inclusive[trace] += count + exclusive[trace] += count + # Update the profile features. + profile_features.total_resources += total + profile_features.max_trace_len = max(max_trace_len, profile_features.max_trace_len) + return _build_trace_profile(inclusive, exclusive, squashed_id_map) + + +def _build_trace_profile( + inclusive: defaultdict[str, int], + exclusive: defaultdict[str, int], + squashed_id_map: Optional[Mapping[int, int]], +) -> pl.LazyFrame: + """Build a per-trace profile from inclusive and exclusive resource consumption records. + + No filtering takes place at this point; although we could, e.g., filter records with no + exclusive consumption, we would not be able to correctly compute some profile features or lose + some precision when detecting common functions to baseline and target profiles. + + Both the and maps should have keys in the form of 'id1;id2;id3;...' + which represent traces in a compact format where id1, id2, and id3 correspond to function IDs. + + If recursion squashing took place, a should be provided as well. + + :param inclusive: a mapping of trace -> inclusive resource consumption + :param exclusive: a mapping of trace -> inclusive resource consumption + :param squashed_id_map: a mapping of squashed function ID -> function ID; if not provided, + we assume no squashing was done + + :return: a per-trace profile with 'func', 'trace', 'inclusive', and 'exclusive' columns + """ + return pl.LazyFrame( + { + "func": _generate_func_ids(inclusive.keys(), squashed_id_map), + "trace": inclusive.keys(), + "inclusive": inclusive.values(), + # The .get() method avoids needlessly growing the defaultdict. + "exclusive": (exclusive.get(t, 0) for t in inclusive), + }, + schema={ + "func": pl.UInt32, + "trace": pl.String, + "inclusive": pl.Int64, + "exclusive": pl.Int64, + }, + ) + + +def _generate_func_ids( + traces: KeysView[str], squashed_id_map: Optional[Mapping[int, int]] +) -> Iterator[int]: + """A helper function that extracts the ID of the last function from a possibly squashed trace. + + For squashed traces, the function returns the ID of the non-squashed function symbol. For + example, if the ID of last function in a trace corresponds to the 'foo{x8}' symbol, the + function will return the ID of the original 'foo' symbol. + + :param traces: a set of traces to extract the function ID from + :param squashed_id_map: a mapping of squashed function ID -> function ID; if not provided, + we assume no squashing was done + + :return: a generator over the extracted function IDs + """ + # Optimize dot operator access. + str_rsplit = str.rsplit + + if squashed_id_map is None: + # No squashing was done, simply extract and return the last ID in the trace. + for trace in traces: + yield int(str_rsplit(trace, ";", maxsplit=1)[-1]) + else: + # Some of the traces may have been squashed. + for trace in traces: + last_func_id = int(str_rsplit(trace, ";", maxsplit=1)[-1]) + # Check if the last ID corresponds to a squashed function that needs to be translated. + try: + yield squashed_id_map[last_func_id] + except KeyError: + yield last_func_id diff --git a/perun/profiles/polars/structs.py b/perun/profiles/polars/structs.py new file mode 100644 index 000000000..2e05849f4 --- /dev/null +++ b/perun/profiles/polars/structs.py @@ -0,0 +1,258 @@ +"""A collection of Polars profile representations and helper structures.""" + +from __future__ import annotations + +# Standard Imports +from collections import defaultdict +import dataclasses + +# Third-Party Imports +import polars as pl + +# Perun Imports +from perun.profiles import structs + + +class FunctionMaps: + """A collection of mappings between function names and IDs created during parsing of profiles. + + The mappings are used to map function names to their IDs and back, which optimizes both memory + and time of profile representation and manipulation. + + The maps may be reused when parsing multiple profiles (e.g., multiple baseline and target + profiles), which leads to faster and easier matching when merging the profiles. + + :ivar func_id_map: a mapping of function name -> function ID + :ivar func_id_reverse_map: a mapping of function ID -> function name; sorted by function ID + :ivar squashed_id_map: a mapping of squashed function ID -> function ID; squashed functions + refer to artificial function names representing merged recursive calls, e.g. 'func{x5}' + """ + + __slots__ = "func_id_map", "func_id_reverse_map", "squashed_id_map" + + def __init__(self) -> None: + self.func_id_map: defaultdict[str, int] = defaultdict(lambda: len(self.func_id_map)) + self.func_id_reverse_map: dict[int, str] = {} + self.squashed_id_map: dict[int, int] = {} + + def finalize(self) -> None: + """Construct the reverse map. + + The reverse map is sorted in ascending order according to the function IDs, i.e., the keys. + + The finalization should be done after all profiles are parsed. + """ + # The func_id_map is sorted by values, i.e., the function IDs (see the defaultdict factory + # lambda). Hence, the reverse map will be sorted as well as long as it is constructed by + # iterating over the func_id_map. + self.func_id_reverse_map = {f_id: f_name for f_name, f_id in self.func_id_map.items()} + + +@dataclasses.dataclass +class PolarsTraceProfile: + """A Polars DataFrame representation of a single profile containing traces (e.g., perf folded). + + The profile DataFrame has the following columns: 'func', 'trace', 'inclusive', 'exclusive' + where 'func' is the ID of the last function the trace, 'trace' is a semicolon-delimited string + of function IDs representing the trace, and 'inclusive' and 'exclusive' store the resource + consumption of the trace. + + For example, the profile DataFrame might look like this: + + ``` + shape: (369_033, 4) + ┌──────┬─────────────────────────────────┬───────────┬───────────┐ + │ func ┆ trace ┆ inclusive ┆ exclusive │ + │ --- ┆ --- ┆ --- ┆ --- │ + │ u32 ┆ str ┆ i64 ┆ i64 │ + ╞══════╪═════════════════════════════════╪═══════════╪═══════════╡ + │ 8 ┆ 0;3;2;4;5;6;7;8 ┆ 657306 ┆ 657306 │ + │ 15 ┆ 0;9;2;4;5;6;10;11;12;13;14;15 ┆ 475601 ┆ 475601 │ + │ 27 ┆ 0;17;16;4;5;18;19;20;21;22;23;… ┆ 683530 ┆ 683530 │ + │ … ┆ … ┆ … ┆ … │ + │ 6201 ┆ 6202;657;658;6186;6187;6200;62… ┆ 1360700 ┆ 1360700 │ + │ 453 ┆ 6202;657;658;6186;6187;6203;29… ┆ 78594 ┆ 78594 │ + │ 106 ┆ 6202;657;658;6186;6187;6203;29… ┆ 271800 ┆ 271800 │ + └──────┴─────────────────────────────────┴───────────┴───────────┘ + ``` + + :ivar profile: a DataFrame representation of a trace profile + :ivar features: a set of features describing and summarizing the profile + :ivar maps: function maps created during the parsing of the profile + """ + + __slots__ = "profile", "features", "maps" + + profile: pl.DataFrame + features: structs.ProfileFeatures + maps: FunctionMaps + + +@dataclasses.dataclass +class PolarsTraceProfilePair: + """A Polars representation of a pair of baseline and target trace profiles. + + This representation should be preferred when working with a pair of profiles that are expected + to be compared and analyzed, as it also stores sets of functions and traces that appear in both + profiles. These sets take into account all functions and traces that appeared in both profiles, + even if they have no measured exclusive resource consumption (and as such are omitted from the + PolarsTraceProfile). Currently, only functions and traces that are exact match are considered + to be common to both profiles. + + :ivar baseline: the baseline trace profile + :ivar target: the target trace profile + :ivar common_traces: a DataFrame containing only one column 'trace' with traces that appear in + both profiles + :ivar common_funcs: a DataFrame containing only one column 'func' with function IDs that appear + in both profiles + """ + + __slots__ = "baseline", "target", "common_traces", "common_funcs" + + baseline: PolarsTraceProfile + target: PolarsTraceProfile + common_traces: pl.DataFrame + common_funcs: pl.DataFrame + + +@dataclasses.dataclass +class PolarsMergedTraceProfiles: + """A Polars representation of merged baseline-target trace and function profiles. + + This representation stores both per-trace and per-function profiles that contain merged rows + for some baseline and target profiles. + + *Structure*: Both DataFrames contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', + 'exclusive_target', 'prop_diff_incl', and 'prop_diff_excl' columns. The merged traces profile + additionally contains the 'trace' column. See the module docstring for description of the + difference metrics. + + The merged function profile contains aggregated resource consumption (aggregated separately for + baseline and target profiles) across all traces associated with a function. Furthermore, it + omits functions that have 0 exclusive cost and do not meet filtering thresholds + (see FilterParameters). + + The merged trace profile omits all traces that belong to filtered functions, and all traces that + have 0 exclusive cost and do not meet filtering thresholds (see FilterParameters). + + For example, the merged traces and merged function DataFrames might look like this: + + ``` + shape: (32_342, 8) + ┌──────┬───────────┬───────────┬───┬──────────────────┬────────────────┬────────────────┐ + │ func ┆ trace ┆ inclusive ┆ … ┆ exclusive_target ┆ prop_diff_incl ┆ prop_diff_excl │ + │ --- ┆ --- ┆ --- ┆ … ┆ --- ┆ --- ┆ --- │ + │ u32 ┆ str ┆ i64 ┆ … ┆ i64 ┆ f32 ┆ f32 │ + ╞══════╪═══════════╪═══════════╪═══╪══════════════════╪════════════════╪════════════════╡ + │ 4 ┆ 971;4 ┆ 808775194 ┆ … ┆ 0 ┆ -0.010969 ┆ -0.000006 │ + │ 5 ┆ 971;4;5 ┆ 808331377 ┆ … ┆ 0 ┆ -0.010963 ┆ -0.000009 │ + │ 238 ┆ 971;4;5;… ┆ 246640065 ┆ … ┆ 0 ┆ -0.003345 ┆ -0.000063 │ + │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ + │ 135 ┆ 1554;155… ┆ 0 ┆ … ┆ 24230903 ┆ 0.001149 ┆ 0.001077 │ + │ 6235 ┆ 4;5;6235 ┆ 0 ┆ … ┆ 2683915 ┆ 0.014867 ┆ 0.000119 │ + │ 185 ┆ 3471;29… ┆ 0 ┆ … ┆ 81537127 ┆ 0.003625 ┆ 0.003625 │ + └──────┴───────────┴───────────┴───┴──────────────────┴────────────────┴────────────────┘ + + shape: (482, 7) + ┌──────┬──────────────┬──────────────┬───┬──────────────────┬────────────────┬────────────────┐ + │ func ┆ inclusive ┆ exclusive ┆ … ┆ exclusive_target ┆ prop_diff_incl ┆ prop_diff_excl │ + │ --- ┆ --- ┆ --- ┆ … ┆ --- ┆ --- ┆ --- │ + │ u32 ┆ i64 ┆ i64 ┆ … ┆ i64 ┆ f32 ┆ f32 │ + ╞══════╪══════════════╪══════════════╪═══╪══════════════════╪════════════════╪════════════════╡ + │ 545 ┆ 905453006855 ┆ 203226689 ┆ … ┆ 162086637 ┆ 34.676102 ┆ 0.004449 │ + │ 1200 ┆ 4591717786 ┆ 486459792 ┆ … ┆ 243767256 ┆ 0.304794 ┆ 0.004239 │ + │ 3823 ┆ 108197824030 ┆ 104876145383 ┆ … ┆ 23210060569 ┆ -0.403142 ┆ -0.390618 │ + │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ + │ 1569 ┆ 49300568157 ┆ 208229571 ┆ … ┆ 54313042 ┆ -0.368518 ┆ -0.00041 │ + │ 6931 ┆ 0 ┆ 0 ┆ … ┆ 79892101 ┆ 0.184407 ┆ 0.003551 │ + │ 777 ┆ 25804957237 ┆ 12916984718 ┆ … ┆ 1312987872 ┆ -0.24692 ┆ -0.116819 │ + └──────┴──────────────┴──────────────┴───┴──────────────────┴────────────────┴────────────────┘ + ``` + + Note that if a function or trace shows 0 resource consumption in target or baseline, it does not + mean that such function or trace is baseline-only or target-only, respectively, since the + baseline and target profiles have already omitted functions and traces that exist in the profile + but have no exclusive resource consumption (see PolarsTraceProfile for more details). Use the + 'common_traces' and 'common_funcs' to determine whether a 0 resource consumption indicates + baseline-only or target-only records. + + :ivar traces: a merged trace profile + :ivar funcs: a merged per-function profile + """ + + __slots__ = "traces", "funcs" + + traces: pl.DataFrame + funcs: pl.DataFrame + + +@dataclasses.dataclass +class PolarsTabularTraceProfiles: + """A Polars representation of baseline-target trace and function tabular profiles. + + The tabular trace and function profiles are similar to the merged profiles but contain fewer + records, additional difference metrics, and are sorted such that they can be easily iterated in + a lockstep to generate tabular records for diff reports. + + *Structure*: Both DataFrames contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', + 'exclusive_target', 'prop_diff_incl', 'prop_diff_excl', 'abs_diff_incl', 'abs_diff_excl', + 'rel_diff_incl', and 'rel_diff_excl' columns. The traces profile additionally contains the + 'trace' column. See the module docstring for description of the difference metrics. + + *Filtered*: The trace profile will have only up to top 'max_traces_per_func' traces per each + function w.r.t. the 'prop_diff_incl' and 'prop_diff_excl' metric. Note that the traces are + deduplicated; for example, if `max_traces_per_func` = 10 and some function has a total of 15 + traces, but only 13 of them appear in both top selections, the resulting table will keep only + the 13 traces instead of 20 (7 of which would be duplicated). + + *Sorted*: Both profiles are sorted by the function IDs in the ascending order. + + For example, the tabular traces and function DataFrames might look like this: + + ``` + shape: (3_447, 12) + ┌──────┬─────────────┬───────────────┬───┬───────────────┬───────────────┬───────────────┐ + │ func ┆ trace ┆ inclusive ┆ … ┆ abs_diff_excl ┆ rel_diff_incl ┆ rel_diff_excl │ + │ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- │ + │ u32 ┆ str ┆ i64 ┆ ┆ i64 ┆ f32 ┆ f32 │ + ╞══════╪═════════════╪═══════════════╪═══╪═══════════════╪═══════════════╪═══════════════╡ + │ 4 ┆ 1;268;4 ┆ 1246557813681 ┆ … ┆ -39682724 ┆ -1.382918 ┆ -68.930069 │ + │ 4 ┆ 1;124;4 ┆ 0 ┆ … ┆ 1900490 ┆ 100.0 ┆ 100.0 │ + │ 4 ┆ 1;1174;4 ┆ 0 ┆ … ┆ 260703 ┆ 100.0 ┆ 100.0 │ + │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ + │ 7483 ┆ 5284;5304;… ┆ 0 ┆ … ┆ 14016473 ┆ 100.0 ┆ 100.0 │ + │ 7483 ┆ 5284;5290;… ┆ 0 ┆ … ┆ 8070493 ┆ 100.0 ┆ 100.0 │ + │ 7483 ┆ 5284;5290;… ┆ 0 ┆ … ┆ 8065807 ┆ 100.0 ┆ 100.0 │ + └──────┴─────────────┴───────────────┴───┴───────────────┴───────────────┴───────────────┘ + + shape: (482, 11) + ┌──────┬───────────────┬───┬────────────────┬───────────────┬───────────────┬───────────────┐ + │ func ┆ inclusive ┆ … ┆ abs_diff_incl ┆ abs_diff_excl ┆ rel_diff_incl ┆ rel_diff_excl │ + │ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │ + │ u32 ┆ i64 ┆ ┆ i64 ┆ i64 ┆ f32 ┆ f32 │ + ╞══════╪═══════════════╪═══╪════════════════╪═══════════════╪═══════════════╪═══════════════╡ + │ 4 ┆ 4238716948387 ┆ … ┆ -2653980496373 ┆ -52677630 ┆ -62.612827 ┆ -42.958553 │ + │ 5 ┆ 4247354710131 ┆ … ┆ -2658348948953 ┆ -97869614 ┆ -62.588345 ┆ -20.323265 │ + │ 11 ┆ 196590265531 ┆ … ┆ -196590265531 ┆ -1199560386 ┆ -100.0 ┆ -100.0 │ + │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ + │ 6941 ┆ 0 ┆ … ┆ 24743188735 ┆ 95707629 ┆ 100.0 ┆ 100.0 │ + │ 6942 ┆ 0 ┆ … ┆ 24044566859 ┆ 225687075 ┆ 100.0 ┆ 100.0 │ + │ 7483 ┆ 0 ┆ … ┆ 3459034839 ┆ 71846967 ┆ 100.0 ┆ 100.0 │ + └──────┴───────────────┴───┴────────────────┴───────────────┴───────────────┴───────────────┘ + ``` + + Note that if a function or trace shows 0 resource consumption in target or baseline, it does not + mean that such function or trace is baseline-only or target-only, respectively, since the + baseline and target profiles have already omitted functions and traces that exist in the profile + but have no exclusive resource consumption (see PolarsTraceProfile for more details). Use the + 'common_traces' and 'common_funcs' to determine whether a 0 resource consumption indicates + baseline-only or target-only records. + + :ivar traces: a merged trace profile + :ivar funcs: a merged per-function profile + """ + + __slots__ = "traces", "funcs" + + traces: pl.DataFrame + funcs: pl.DataFrame diff --git a/perun/profiles/native/stats.py b/perun/profiles/stats.py similarity index 86% rename from perun/profiles/native/stats.py rename to perun/profiles/stats.py index 03f46701b..d257f3523 100644 --- a/perun/profiles/native/stats.py +++ b/perun/profiles/stats.py @@ -23,13 +23,16 @@ import enum import math import statistics -from typing import Any, Protocol, Iterable, ClassVar, Union, cast +from typing import Any, Protocol, Iterable, ClassVar, Union, cast, TYPE_CHECKING # Third-Party Imports # Perun Imports from perun.utils import log as perun_log +if TYPE_CHECKING: + from perun.profiles.structs import ProfileFeatures + class ProfileStatComparison(str, enum.Enum): """The profile stat comparison types. @@ -526,3 +529,81 @@ def compare_stats( if comparison == ProfileStatComparison.LOWER else StatComparisonResult.TARGET_BETTER ) + + +def merge_stats(new_stat: ProfileStat, into_stats: list[ProfileStat]) -> None: + """Merge a new profile stat values into the current profile stats. + + If an existing stat with the same name exists, the values of both stats are merged. If no such + stat is found, the new stat is added to the collection of current stats. + + :param new_stat: the new profile stat to merge. + :param into_stats: the current collection of profile stats. + """ + for stat in into_stats: + if new_stat.name == stat.name: + # We found a stat with a matching name, merge + stat.merge_with(new_stat) + return + # There is no stat to merge with, extend the current collection of stats + into_stats.append(new_stat) + + +def features_to_stats(features: ProfileFeatures, resource_name: str) -> list[ProfileStat]: + """Transform profile features into profile stats. + + :param features: profile features + :param resource_name: profile resource name + + :return: a collection of profile stats derived from the features + """ + return [ + ProfileStat( + f"Total {resource_name}", + ProfileStatComparison.LOWER, + "#", + description=f"The total amount of {resource_name} accounted for in the profile.", + value=[features.total_resources], + ), + ProfileStat( + "Unique Functions", + ProfileStatComparison.LOWER, + "#", + description="The number of unique function symbols seen in the profile. This includes " + "functions that have no measured exclusive resource consumption but were " + "seen in traces.", + value=[features.seen_functions_count], + ), + ProfileStat( + "Unique Traces", + ProfileStatComparison.LOWER, + "#", + description="The number of unique traces seen in the profile. This includes traces " + "that have no measured exclusive resource consumption but were seen in the " + "profile.", + value=[features.seen_traces_count], + ), + ProfileStat( + "Measured Functions", + ProfileStatComparison.LOWER, + "#", + description="The number of unique function symbols that have exclusive resource " + "consumption recorded in the profile.", + value=[features.measured_functions_count], + ), + ProfileStat( + "Measured Traces", + ProfileStatComparison.LOWER, + "#", + description="The number of unique traces that have exclusive resource consumption " + "recorded in the profile.", + value=[features.measured_traces_count], + ), + ProfileStat( + "Longest Profile Trace", + ProfileStatComparison.LOWER, + "#", + description="The longest trace recorded in the profile.", + value=[features.max_trace_len], + ), + ] diff --git a/perun/profiles/structs.py b/perun/profiles/structs.py new file mode 100644 index 000000000..828cb7241 --- /dev/null +++ b/perun/profiles/structs.py @@ -0,0 +1,183 @@ +"""Helper structures for parsing, postprocessing, or describing profiles.""" + +from __future__ import annotations + +# Standard Imports +import dataclasses +import re +from typing import Any, Optional, Union + +# Third-Party Imports + +# Perun Imports +from perun.logic import config +from perun.utils.common import common_kit +from perun.utils.structs.view_structs import DEFAULT_SQUASH_RE + + +class PostprocessParameters: + """Profile postprocessing parameters. + + :ivar hide_generics: hide type generics in function names. + :ivar squash_recursion: squash recursive calls into a single function record. + :ivar squash_pattern: a regex pattern to filter functions to squash. + :ivar agg_func: the aggregation function. + """ + + __slots__ = ( + "hide_generics", + "squash_recursion", + "squash_pattern", + "agg_func", + ) + + def __init__( + self, hide_generics: bool, squash: bool, squash_regex: str = DEFAULT_SQUASH_RE, **_: Any + ) -> None: + """ + :param hide_generics: a flag indicating whether to hide type generics in function names. + :param squash: squash recursive calls into a single function record. + :param squash_regex: a regex pattern to filter functions to squash. + """ + self.hide_generics: bool = hide_generics + self.squash_recursion: bool = squash + self.squash_pattern: Optional[re.Pattern[str]] = None + # Pre-compile non-default squash patterns. The default squash pattern matches everything + # so there is no need to do regex matching at all. + if squash and squash_regex != DEFAULT_SQUASH_RE: + self.squash_pattern = re.compile(squash_regex) + self.agg_func: common_kit.Aggregations = common_kit.Aggregations.from_string( + config.lookup_key_recursively( + "profile.aggregation", default=common_kit.Aggregations.default_name() + ) + ) + + +class FilterParameters: + """Performance data filtering parameters. + + Large profiles contain too much data to efficiently process and visualize. We thus allow the + users to exclude data based on several thresholds and limits. The assumption is that if certain + functions and traces take very little total resources (time, samples, etc.), then they are + likely not the culprit of possible performance degradations. + + :ivar function_threshold: exclude functions that consumed less than the threshold ratio of the + total resources in *both* baseline and target. + :ivar traces_threshold: exclude traces that consumed less than the threshold ratio of the + total resources in *both* baseline and target. + :ivar max_function_traces: limit the number of most expensive traces stored per function. + :ivar top_diffs: limit the number of overall most expensive traces and function diffs. + """ + + __slots__ = "function_threshold", "traces_threshold", "max_function_traces", "top_diffs" + + def __init__( + self, + function_threshold: float, + traces_threshold: float, + max_function_traces: int, + top_diffs: int, + **_: Any, + ) -> None: + """ + :param function_threshold: the % threshold for function total resource consumption. + :param traces_threshold: the % threshold for trace resource consumption. + :param max_function_traces: the maximum number of most expensive traces to keep per + function. + :param top_diffs: the number of overall most expensive traces and function diffs. + """ + # The thresholds are given as percentages, adjust to [0, 1] ratio. + self.function_threshold: float = function_threshold * 0.01 + self.traces_threshold: float = traces_threshold * 0.01 + self.max_function_traces: int = max_function_traces + self.top_diffs: int = top_diffs + + +class ProfileFeatures: + """A collection of profile features. + + :ivar total_resources: the total number of consumed resources. + :ivar max_trace_len: the longest seen trace. + :ivar measured_traces_count: the number of unique traces that have been measured; this does not + include traces that have no measured resource consumption, e.g., sub-traces of recorded + traces. + :ivar measured_functions_count: the number of unique functions that have been measured in any + calling context; this does not include functions that have no measured resource + consumption, e.g., functions seen in traces but with no measured consumption. + :ivar seen_traces_count: the number of all traces seen in a profile, including those that have + no recorded exclusive resource consumption. + :ivar seen_functions_count: the number of all functions seen in a profile, including those that + have no recorded exclusive resource consumption. + """ + + __slots__ = ( + "total_resources", + "max_trace_len", + "measured_traces_count", + "measured_functions_count", + "seen_traces_count", + "seen_functions_count", + ) + + def __init__(self) -> None: + self.total_resources: int = 0 + self.max_trace_len: int = 0 + self.measured_traces_count: int = 0 + self.measured_functions_count: int = 0 + self.seen_traces_count: int = 0 + self.seen_functions_count: int = 0 + + +# A tuple representation of the ProfileMetadataEntry object +ProfileHeaderTuple = tuple[str, Union[str, float], str, dict[str, Union[str, float]]] + + +@dataclasses.dataclass +class ProfileMetadataEntry: + """A representation of a single profile header entry. + + :ivar name: the name (key) of the header entry. + :ivar value: the value of the header entry. + :ivar description: detailed description of the header entry. + :ivar details: nested key: value data. + """ + + name: str + value: str | float + description: str = "" + details: dict[str, str | float] = dataclasses.field(default_factory=dict) + + @classmethod + def from_string(cls, header: str) -> ProfileMetadataEntry: + """Constructs a `ProfileHeaderRecord` object from a string representation. + + :param header: the string representation of a header entry. + + :return: the constructed ProfileHeaderRecord object. + """ + split = header.split("|") + name = split[0] + value = common_kit.try_convert(split[1] if len(split) > 1 else "[empty]", [float, str]) + desc = split[2] if len(split) > 2 else ProfileMetadataEntry.description + details: dict[str, str | float] = {} + for detail in split[4:]: + detail_key, detail_value = detail.split(maxsplit=1) + details[detail_key] = common_kit.try_convert(detail_value, [float, str]) + return cls(name, value, desc, details) + + @classmethod + def from_profile(cls, header: dict[str, Any]) -> ProfileMetadataEntry: + """Constructs a ProfileMetadataEntry object from native Profile header. + + :param header: the dictionary representation of a header entry. + + :return: the constructed ProfileMetadataEntry object. + """ + return cls(**header) + + def as_tuple(self) -> ProfileHeaderTuple: + """Converts the header object into a tuple. + + :return: the tuple representation of a header entry. + """ + return self.name, self.value, self.description, self.details diff --git a/perun/profiles/utils.py b/perun/profiles/utils.py new file mode 100644 index 000000000..cc9c40a26 --- /dev/null +++ b/perun/profiles/utils.py @@ -0,0 +1,23 @@ +"""Utility functions for all profile types and conversions.""" + +from __future__ import annotations + + +def hide_uid_generics(uid: str) -> str: + """Hides the generics in the uid + + This transforms std::> into std::<*> + + :param uid: uid with generics + :return: uid without generics + """ + nesting = 0 + chars = [] + for c in uid: + if c == "<": + nesting += 1 + elif c == ">": + nesting -= 1 + if nesting == 0 or (c == "<" and nesting == 1): + chars.append(c) + return "".join(chars) diff --git a/perun/select/__init__.py b/perun/selection/__init__.py similarity index 77% rename from perun/select/__init__.py rename to perun/selection/__init__.py index 079d39cd2..43625d1ae 100755 --- a/perun/select/__init__.py +++ b/perun/selection/__init__.py @@ -1,5 +1,5 @@ """ -perun.select is a collection of method for selection of the versions and profiles in the history. +perun.selection is a collection of method for selection of the versions and profiles in the history. This corresponds to family of techniques of Selections or Reduction of the test case duration and includes many different techniques, such as predicting which versions will have impact on diff --git a/perun/select/abstract_base_selection.py b/perun/selection/abstract_base_selection.py similarity index 100% rename from perun/select/abstract_base_selection.py rename to perun/selection/abstract_base_selection.py diff --git a/perun/select/factory.py b/perun/selection/factory.py similarity index 92% rename from perun/select/factory.py rename to perun/selection/factory.py index ad8447cfa..ccedbe04f 100755 --- a/perun/select/factory.py +++ b/perun/selection/factory.py @@ -12,7 +12,7 @@ # Perun Imports from perun.logic import config from perun.utils import decorators -from perun.select import whole_repository_selection, abstract_base_selection +from perun.selection import whole_repository_selection, abstract_base_selection from perun.utils.exceptions import UnsupportedModuleException diff --git a/perun/select/meson.build b/perun/selection/meson.build similarity index 83% rename from perun/select/meson.build rename to perun/selection/meson.build index 82bdaaa1c..d6313e89c 100755 --- a/perun/select/meson.build +++ b/perun/selection/meson.build @@ -1,4 +1,4 @@ -perun_select_dir = perun_dir / 'select' +perun_select_dir = perun_dir / 'selection' perun_select_files = files( '__init__.py', diff --git a/perun/select/whole_repository_selection.py b/perun/selection/whole_repository_selection.py similarity index 98% rename from perun/select/whole_repository_selection.py rename to perun/selection/whole_repository_selection.py index 28baacea5..387ed17a5 100755 --- a/perun/select/whole_repository_selection.py +++ b/perun/selection/whole_repository_selection.py @@ -14,7 +14,7 @@ # Perun Imports from perun.logic import pcs from perun.profiles.native import helpers as profile_helpers -from perun.select.abstract_base_selection import AbstractBaseSelection +from perun.selection.abstract_base_selection import AbstractBaseSelection from perun.utils.structs.common_structs import MinorVersion if TYPE_CHECKING: diff --git a/perun/templates/diff_sections/section_sankey_graph.html.jinja2 b/perun/templates/diff_sections/section_sankey_graph.html.jinja2 deleted file mode 100644 index 5f429bb73..000000000 --- a/perun/templates/diff_sections/section_sankey_graph.html.jinja2 +++ /dev/null @@ -1,38 +0,0 @@ -{% import 'macros/section_title.html.jinja2' as m_title %} -{% import 'macros/section_sankey.html.jinja2' as m_sankey %} - -{% set sankey_info = "Use the tools at the top-right corner of the graph to expand/fold the callers/callees. - Wide red edges are (generally) bad; wide green edges are (generally) good." %} - -
- - {{ m_title.display_title("Sankey Graph", "sankey graph", flame_graphs_info ) }} - - - -
-
-
-
diff --git a/perun/templates/diff_views/flamegraph.html.jinja2 b/perun/templates/diff_views/flamegraph.html.jinja2 index d16896bf9..a68a22ec4 100755 --- a/perun/templates/diff_views/flamegraph.html.jinja2 +++ b/perun/templates/diff_views/flamegraph.html.jinja2 @@ -8,22 +8,47 @@ {% if not offline %} + + {% if chatbot %} + + + + + {% endif %} {% else %} + {% endif %} + + + {% include 'scripts/value_formatters_js.html.jinja2' %} + + {% include 'diff_sections/section_page_title.html.jinja2' %} + + {% include 'diff_components/chatbot.html.jinja2' %} + + + {% include 'diff_components/side_panels/themes_sidepanel.html.jinja2' %} + + {% include 'diff_sections/section_flame_graphs.html.jinja2' %} @@ -43,10 +80,17 @@ - {% include 'scripts/value_formatters_js.html.jinja2' %} + {% include 'scripts/themes_js.html.jinja2' %} {% include 'scripts/flame_graph_js.html.jinja2' %} + {% include 'scripts/chatbot.html.jinja2' %} + + \ No newline at end of file diff --git a/perun/templates/diff_views/report.html.jinja2 b/perun/templates/diff_views/report.html.jinja2 index 85f28afba..05f7ee7d1 100644 --- a/perun/templates/diff_views/report.html.jinja2 +++ b/perun/templates/diff_views/report.html.jinja2 @@ -312,9 +312,6 @@ AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// {% include 'diff_components/traces_table.html.jinja2' %} {% include 'diff_components/trace_details_modal.html.jinja2' %} - - {#% include 'diff_sections/section_sankey_graph.html.jinja2' %#} - {% include 'diff_sections/section_vulnerabilities.html.jinja2' %} diff --git a/perun/templates/scripts/sankey_graph_js.html.jinja2 b/perun/templates/scripts/sankey_graph_js.html.jinja2 deleted file mode 100644 index 610956664..000000000 --- a/perun/templates/scripts/sankey_graph_js.html.jinja2 +++ /dev/null @@ -1,683 +0,0 @@ - \ No newline at end of file diff --git a/perun/templates/style/sankey_graph.css b/perun/templates/style/sankey_graph.css deleted file mode 100644 index 8e3703a4f..000000000 --- a/perun/templates/style/sankey_graph.css +++ /dev/null @@ -1,89 +0,0 @@ -.tag { - display: inline-block; - background-color: #e0e0e0; - border-radius: 4px; - padding: 4px 8px; - margin-right: 8px; - margin-bottom: 8px; -} - -.close-button { - cursor: pointer; -} - -/* courtesy of: https://medium.com/@predragdavidovic10/native-dual-range-slider-html-css-javascript-91e778134816 */ -.range_container { - display: flex; - flex-direction: column; - width: 80%; - margin: 35% auto; -} - -.sliders_control { - position: relative; - min-height: 15px; -} - -.form_control { - position: relative; - display: flex; - justify-content: space-between; - font-size: 14px; - color: #635a5a; -} - -input[type="range"]::-webkit-slider-thumb { - appearance: none; - pointer-events: all; - width: 14px; - height: 14px; - background-color: #fff; - border-radius: 50%; - box-shadow: 0 0 0 1px #C6C6C6; - cursor: pointer; -} - -input[type="range"]::-moz-range-thumb { - appearance: none; - pointer-events: all; - width: 14px; - height: 14px; - background-color: #fff; - border-radius: 50%; - box-shadow: 0 0 0 1px #C6C6C6; - cursor: pointer; -} - -input[type="range"]::-webkit-slider-thumb:hover { - background: #f7f7f7; -} - -input[type="range"]::-webkit-slider-thumb:active { - box-shadow: inset 0 0 3px #387bbe, 0 0 9px #387bbe; -} - -input[type="number"] { - color: #8a8383; - height: 30px; - font-size: 12px; - border: none; -} - -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - opacity: 1; -} - -input[type="range"] { - appearance: none; - height: 2px; - width: 100%; - position: absolute; - background-color: #C6C6C6; - pointer-events: none; -} - -#fromSlider { - height: 0; - z-index: 1; -} \ No newline at end of file diff --git a/perun/utils/common/cli_kit.py b/perun/utils/common/cli_kit.py index f6826032d..a9c2fd478 100644 --- a/perun/utils/common/cli_kit.py +++ b/perun/utils/common/cli_kit.py @@ -8,9 +8,8 @@ # Standard Imports from collections import defaultdict -from importlib import metadata -from typing import Optional, Callable, Any import functools +from importlib import metadata import json import os import platform @@ -18,6 +17,7 @@ import sys import time import traceback +from typing import Optional, Callable, Any # Third-Party Imports import click @@ -26,14 +26,14 @@ # Perun Imports import perun from perun.profiles import native as profile -from perun.logic import commands, store, stats, config, pcs -from perun.utils import exceptions, streams, timestamps, log, metrics +from perun.logic import commands, config, pcs, stats, store +from perun.utils import exceptions, log, metrics, streams, timestamps from perun.utils.common import common_kit from perun.utils.exceptions import ( - VersionControlSystemException, - TagOutOfRangeException, - StatsFileNotFoundException, NotPerunRepositoryException, + StatsFileNotFoundException, + TagOutOfRangeException, + VersionControlSystemException, ) from perun.utils.structs import collect_structs from perun.utils.structs.common_structs import MinorVersion, SortOrder @@ -510,13 +510,7 @@ def lookup_list_of_profiles_callback( :param value: list of profiles :return: list of profiles """ - profiles = [] - aggregation_function = config.lookup_key_recursively("profile.aggregation", default="median") - for prof in value: - loaded_profile = lookup_any_profile_callback(ctx, arg, prof) - loaded_profile.apply(aggregation_function) - profiles.append(loaded_profile) - return profiles + return [lookup_any_profile_callback(ctx, arg, prof) for prof in value] def lookup_any_profile_callback( diff --git a/perun/utils/common/common_kit.py b/perun/utils/common/common_kit.py index 69b9d4660..c19d63488 100644 --- a/perun/utils/common/common_kit.py +++ b/perun/utils/common/common_kit.py @@ -3,9 +3,19 @@ from __future__ import annotations # Standard Imports +import array +import contextlib +import enum +import importlib +import itertools +import operator +import os +import re +import signal from typing import ( Any, Callable, + cast, Iterable, Literal, Optional, @@ -13,21 +23,11 @@ TypeVar, TYPE_CHECKING, ) -import array -import contextlib -import importlib -import itertools -import operator -import os -import re -import signal -import statistics # Third-Party Imports import click # Perun Imports -from perun.postprocess.regression_analysis import tools from perun.utils.exceptions import NotPerunRepositoryException, SignalReceivedException if TYPE_CHECKING: @@ -70,7 +70,6 @@ TEXT_ATTRS: Optional[AttrChoiceType] = None TEXT_EMPH_COLOUR: ColorChoiceType = "green" TEXT_WARN_COLOUR: ColorChoiceType = "red" -AGGREGATIONS: tuple[str, ...] = "sum", "mean", "count", "nunique", "median", "min", "max" # Profile specific stuff SUPPORTED_PROFILE_TYPES: list[str] = ["memory", "mixed", "time"] @@ -121,6 +120,147 @@ ALWAYS_CONFIRM: bool = False DEFAULT_CONFIRMATION: bool = True +# Zero approximation to avoid zero division etc. +APPROX_ZERO: float = 0.000001 + + +class Aggregations(enum.Enum): + """Supported aggregation functions.""" + + SUM = "sum" + MIN = "min" + MAX = "max" + COUNT = "count" + NUNIQUE = "nunique" + MEAN = ("mean", "avg", "average") + MEDIAN = ("median", "med") + + @staticmethod + def supported() -> list[str]: + """Obtain the collection of supported aggregation functions. + + :return: the collection of supported aggregation functions + """ + return ( + [ + Aggregations.SUM.value, + Aggregations.MIN.value, + Aggregations.MAX.value, + Aggregations.COUNT.value, + Aggregations.NUNIQUE.value, + ] + + list(Aggregations.MEAN.value) + + list(Aggregations.MEDIAN.value) + ) + + @staticmethod + def default_name() -> str: + """Provide the default aggregation function name. + + :return: the default aggregation function name + """ + return Aggregations.MEDIAN.value[0] + + @staticmethod + def default() -> Aggregations: + """Provide the default aggregation function. + + :return: the default aggregation function + """ + return Aggregations.MEDIAN + + @classmethod + def from_string(cls, agg_name: str, default: Aggregations | None = None) -> Aggregations: + """Translate a string name to an enum member. + + :param agg_name: the name of the aggregation function. + :param default: the default aggregation to use if the string is not recognized. + If set to `None`, a `KeyError` is raised if the name is not recognized. + + :return: an enum member corresponding to the name. + """ + try: + return cls(agg_name) + except ValueError: + # Handle the aliases. + if agg_name in Aggregations.MEAN.value: + return Aggregations.MEAN + elif agg_name in Aggregations.MEDIAN.value: + return Aggregations.MEDIAN + if default is not None: + return default + raise + + +# Python-native aggregation functions. +AggregationFunc = Callable[[list[float]], float] + + +def mean_agg(data: list[float]) -> float: + """Computes the average value of a collection of numbers. + + :param data: the list to aggregate + + :return: the average value of the `data` elements + """ + try: + return sum(data) / len(data) + except ZeroDivisionError: + # Makes sure empty lists are handled correctly. + return 0.0 + + +def median_agg(data: list[float]) -> float: + """Selects or computes the median value of a collection of numbers. + + The implementation is inspired by the `statistics` standard library, but avoids a lot of + checks that are unnecessary for our use-case. + + :param data: the list to aggregate + + :return: the (possibly interpolated) median value + """ + data.sort() + data_len = len(data) + midpoint = data_len // 2 + try: + if len(data) & 1 == 1: + return data[midpoint] + else: + return (data[midpoint - 1] + data[midpoint]) / 2 + except IndexError: + # Makes sure empty lists are handled correctly. + return 0.0 + + +def nunique_agg(data: list[float]) -> float: + """Counts the number of unique elements in a list. + + :param data: the list to aggregate + + :return: the number of unique elements in `data` + """ + return len(set(data)) + + +def get_aggregation_callable(agg_func: Aggregations) -> AggregationFunc: + """Maps aggregation function to a python-native callable. + + :param agg_func: the aggregation function + :return: a python-native callable + """ + # The built-in methods have complex type signatures. The cast narrows it down to our + # use-case to avoid type hint errors + return { + Aggregations.SUM: cast(AggregationFunc, sum), + Aggregations.MIN: cast(AggregationFunc, min), + Aggregations.MAX: cast(AggregationFunc, max), + Aggregations.COUNT: cast(AggregationFunc, len), + Aggregations.NUNIQUE: nunique_agg, + Aggregations.MEAN: mean_agg, + Aggregations.MEDIAN: median_agg, + }[agg_func] + def perun_confirm(confirm_message: str) -> bool: """Wrapper function for confirming information from user @@ -418,7 +558,7 @@ def safe_division(dividend: float, divisor: float) -> float: try: return dividend / divisor except (ZeroDivisionError, ValueError): - return dividend / tools.APPROX_ZERO + return dividend / APPROX_ZERO def chunkify(generator: Iterable[Any], chunk_size: int) -> Iterable[Any]: @@ -636,26 +776,6 @@ def add_to_sorted( values.pop(0) -def hide_generics(uid: str) -> str: - """Hides the generics in the uid - - This transforms std::> into std::<*> - - :param uid: uid with generics - :return: uid without generics - """ - nesting = 0 - chars = [] - for c in uid: - if c == "<": - nesting += 1 - elif c == ">": - nesting -= 1 - if nesting == 0 or (c == "<" and nesting == 1): - chars.append(c) - return "".join(chars) - - def ensure_type(obj: Any, target_type: Type[T]) -> T: """Ensures that object is of target type @@ -668,27 +788,4 @@ def ensure_type(obj: Any, target_type: Type[T]) -> T: return target_type(obj) # type: ignore -def aggregate_list( - input_list: list[float], - aggregation: Literal["sum", "min", "max", "avg", "mean", "med", "median"], -) -> float: - """Aggregates list of values according to the given function - - :param input_list: list of input values - :param aggregation: named aggregation function - :return: aggregation of the function - """ - if aggregation == "sum": - return sum(input_list) - elif aggregation == "min": - return min(input_list) - elif aggregation == "max": - return max(input_list) - elif aggregation in ("avg", "mean"): - return statistics.mean(input_list) - elif aggregation in ("med", "median"): - return statistics.median(input_list) - assert False, f"Unknown aggregation {aggregation}" - - MODULE_CACHE: dict[str, types.ModuleType] = {} diff --git a/perun/utils/common/diff_kit.py b/perun/utils/common/diff_kit.py index 4d06237e3..98ffff16c 100755 --- a/perun/utils/common/diff_kit.py +++ b/perun/utils/common/diff_kit.py @@ -5,49 +5,52 @@ # Standard Imports import dataclasses import difflib -import os -from typing import Any, Optional, Iterable, Literal, cast, Union +from pathlib import Path +from typing import Any, cast, Iterable, Literal, Optional, TYPE_CHECKING, Union # Third-Party Imports # Perun Imports -from perun.profiles import native as profile from perun.logic import config -from perun.utils import log +from perun.profiles import native as profile, stats, structs from perun.utils.common import common_kit from perun.utils.common.common_kit import ColorVariableType from perun.utils.structs.diff_structs import HeaderDisplayStyle +if TYPE_CHECKING: + from perun.profiles.native import Profile -def save_diff_view( - output_file: Optional[str], - content: str, - output_type: str, - lhs_profile: profile.Profile, - rhs_profile: profile.Profile, -) -> str: - """Saves the content to the output file; if no output file is stated, then it is automatically - generated. - - :param output_file: file, where the content will be stored - :param content: content of the output file - :param output_type: type of the output - :param lhs_profile: baseline profile - :param rhs_profile: target profile - :return: name of the output file - """ - if output_file is None: - lhs_name = os.path.splitext(profile.generate_profile_name(lhs_profile))[0] - rhs_name = os.path.splitext(profile.generate_profile_name(rhs_profile))[0] - output_file = f"{output_type}-diff-of-{lhs_name}-and-{rhs_name}" + ".html" - if not output_file.endswith("html"): - output_file += ".html" +def save_diff_view(output_path: Path, content: str) -> None: + """Saves the content of a diff view to an output file. + + :param output_path: path to the output file. + :param content: the diff view content to save. + """ + if output_path.suffix != ".html": + output_path = Path(f"{output_path}.html") - with open(output_file, "w", encoding="utf-8") as template_out: + with open(output_path, "w", encoding="utf-8") as template_out: template_out.write(content) - return output_file + +def generate_output_filepath( + output_path: Optional[str], output_type: str, lhs_profile: Profile, rhs_profile: Profile +) -> Path: + """Generates a path to the output file using the baseline and target profiles. + + :param output_path: an optional user-specified output path. + :param output_type: type of the output. + :param lhs_profile: baseline profile. + :param rhs_profile: target profile. + + :return: path to the output file. + """ + if output_path is None: + lhs_name = Path(profile.generate_profile_name(lhs_profile)).stem + rhs_name = Path(profile.generate_profile_name(rhs_profile)).stem + return Path(f"{output_type}-diff-of-{lhs_name}-and-{rhs_name}.html") + return Path(output_path) def get_candidate_keys(candidate_keys: Iterable[str]) -> list[str]: @@ -75,85 +78,10 @@ def get_candidate_keys(candidate_keys: Iterable[str]) -> list[str]: return sorted([candidate for candidate in candidate_keys if candidate in allowed_keys]) -def generate_vulnerabilities(prof: profile.Profile) -> list[profile.ProfileHeaderEntry]: - """Generates vulnerabilities from the given profile - - :param prof: profile for which we are generating the specification - :return: vulnerabilities as an entry - """ - machine_info = prof.get("machine", {}) - return [ - profile.ProfileHeaderEntry( - "vulnerabilities", - "?" if "cpu_vulnerabilities" not in machine_info else "", - "CPU vulnerabilities summary.", - machine_info.get("cpu_vulnerabilities", {}), - ) - ] - - -def generate_specification(prof: profile.Profile) -> list[profile.ProfileHeaderEntry]: - """Generates profile specification from the given profile - - :param prof: profile for which we are generating the specification - - :return: the profile specification as a list of entries - """ - command = " ".join([prof["header"]["cmd"], prof["header"]["workload"]]).strip() - exitcode = format_exit_codes(prof["header"].get("exitcode", "?")) - machine_info = prof.get("machine", {}) - return [ - profile.ProfileHeaderEntry( - "origin", - prof.get("origin", "?"), - "The version control version, for which the profile was measured.", - ), - profile.ProfileHeaderEntry( - "profile label", - prof["header"].get("label", "-"), - "A label associated with this profile, if any.", - ), - profile.ProfileHeaderEntry( - "command", command, "The workload / command, for which the profile was measured." - ), - profile.ProfileHeaderEntry( - "exitcode", exitcode, "The exit code that was returned by the underlying command." - ), - profile.ProfileHeaderEntry( - "collector command", - log.collector_to_command(prof.get("collector_info", {})), - "The collector / profiler, which collected the data.", - ), - profile.ProfileHeaderEntry( - "kernel", - machine_info.get("release", "?"), - "The underlying kernel version, where the results were measured.", - ), - profile.ProfileHeaderEntry( - "boot info", - machine_info.get("boot_info", "?"), - "The contents of `/proc/cmdline` containing boot information about kernel", - ), - profile.ProfileHeaderEntry( - "host", machine_info["host"], "The hostname, where the results were measured." - ), - profile.ProfileHeaderEntry( - "cpu (total)", - machine_info.get("cpu", {"total": "?"}).get("total", "?"), - "The total number (physical and virtual) of CPUs available on the host.", - ), - profile.ProfileHeaderEntry( - "memory (total)", - machine_info.get("memory", {"total_ram": "?"}).get("total_ram", "?"), - "The total number of RAM available on the host.", - ), - ] - - def generate_diff_of_headers( - lhs_headers: Iterable[profile.ProfileHeaderEntry], - rhs_headers: Iterable[profile.ProfileHeaderEntry], -) -> tuple[list[profile.ProfileHeaderTuple], list[profile.ProfileHeaderTuple]]: + lhs_headers: Iterable[structs.ProfileMetadataEntry], + rhs_headers: Iterable[structs.ProfileMetadataEntry], +) -> tuple[list[structs.ProfileHeaderTuple], list[structs.ProfileHeaderTuple]]: """Generates diffed headers for lhs (baseline) and rhs (target) profiles. Based on the configuration parameter 'display_style', either all or only different header @@ -200,7 +128,7 @@ def generate_diff_of_headers( def generate_diff_of_stats( - lhs_stats: Iterable[profile.ProfileStat], rhs_stats: Iterable[profile.ProfileStat] + lhs_stats: Iterable[stats.ProfileStat], rhs_stats: Iterable[stats.ProfileStat] ) -> tuple[list[tuple[str, str, str, dict[str, Any]]], list[tuple[str, str, str, dict[str, Any]]]]: """Generates the profile stats with HTML diff styles suitable for an output. @@ -211,13 +139,13 @@ def generate_diff_of_stats( """ # Get all the stats that occur in either lhs or rhs and match those that exist in both stats_map = common_kit.match_dicts_by_keys( - {stats.name: stats for stats in lhs_stats}, {stats.name: stats for stats in rhs_stats} + {stat.name: stat for stat in lhs_stats}, {stat.name: stat for stat in rhs_stats} ) # Iterate the stats and format them according to their diffs lhs_diff, rhs_diff = [], [] for stat_key in sorted(stats_map.keys()): - lhs_stat: profile.ProfileStat | None - rhs_stat: profile.ProfileStat | None + lhs_stat: stats.ProfileStat | None + rhs_stat: stats.ProfileStat | None lhs_stat, rhs_stat = stats_map[stat_key] lhs_info, rhs_info = _generate_diff_of_stats_record(lhs_stat, rhs_stat) lhs_diff.append(lhs_info) @@ -226,8 +154,8 @@ def generate_diff_of_stats( def _generate_missing_entry( - lhs_data: profile.ProfileHeaderEntry | None, rhs_data: profile.ProfileHeaderEntry | None -) -> tuple[profile.ProfileHeaderEntry, profile.ProfileHeaderEntry]: + lhs_data: structs.ProfileMetadataEntry | None, rhs_data: structs.ProfileMetadataEntry | None +) -> tuple[structs.ProfileMetadataEntry, structs.ProfileMetadataEntry]: """Check if both header entries exist and if not, generate the missing one. The missing header entry is generated using the values from the existing one. @@ -243,11 +171,11 @@ def _generate_missing_entry( return lhs_data, rhs_data if lhs_data is not None: # Note: rhs data must be None - return lhs_data, profile.ProfileHeaderEntry(lhs_data.name, "-", "missing header info", {}) + return lhs_data, structs.ProfileMetadataEntry(lhs_data.name, "-", "missing header info", {}) else: # Note: lhs data must be None assert rhs_data is not None - return profile.ProfileHeaderEntry(rhs_data.name, "-", "missing header info", {}), rhs_data + return structs.ProfileMetadataEntry(rhs_data.name, "-", "missing header info", {}), rhs_data def _generate_diff_of_details( @@ -304,7 +232,7 @@ def _generate_diff_of_values( def _generate_diff_of_stats_record( - lhs_stat: profile.ProfileStat | None, rhs_stat: profile.ProfileStat | None + lhs_stat: stats.ProfileStat | None, rhs_stat: stats.ProfileStat | None ) -> tuple[tuple[str, str, str, dict[str, Any]], tuple[str, str, str, dict[str, Any]]]: """Generates a single diffed LHS and RHS profile stats entry. @@ -328,7 +256,7 @@ def _generate_diff_of_stats_record( def _diff_to_html(diff: list[str], start_tag: Literal["+", "-"]) -> str: - """Create a html tag with differences for either + or - changes + """Create an HTML tag with differences for either + or - changes :param diff: diff computed by difflib.ndiff :param start_tag: starting point of the tag @@ -346,9 +274,7 @@ def _diff_to_html(diff: list[str], start_tag: Literal["+", "-"]) -> str: return " ".join(result) -def _stat_description_to_tooltip( - description: str, comparison: profile.ProfileStatComparison -) -> str: +def _stat_description_to_tooltip(description: str, comparison: stats.ProfileStatComparison) -> str: """Transform a stat description into a tooltip by including the comparison type as well. :param description: the original stat description @@ -361,7 +287,7 @@ def _stat_description_to_tooltip( def _emphasize(value: str, color: ColorVariableType) -> str: - """Emphasize a string with a HTML color. + """Emphasize a string with an HTML color. :param value: the string to emphasize :param color: the color to use @@ -391,11 +317,11 @@ def format_exit_codes(exit_code: str | list[str] | list[int]) -> str: def _color_stat_record_diff( - lhs_stat_agg: profile.ProfileStatAggregation, - rhs_stat_agg: profile.ProfileStatAggregation, + lhs_stat_agg: stats.ProfileStatAggregation, + rhs_stat_agg: stats.ProfileStatAggregation, stats_unit: str, compare_key: str, - comparison: profile.ProfileStatComparison, + comparison: stats.ProfileStatComparison, ) -> tuple[str, str]: """Color the stats values on the LHS and RHS according to their difference. @@ -410,17 +336,17 @@ def _color_stat_record_diff( :return: colored LHS and RHS stat values """ # Build a color map for different comparison results - color_map: dict[profile.StatComparisonResult, tuple[ColorVariableType, ColorVariableType]] = { - profile.StatComparisonResult.INVALID: ("wrong", "wrong"), - profile.StatComparisonResult.UNEQUAL: ("wrong", "wrong"), - profile.StatComparisonResult.EQUAL: ("primary", "primary"), - profile.StatComparisonResult.BASELINE_BETTER: ("correct", "wrong"), - profile.StatComparisonResult.TARGET_BETTER: ("wrong", "correct"), + color_map: dict[stats.StatComparisonResult, tuple[ColorVariableType, ColorVariableType]] = { + stats.StatComparisonResult.INVALID: ("wrong", "wrong"), + stats.StatComparisonResult.UNEQUAL: ("wrong", "wrong"), + stats.StatComparisonResult.EQUAL: ("primary", "primary"), + stats.StatComparisonResult.BASELINE_BETTER: ("correct", "wrong"), + stats.StatComparisonResult.TARGET_BETTER: ("wrong", "correct"), } # Compare and color the stat entry - comparison_result = profile.compare_stats(lhs_stat_agg, rhs_stat_agg, compare_key, comparison) + comparison_result = stats.compare_stats(lhs_stat_agg, rhs_stat_agg, compare_key, comparison) baseline_color, target_color = color_map[comparison_result] - if comparison_result == profile.StatComparisonResult.INVALID: + if comparison_result == stats.StatComparisonResult.INVALID: baseline_value, target_value = "invalid comparison", "invalid comparison" else: baseline_value = _format_stat_value(lhs_stat_agg.as_table(compare_key)[0], stats_unit) @@ -471,12 +397,12 @@ class _StatsDiffRecord: name: str = "" value: str = "-" tooltip: str = "missing stat info" - stat_agg: profile.ProfileStatAggregation | None = None + stat_agg: stats.ProfileStatAggregation | None = None details: dict[str, Any] = dataclasses.field(default_factory=dict) @classmethod def from_stat( - cls, stat: profile.ProfileStat | None, other_stat: profile.ProfileStat | None + cls, stat: stats.ProfileStat | None, other_stat: stats.ProfileStat | None ) -> _StatsDiffRecord: """Construct a difference record from a baseline/target profile stat. @@ -494,7 +420,7 @@ def from_stat( unit = f" [{other_stat.unit}]" if other_stat.unit else "" return cls(f"{other_stat.name}{unit}") # The standard construction - stat_agg = profile.aggregate_stats(stat) + stat_agg = stats.aggregate_stats(stat) unit = f" [{stat.unit}]" if stat.unit else "" agg_key = stat_agg.normalize_aggregate_key(stat.aggregate_by) name = f"{stat.name}{unit} " f"({agg_key})" diff --git a/perun/utils/meson.build b/perun/utils/meson.build index 11a4270f9..d26b8bf36 100644 --- a/perun/utils/meson.build +++ b/perun/utils/meson.build @@ -9,6 +9,7 @@ perun_utils_files = files( 'metrics.py', 'streams.py', 'timestamps.py', + 'type_hints.py', ) py3.install_sources( diff --git a/perun/utils/streams.py b/perun/utils/streams.py index 415d2a970..a297d8954 100644 --- a/perun/utils/streams.py +++ b/perun/utils/streams.py @@ -14,18 +14,19 @@ import os from pathlib import Path import re -from typing import Any, BinaryIO, Iterator, IO, Literal, TextIO, TYPE_CHECKING, overload +from typing import Any, BinaryIO, Iterator, IO, Literal, overload, TextIO, TYPE_CHECKING import zlib -if TYPE_CHECKING: - from _typeshed import OpenBinaryMode, OpenTextMode - # Third-Party Imports from ruamel.yaml import YAML # Perun Imports from perun.utils import log +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode + + GzBinaryMode = Literal["r", "rb", "w", "wb", "x", "xb", "a", "ab"] GzTextMode = Literal["rt", "wt", "xt", "at"] @@ -294,3 +295,20 @@ def safely_open_and_log_gz( if fatal_fail: log.error(str(exc), exc) yield None + + +@contextlib.contextmanager +def open_folded_profile(filepath: Path) -> Iterator[TextIO]: + """Open a (possibly gzipped) folded profile for reading. + + Regardless if the file is compressed or not, the file is opened in a text mode and can be read + line by line in a streaming manner. + + :param filepath: a path to the folded profile + :return: the file handle + """ + open_func = safely_open_and_log_gz if filepath.suffix.lower() == ".gz" else safely_open_and_log + + # DO NOT simplify the mode to "r": the gzip library interprets "r" as a binary mode. + with open_func(filepath, "rt", fatal_fail=True, encoding="utf-8") as folded_handle: + yield folded_handle diff --git a/perun/utils/structs/diff_structs.py b/perun/utils/structs/diff_structs.py index 527dc226a..187766899 100644 --- a/perun/utils/structs/diff_structs.py +++ b/perun/utils/structs/diff_structs.py @@ -1,28 +1,19 @@ +"""Showdiff helper structures and constants.""" + from __future__ import annotations # Standard Imports -from collections import defaultdict import enum -from typing import Type, Any, Callable, TYPE_CHECKING # Third-Party Imports # Perun Imports -if TYPE_CHECKING: - from perun.profiles.native import ProfileStat - - -DEFAULT_AGGREGATE_FUNC: str = "median" - -FG_DEFAULT_IMAGE_WIDTH: int = 800 -FG_DEFAULT_MIN_WIDTH: float = 0.1 DEFAULT_MAX_FUNCTION_TRACES: int = 10 DEFAULT_TOP_DIFFS: int = 50 DEFAULT_FUNCTION_THRESHOLD: float = 0.1 DEFAULT_TRACE_THRESHOLD: float = 0.001 -DEFAULT_SQUASH_RE: str = r".*" class HeaderDisplayStyle(enum.Enum): @@ -46,44 +37,3 @@ def default() -> str: :return: the default display style """ return HeaderDisplayStyle.FULL.value - - -def singleton_class(cls: Type[Any]) -> Callable[[], Config]: - """Helper class for creating singleton objects""" - instances = {} - - def getinstance() -> Config: - """Singleton instance""" - if cls not in instances: - instances[cls] = cls() - return instances[cls] - - return getinstance - - -@singleton_class -class Config: - """Singleton config for generation of sankey graphs - - :ivar trace_is_inclusive: whether then the amounts are distributed among the whole traces - """ - - DefaultTopN: int = 10 - DefaultRelativeThreshold: float = 0.01 - DefaultHeightCoefficient: int = 50 - - def __init__(self) -> None: - """Initializes the config - - By default, we consider that the traces are not inclusive - """ - self.trace_is_inclusive: bool = False - self.top_n_traces: int = self.DefaultTopN - self.relative_threshold = self.DefaultRelativeThreshold - self.max_seen_trace: int = 0 - self.max_per_resource: dict[str, float] = defaultdict(float) - self.minimize: bool = False - self.profile_stats: dict[str, list[ProfileStat]] = { - "baseline": [], - "target": [], - } diff --git a/perun/utils/structs/meson.build b/perun/utils/structs/meson.build index 21947fbc3..a3e932b61 100644 --- a/perun/utils/structs/meson.build +++ b/perun/utils/structs/meson.build @@ -7,6 +7,7 @@ perun_utils_structs_files = files( 'common_structs.py', 'diff_structs.py', 'postprocess_structs.py', + 'view_structs.py', ) py3.install_sources( diff --git a/perun/utils/structs/view_structs.py b/perun/utils/structs/view_structs.py new file mode 100644 index 000000000..53aa96d7f --- /dev/null +++ b/perun/utils/structs/view_structs.py @@ -0,0 +1,196 @@ +"""Helper structures and constants for (diff) view modules.""" + +from __future__ import annotations + +# Standard Imports +from typing import Any, ClassVar + +# Third-Party Imports + +# Perun Imports + + +DEFAULT_SQUASH_RE: str = r".*" + + +class FlameGraphSettings: + """A collection of flamegraph parameters and settings to be used when rendering flamegraphs. + + :ivar width: the width of the flamegraph image + :ivar height: the height of each function frame in the flamegraph + :ivar min_width: the minimum width of a function frame, either pixels or percentage of time + :ivar maxtrace: the longest trace in the flamegraph, should account for the min_width filtering + :ivar fonttype: the font type + :ivar fontsize: the font size + :ivar countname: the resource type used in the profile, e.g., samples or CPU cycles + :ivar colors: the color palette to use for frames + :ivar bgcolors: the image background color + :ivar inverted: whether an icicle graph should be rendered instead + :ivar rootnode: the root node name + :ivar subrootnode: the sub-root node name + :ivar total: the total amount of consumed resources + :ivar normalize: normalize the sample counts in differential graphs + :ivar parallelize: parallelize the creation of flamegraph grids + :ivar use_perl: use the Perl variants of flame graph scripts + """ + + __slots__ = ( + "width", + "height", + "min_width", + "maxtrace", + "fonttype", + "fontsize", + "countname", + "colors", + "bgcolors", + "inverted", + "rootnode", + "subrootnode", + "total", + "normalize", + "parallelize", + "use_perl", + ) + + # Default flamegraph parameters reconstructed from the flamegraph.pl script. + DefaultImageWidth: ClassVar[int] = 1200 + DefaultFrameHeight: ClassVar[int] = 16 + DefaultMinWidth: ClassVar[str] = "0.1" + DefaultMaxTrace: ClassVar[int] = 0 + DefaultFontType: ClassVar[str] = "Verdana" + DefaultFontSize: ClassVar[int] = 12 + DefaultCountName: ClassVar[str] = "samples" + DefaultColors: ClassVar[str] = "hot" + DefaultBgColors: ClassVar[str] = "" + DefaultInverted: ClassVar[bool] = False + DefaultRootNode: ClassVar[str] = "all" + DefaultSubRootNode: ClassVar[str] = "subtotal" + DefaultTotal: ClassVar[int] = 0 + + # The map links the attribute names and their default values for easier iteration over the + # keyword parameters. This map ignores the flags, e.g., 'inverted'. + KwAttributeMap: ClassVar[dict[str, int | str]] = { + "width": DefaultImageWidth, + "height": DefaultFrameHeight, + "min_width": DefaultMinWidth, + "maxtrace": DefaultMaxTrace, + "fonttype": DefaultFontType, + "fontsize": DefaultFontSize, + "countname": DefaultCountName, + "colors": DefaultColors, + "bgcolors": DefaultBgColors, + "rootnode": DefaultRootNode, + "subrootnode": DefaultSubRootNode, + "total": DefaultTotal, + } + + def __init__( + self, + width: int = DefaultImageWidth, + height: int = DefaultFrameHeight, + min_width: str = DefaultMinWidth, + maxtrace: int = DefaultMaxTrace, + fonttype: str = DefaultFontType, + fontsize: int = DefaultFontSize, + countname: str = DefaultCountName, + colors: str = DefaultColors, + bgcolors: str = DefaultBgColors, + inverted: bool = DefaultInverted, + rootnode: str = DefaultRootNode, + subrootnode: str = DefaultSubRootNode, + total: int = DefaultTotal, + normalize: bool = False, + parallelize: bool = True, + use_perl_scripts: bool = False, + **_: Any, + ) -> None: + """ + :param width: the width of the flamegraph image + :param height: the height of each function frame in the flamegraph + :param min_width: the minimum width of a function frame, either pixels or percentage of time + :param maxtrace: the longest trace in the flamegraph after the min_width filtering + :param fonttype: the font type + :param fontsize: the font size + :param countname: the resource type used in the profile, e.g., samples or CPU cycles + :param colors: the color palette to use for frames + :param bgcolors: the image background color + :param inverted: whether an icicle graph should be rendered instead + :param rootnode: the root node name + :param subrootnode: the sub-root node name + :param total: the total amount of consumed resources + :param parallelize: parallelize the creation of flamegraph grids + :param use_perl_scripts: use the Perl variants of flame graph scripts + """ + # We call the type conversion functions since the parameters may be supplied from CLI + # where the types do not necessarily have to match. + self.width: int = int(width) + self.height: int = int(height) + self.min_width: str = str(min_width) + self.maxtrace: int = int(maxtrace) + self.fonttype: str = str(fonttype) + self.fontsize: int = int(fontsize) + self.countname: str = str(countname) + self.colors: str = str(colors) + self.bgcolors: str = str(bgcolors) + self.inverted: bool = bool(inverted) + self.rootnode: str = str(rootnode) + self.subrootnode: str = str(subrootnode) + self.total: int = int(total) + self.normalize: bool = bool(normalize) + + self.parallelize: bool = parallelize + self.use_perl = use_perl_scripts + + @classmethod + def from_cli(cls, **cli_kwargs: Any) -> FlameGraphSettings: + """Initialize the flamegraph settings from CLI and init parameters. + + The parameters supplied through CLI will have a 'flamegraph_' prefix. Parameters supplied + additionally by the caller (e.g., to overwrite certain CLI parameters) do not have to use + the prefix. However, the order of specification matters: in order to overwrite CLI + parameters, the additional parameters need to be specified after the CLI parameters. + + :param cli_kwargs: the CLI parameters + :return: an initialized FlameGraphSettings instance + """ + return cls( + **{ + arg_name.replace("flamegraph_", ""): arg_value + for arg_name, arg_value in cli_kwargs.items() + if arg_value is not None + } + ) + + def get_nondefault_kw_attributes(self) -> dict[str, str | int]: + """Construct a collection of keyword attributes that have non-default values. + + :return: a dictionary of all keyword attributes with non-default values + """ + params: dict[str, str | int] = {} + for attr_name, attr_default_value in self.KwAttributeMap.items(): + if (value := getattr(self, attr_name)) != attr_default_value: + params[attr_name] = value + return params + + def compute_minwidth_threshold(self) -> float: + """Compute the minimum width threshold for flamegraph blocks to be displayed. + + The threshold is needed to correctly compute the maxtrace value so that flamegraphs in + a grid are correctly aligned and have the appropriate height. + + Reconstructed from the flamegraph.pl script. + + :return: the minimum width threshold + """ + try: + # The minimum width was provided as a percentage + if self.min_width.endswith("%"): + return self.total * float(self.min_width[:-1]) / 100 + # The minimum width is set in pixels + x_padding: int = 10 + width_per_time: float = (self.width - 2 * x_padding) / self.total + return float(self.min_width) / width_per_time + except ZeroDivisionError: + # No total provided, we set the threshold so that it does not filter anything. + return 0.0 diff --git a/perun/utils/type_hints.py b/perun/utils/type_hints.py new file mode 100644 index 000000000..74d44d5d2 --- /dev/null +++ b/perun/utils/type_hints.py @@ -0,0 +1,17 @@ +"""Type annotation helpers.""" + +from __future__ import annotations + +# Standard Imports +from typing import TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports + +if TYPE_CHECKING: + import tempfile + + # The tempfile.NamedTemporaryFile context manager does not have a public type hint. + # Hence, we use the private tempfile object and alias it to avoid redundant warnings. + TempTextIO = tempfile._TemporaryFileWrapper[str] diff --git a/perun/view/bars/factory.py b/perun/view/bars/factory.py index 5ff297bce..9d326f0a1 100644 --- a/perun/view/bars/factory.py +++ b/perun/view/bars/factory.py @@ -9,7 +9,7 @@ import holoviews as hv # Perun Imports -from perun.profiles.native import convert +from perun.profiles.conversions import native_pandas from perun.utils.common import view_kit if TYPE_CHECKING: @@ -47,7 +47,7 @@ def create_from_params( view_kit.lazy_init_holoviews() # Convert profile to pandas data grid - data_frame = convert.resources_to_pandas_dataframe(profile) + data_frame = native_pandas.resources_to_pandas_dataframe(profile) data_frame.sort_values([per_key, by_key], inplace=True) # Holoviews improperly implements pandas aggregation for non-numeric dims. Their aggregation diff --git a/perun/view/bars/run.py b/perun/view/bars/run.py index 1959e2e0a..23b41b10d 100644 --- a/perun/view/bars/run.py +++ b/perun/view/bars/run.py @@ -9,11 +9,11 @@ import click # Perun Imports -import perun.view.bars.factory as bars_factory from perun.profiles import native as profile_factory from perun.utils import log from perun.utils.common import cli_kit, common_kit, view_kit from perun.utils.exceptions import InvalidParameterException +import perun.view.bars.factory as bars_factory def process_title(ctx: click.Context, _: click.Option, value: str) -> str: @@ -43,9 +43,9 @@ def process_title(ctx: click.Context, _: click.Option, value: str) -> str: @click.argument( "func", required=False, - default="sum", + default=common_kit.Aggregations.SUM.value, metavar="", - type=click.Choice(common_kit.AGGREGATIONS), + type=click.Choice(common_kit.Aggregations.supported()), ) @click.option( "--of", diff --git a/perun/view/flamegraph/__init__.py b/perun/view/flamegraph/__init__.py index 570188615..cf5f30120 100644 --- a/perun/view/flamegraph/__init__.py +++ b/perun/view/flamegraph/__init__.py @@ -1,8 +1,10 @@ -"""`Flame graph` shows the relative consumption of resources w.r.t. to the -trace of the resource origin. Currently it is limited to `memory` profiles -(however, the generalization of the module is in plan). The usage of flame -graphs is for faster localization of resource consumption hot spots and -bottlenecks. +"""`Flame graph` shows the relative consumption of resources w.r.t. to the trace of the resource +origin. The usage of flame graphs is for faster localization of resource consumption hot spots +and bottlenecks. """ -SUPPORTED_PROFILES = ["memory"] +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) + +SUPPORTED_PROFILES = ["memory", "time"] diff --git a/perun/view/flamegraph/__init__.pyi b/perun/view/flamegraph/__init__.pyi new file mode 100644 index 000000000..69c856647 --- /dev/null +++ b/perun/view/flamegraph/__init__.pyi @@ -0,0 +1,20 @@ +from .core import ( + generate_flamegraph as generate_flamegraph, + generate_title as generate_title, + build_flamegraph_command as build_flamegraph_command, + build_diff_flamegraph_commands as build_diff_flamegraph_commands, +) + +from .grid import ( + FlameGraphGrid as FlameGraphGrid, + FlameGraphGridCommands as FlameGraphGridCommands, + FlameGraphGridBuilder as FlameGraphGridBuilder, + FlameGraphGridSerialBuilder as FlameGraphGridSerialBuilder, + FlameGraphGridParallelBuilder as FlameGraphGridParallelBuilder, + build_flamegraph_grid_serially as build_flamegraph_grid_serially, + build_flamegraph_grid as build_flamegraph_grid, + build_flamegraph_grid_commands as build_flamegraph_grid_commands, + escape_flamegraph_svg as escape_flamegraph_svg, +) + +from .run import flamegraph as flamegraph diff --git a/perun/view/flamegraph/core.py b/perun/view/flamegraph/core.py new file mode 100644 index 000000000..cab467bf9 --- /dev/null +++ b/perun/view/flamegraph/core.py @@ -0,0 +1,175 @@ +"""A module for generating a single Flame graph visualization.""" + +from __future__ import annotations + +# Standard Imports +import pathlib +import tempfile +from typing import Any, Iterator, Optional + +# Third-Party Imports + +# Perun Imports +from perun.profiles import structs +from perun.profiles.folded import postprocess +from perun.utils.common import script_kit +from perun.utils.external import processes +from perun.utils.structs.view_structs import FlameGraphSettings + + +def generate_flamegraph( + folded_stream: Iterator[tuple[str, int]], + output_path: pathlib.Path, + title: str, + settings: FlameGraphSettings, + postprocess_params: structs.PostprocessParameters, +) -> None: + """Draws and saves a flamegraph to a file. + + :param folded_stream: an input stream of folded profile records (stack trace, consumption) + :param output_path: a path to the output .svg file containing the flame graph + :param title: the title of the flame graph + :param settings: parameters forwarded to the flame graph scripts + :param postprocess_params: profile postprocessing parameters + """ + + with tempfile.NamedTemporaryFile(mode="w+") as folded_file: + for trace, count in postprocess.postprocess_folded_records( + folded_stream, postprocess_params + ): + folded_file.write(f"{trace} {count}\n") + folded_file.flush() + fg_cmd = build_flamegraph_command(pathlib.Path(folded_file.name), settings, title) + # TODO: hack. We want to redirect the output of the flamegraph script directly to file + # without storing it in a temporary string. Hence, we use a non-blocking subprocess helper + # which allows us to set the output pipe to a file handle. + with ( + open(output_path, "w") as output_file, + processes.nonblocking_subprocess(fg_cmd, {"stdout": output_file}), + ): + pass + + +def generate_title(profile_header: dict[str, Any]) -> str: + """Generate a title for flame graph based on the profile header. + + :param profile_header: the profile header + + :return: the title of the flame graph + """ + profile_type = profile_header["type"] + cmd, workload = (profile_header["cmd"], profile_header["workload"]) + return f"{profile_type} consumption of {cmd} {workload}" + + +def build_flamegraph_command( + input_path: Optional[pathlib.Path], + settings: FlameGraphSettings, + title: str, + *new_flags: str, + **override_kwargs: Any, +) -> str: + """Create a command to generate a flame graph. + + :param input_path: a path to the file with folded flame graph data; may be omitted in which case + the input data should be supplied to the flamegraph process via stdin + :param settings: flamegraph configuration parameters and flags + :param title: the title of the flame graph + :param new_flags: additional flags that should be passed to the flamegraph script + :param override_kwargs: additional parameters that should extend or override the parameter + values stored in the settings object + + :return: the command for generating a flame graph + """ + + suffix = ".pl" if settings.use_perl else ".py" + cmd = [ + script_kit.get_script(f"flamegraph{suffix}"), + str(input_path) if input_path is not None else "", + "--title", + f"'{title}'", + ] + # Extend the command with parameters. + cmd.extend(_add_flamegraph_params(settings, *new_flags, **override_kwargs)) + return " ".join(cmd) + + +def build_diff_flamegraph_commands( + baseline: pathlib.Path, + target: pathlib.Path, + settings: FlameGraphSettings, + title: str, + *new_flags: str, + **override_kwargs: Any, +) -> tuple[str, str]: + """Create diffing and flamegraph commands to generate a differential flame graph. + + :param baseline: a path to the file with baseline folded flame graph data + :param target: a path to the file with target folded flame graph data + :param settings: flamegraph configuration parameters and flags + :param title: the title of the flame graph + :param new_flags: additional flags that should be passed to the flamegraph script + :param override_kwargs: additional parameters that should extend or override the parameter + values stored in the settings object + + :return: the difffolded and flamegraph commands for generating a differential flame graph + """ + if settings.use_perl: + normalize_flag = "" + if settings.normalize or "normalize" in new_flags: + normalize_flag = "-n" + diff_cmd = f"{script_kit.get_script('difffolded.pl')} {normalize_flag} {baseline} {target}" + fg_cmd = build_flamegraph_command(None, settings, title, *new_flags, **override_kwargs) + else: + diff_cmd = "" + extended_flags = list(new_flags) + if settings.normalize and "normalize" not in new_flags: + extended_flags.append("normalize") + # We take advantage of our diff_flamegraph helper script here. + cmd = [ + script_kit.get_script("diff_flamegraph.py"), + str(baseline), + str(target), + "--title", + f"'{title}'", + ] + cmd.extend(_add_flamegraph_params(settings, *new_flags, **override_kwargs)) + fg_cmd = " ".join(cmd) + return diff_cmd, fg_cmd + + +def _add_flamegraph_params( + settings: FlameGraphSettings, + *new_flags: str, + **override_kwargs: Any, +) -> list[str]: + """Construct a collection of flags and keyword arguments for a flamegraph script. + + :param settings: flamegraph configuration parameters and flags + :param new_flags: additional flags that should be passed to the flamegraph script + :param override_kwargs: additional parameters that should extend or override the parameter + values stored in the settings object + + :return: a list of flags and parameters + """ + params: list[str] = [] + # Extend the command with flags. + flags: set[str] = set(new_flags) + if settings.inverted and "inverted" not in new_flags: + flags.add("inverted") + params.extend(f"--{flag}" for flag in flags) + + # Extend the command with flamegraph parameters that have non-default values. + # Although we could supply the parameters with default values as well, it would needlessly + # clutter the resulting command. + kw_params: dict[str, str | int] = settings.get_nondefault_kw_attributes() + # The parameters may be overridden and extended by the caller. + kw_params.update(override_kwargs) + if settings.use_perl: + # Sub-root is unsupported by Perl. + del kw_params["subrootnode"] + for key, val in kw_params.items(): + if val is not None: + params.append(f"--{key}") + params.append(f"'{val}'") + return params diff --git a/perun/view/flamegraph/flamegraph.py b/perun/view/flamegraph/flamegraph.py deleted file mode 100644 index 52b26734c..000000000 --- a/perun/view/flamegraph/flamegraph.py +++ /dev/null @@ -1,287 +0,0 @@ -"""This module provides wrapper for the Flame graph visualization""" - -from __future__ import annotations - -# Standard Imports -from contextlib import contextmanager, nullcontext -from dataclasses import dataclass, field -from pathlib import Path -import tempfile -from typing import Any, Iterator - -# Third-Party Imports - -# Perun Imports -from perun.utils import mapping -from perun.utils.common import script_kit -from perun.utils.external import commands - - -@contextmanager -def fg_optional_tempfile(flame_data: list[str] | Path) -> Iterator[Path]: - """A helper context manager that wraps flame graph data into a file path. - - If the provided flame graph data are already stored in a file, the file path is wrapped in - a context manager. Otherwise, a new temporary file is created, the data are stored in it - and the context manager wraps the path. The created temporary file will be deleted when the - context manager wrapper exits. - - :param flame_data: the flame graph data to wrap - - :return: a wrapper context manager - """ - if isinstance(flame_data, Path): - # Wrap the path in a nullcontext so that we can use it as if it was a context manager - with nullcontext(flame_data) as tmp: - yield tmp - else: - # Save the flame data into a temporary file - with tempfile.NamedTemporaryFile(mode="w") as tmp: - tmp.write("".join(flame_data)) - # The flush ensures that all data are in the file when the flamegraph scripts read them - tmp.flush() - yield Path(tmp.name) - - -def draw_flame_graph( - flame_data: list[str] | Path, - title: str, - units: str = "samples", - *fg_flags: str, - **fg_kwargs: Any, -) -> str: - """Draw Flame graph from flame data. - - To create Flame graphs we use perl script created by Brendan Gregg. - https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl - - If the flame graph data are provided directly, they are first stored in a temporary file that - can be read by the flame graph scripts. - - :param flame_data: the data to generate the flame graph from - :param title: title of the flame graph - :param units: the units of the flame graph data - :param fg_flags: additional flags to pass to the flamegraph.pl script - :param fg_kwargs: additional parameters forwarded to the flamegraph.pl - - :return: the generated flame graph - """ - # converting profile format to format suitable to Flame graph visualization - with fg_optional_tempfile(flame_data) as tmp: - # We extend the flags with 'cp' for consistent palette - cmd = build_flamegraph_command(tmp, title, units, "cp", *fg_flags, **fg_kwargs) - out, _ = commands.run_safely_external_command(cmd) - return out.decode("utf-8") - - -def draw_differential_flame_graph( - lhs_flame_data: list[str] | Path, - rhs_flame_data: list[str] | Path, - title: str, - units: str = "samples", - *fg_flags: str, - use_perl_scripts: bool = True, - **fg_kwargs: Any, -) -> str: - """Draws a lhs->rhs differential flame graph. - - If the LHS or RHS flame graph data are provided directly, they are first stored in temporary - files that can be read by the flame graph scripts. - - :param lhs_flame_data: the data of the baseline profile to generate the flame graph diff from - :param rhs_flame_data: the data of the target profile to generate the flame graph diff from - :param title: title of the flame graph - :param units: the units of the flame graph data - :param fg_flags: additional flags to pass to the flamegraph.pl script - :param use_perl_scripts: invoke the original Perl flamegraph script - :param fg_kwargs: additional parameters forwarded to the flamegraph.pl script - - :return: the lhs->rhs differential flame graph - """ - with ( - fg_optional_tempfile(lhs_flame_data) as lhs_flame, - fg_optional_tempfile(rhs_flame_data) as rhs_flame, - ): - suffix = ".pl" if use_perl_scripts else ".py" - diff_cmd = " ".join( - [ - script_kit.get_script(f"difffolded{suffix}"), - "-n", - str(lhs_flame), - str(rhs_flame), - ] - ) - fg_cmd = build_flamegraph_command( - None, title, units, *fg_flags, use_perl_scripts=use_perl_scripts, **fg_kwargs - ) - out, _ = commands.run_safely_external_command(f"{diff_cmd} | {fg_cmd}") - return out.decode("utf-8") - - -def build_flamegraph_command( - input_path: Path | None, - title: str, - units: str = "samples", - *flags: str, - total: int | None = None, - use_perl_scripts: bool = True, - **kwargs: Any, -) -> str: - """Creates the flamegraph.pl command that generates a (possibly differential) flame graph. - - :param input_path: path to the file with folded flame graph data - :param title: title of the flame graph - :param units: the units of the flame graph data - :param flags: additional flags to pass to the flamegraph.pl script - :param total: the 'total' parameter of the flamegraph.pl script, if provided - :param use_perl_scripts: invoke the original Perl flamegraph script - :param kwargs: additional parameters forwarded to the flamegraph.pl script - - :return: the resulting command for generating a flame graph - """ - suffix = ".pl" if use_perl_scripts else ".py" - cmd = [ - script_kit.get_script(f"flamegraph{suffix}"), - str(input_path) if input_path is not None else "", - "--title", - f"'{title}'", - "--countname", - f"'{units}'", - "--reverse", - ] - # Additional flags - cmd.extend(f"--{flag}" for flag in flags) - # Additional parameters - for key, val in kwargs.items(): - if val is True: - # This kwarg is a flag - cmd.append(f"--{key}") - elif val: - # This kwarg is a parameter - cmd.append(f"--{key}") - cmd.append(f"'{val}'") - # The 'total' parameter needs special handling: add a rootnode that scales the flamegraph - # on X axis according to the 'total' value - if total is not None and total > 0.0: - cmd.extend(["--total", str(total), "--rootnode", "'Maximum (Baseline, Target)'"]) - return " ".join(cmd) - - -def generate_title(profile_header: dict[str, Any]) -> str: - """Generate a title for flame graph based on the profile header. - - :param profile_header: the profile header - - :return: the title of the flame graph - """ - profile_type = profile_header["type"] - cmd, workload = (profile_header["cmd"], profile_header["workload"]) - return f"{profile_type} consumption of {cmd} {workload}" - - -def get_units(profile_key: str) -> str: - """Obtain the units of the flame graph based on the profile key. - - :param profile_key: the profile key - - :return: the units of the flame graph - """ - return mapping.get_unit(mapping.get_readable_key(profile_key)) - - -def compute_max_traces( - flame_data: list[str], - img_width: float, - min_width: str, -) -> tuple[int, int, int]: - """Recreate Brendan Gregg's max trace depth computation for correct flamegraph height. - - This function provides the maximum length of traces and filtered maximum that takes into - account the filtering of subtraces based on their sample count, as well as the total number of - samples collected. - - :param flame_data: the flame graph data - :param img_width: the width of the graph image - :param min_width: the minimum width of the flame graph rectangles that will be drawn - - :return: the maximum trace length, the maximum length of traces that are being drawn, the total - number of samples collected - """ - max_unfiltered_trace = 0 - flame_stacks: _PerfStackRecord = _PerfStackRecord() - # Process each flame data record - for stack_trace in flame_data: - # Update the perf stack traces representation with this record - stack_str, samples = stack_trace.rsplit(maxsplit=1) - stack = stack_str.split(";") - max_unfiltered_trace = max(len(stack), max_unfiltered_trace) - flame_stacks.update_stack(stack, int(float(samples))) - min_width_f = _compute_minwidth_samples(flame_stacks.inclusive_samples, img_width, min_width) - return max_unfiltered_trace, flame_stacks.filter(min_width_f), flame_stacks.inclusive_samples - - -def _compute_minwidth_samples(max_resource: float, img_width: float, min_width: str) -> float: - """Computes the minimum width threshold for flamegraph blocks to be displayed. - - Reconstructed from the flamegraph.pl script. - - :param max_resource: the total number of samples collected - :param img_width: the width of the graph image - :param min_width: the minimum width of the flame graph rectangles that will be drawn - - :return: the minimum width threshold - """ - try: - if min_width.endswith("%"): - return max_resource * float(min_width[:-1]) / 100 - else: - x_padding: int = 10 - width_per_time: float = (img_width - 2 * x_padding) / max_resource - return float(min_width) / width_per_time - except ZeroDivisionError: - # Unknown or invalid max_resource, we set the threshold so that it does not filter anything - return 0.0 - - -@dataclass -class _PerfStackRecord: - """Representation of a single perf stack frame record from flame data. - - :ivar inclusive_samples: the number of samples in which a given stack frame record occurred - :ivar nested: callee stack frame records - """ - - inclusive_samples: int = 0 - nested: dict[str, _PerfStackRecord] = field(default_factory=dict) - - def update_stack(self, stack: list[str], samples: int) -> None: - """Update the stack traces with new flame data record. - - :param stack: the stack trace - :param samples: the number of samples that contain this stack trace - """ - self.inclusive_samples += samples - if stack: - func = stack.pop() - self.nested.setdefault(func, _PerfStackRecord()).update_stack(stack, samples) - - def filter(self, min_width: float) -> int: - """Filter the stack subtraces (recursively) that do not meet the minimum sample threshold. - - :param min_width: the samples threshold - - :return: the maximum stack length after the filtering - """ - depth: int = 0 - delete_list: list[str] = [] - for nested_func, nested_record in self.nested.items(): - if nested_record.inclusive_samples < min_width: - # This nested subtrace does not meet the samples threshold, we cut the entire - # subtrace off including its subtraces. - delete_list.append(nested_func) - else: - # This subtrace meets the threshold, check recursively - depth = max(depth, nested_record.filter(min_width) + 1) - for del_key in delete_list: - del self.nested[del_key] - return depth diff --git a/perun/view/flamegraph/grid.py b/perun/view/flamegraph/grid.py new file mode 100644 index 000000000..e51215cbc --- /dev/null +++ b/perun/view/flamegraph/grid.py @@ -0,0 +1,555 @@ +"""A module for generating grids of Flame graphs.""" + +from __future__ import annotations + +# Standard Imports +from collections.abc import Iterator +import contextlib +import dataclasses +from pathlib import Path +import re +from subprocess import PIPE, TimeoutExpired +import tempfile +from typing import Any, ClassVar, Optional, Protocol, Type, TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports +from perun.utils import log, type_hints +from perun.utils.external import commands, processes +from perun.utils.structs.view_structs import FlameGraphSettings +from perun.view.flamegraph import core + +if TYPE_CHECKING: + from subprocess import Popen + from types import TracebackType + + +@dataclasses.dataclass +class FlameGraphGrid: + """A collection of flamegraphs that form a 2x2 grid of baseline, target and their diffs. + + The stored flamegraphs are already properly escaped and as such may be directly embedded into + an HTML report. + + :ivar baseline: the baseline flamegraph + :ivar target: the target flamegraph + :ivar baseline_target_diff: the baseline-target difference flamegraph + :ivar target_baseline_diff: the target-baseline difference flamegraph + """ + + __slots__ = "baseline", "target", "baseline_target_diff", "target_baseline_diff" + + # The default flamegraph titles used when generating the flamegraphs. + DefaultTitles: ClassVar[tuple[str, str, str, str]] = ( + "Baseline Flamegraph", + "Target Flamegraph", + "Baseline > Target Diff Flamegraph", + "Target > Baseline Diff Flamegraph", + ) + # The tags used for escaping flamegraphs in the grid. + EscapeTags: ClassVar[tuple[str, str, str, str]] = ("lhs_0", "rhs_0", "lhs_diff_0", "rhs_diff_0") + + def __init__( + self, + baseline: str = "", + target: str = "", + baseline_target_diff: str = "", + target_baseline_diff: str = "", + ) -> None: + """ + :param baseline: the baseline flamegraph + :param target: the target flamegraph + :param baseline_target_diff: the baseline-target difference flamegraph + :param target_baseline_diff: the target-baseline difference flamegraph + """ + self.baseline: str = baseline + self.target: str = target + self.baseline_target_diff: str = baseline_target_diff + self.target_baseline_diff: str = target_baseline_diff + + def __setitem__(self, index: int, flame_graph: str) -> None: + """Set a new flamegraph according to the index. + + A helper method for access to individual flamegraphs, e.g., in a loop. Supported indices + are [0, 3] and correspond to the baseline, target, baseline_target_diff, and + target_baseline_diff flamegraphs. + + :param index: the index of the flamegraph; must be within the [0, 3] range + :param flame_graph: the new flamegraph + """ + setattr(self, self.__slots__[index], flame_graph) + + def __getitem__(self, index: int) -> str: + """Get the flamegraph currently stored at an index. + + A helper method for access to individual flamegraphs, e.g., in a loop. Supported indices + are [0, 3] and correspond to the baseline, target, baseline_target_diff, and + target_baseline_diff flamegraphs. + + :param index: the index of the flamegraph to retrieve; must be within the [0, 3] range + + :return: the flamegraph currently stored at the index + """ + return getattr(self, self.__slots__[index]) + + def copy_from(self, grid: FlameGraphGrid) -> None: + """Copies flamegraphs from another grid into this grid. + + :param grid: the other grid to copy the flamegraphs from + """ + self.baseline = grid.baseline + self.target = grid.target + self.baseline_target_diff = grid.baseline_target_diff + self.target_baseline_diff = grid.target_baseline_diff + + +@dataclasses.dataclass +class FlameGraphGridCommands: + """A collection of commands and options for generating a flamegraph grid. + + :ivar escape_graphs: a flag indicating whether the SVGs should be escaped for HTML embedding + :ivar baseline: the command to generate the baseline flamegraph + :ivar target: the command to generate the target flamegraph + :ivar baseline_target_difffolded: the command to diff baseline and target folded profiles + :ivar baseline_target_fg_diff: the command to generate baseline-target difference flamegraph + :ivar target_baseline_difffolded: the command to diff target and baseline folded profiles + :ivar target_baseline_fg_diff: the command to generate target-baseline difference flamegraph + """ + + __slots__ = ( + "escape_graphs", + "baseline", + "target", + "baseline_target_difffolded", + "baseline_target_fg_diff", + "target_baseline_difffolded", + "target_baseline_fg_diff", + ) + + escape_graphs: bool + baseline: str + target: str + baseline_target_difffolded: str + baseline_target_fg_diff: str + target_baseline_difffolded: str + target_baseline_fg_diff: str + + +class FlameGraphGridBuilder(Protocol): + """A context manager interface for flamegraph grid creation. + + This context manager interface serves as a wrapper to simplify and unify the code for both + serial and parallel creation of flamegraph grids. Although a flamegraph grid can be built + serially without the need for a context manager, building the grid in parallel background + processes needs a context manager that properly terminates and cleanup the processes and + other resources when needed. + """ + + def __enter__(self): ... + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: ... + + +class FlameGraphGridSerialBuilder: + """A context manager wrapper over serial flamegraph grid creation. + + The flamegraph grid creation begins when the class is instantiated. When the context manager + is entered, the flamegraph grid is already available. + + See FlameGraphGridBuilder for more details. + """ + + def __init__( + self, + grid: FlameGraphGrid, + grid_commands: FlameGraphGridCommands, + ) -> None: + """ + :param grid: [out] a reference to the grid object for the generated flamegraphs; context + managers cannot explicitly return values, hence we use an output parameter + :param grid_commands: the commands for generating a flamegraph grid + """ + # We cannot simply assign the grids as it would not propagate outside the object. + grid.copy_from(build_flamegraph_grid_serially(grid_commands)) + + def __enter__(self): + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + return None + + +class FlameGraphGridParallelBuilder(contextlib.ExitStack): + """A context manager for parallel background flamegraph grid creation. + + Each flamegraph in the grid is created using a separate process that runs in the background. + The processes are spawned when the class is instantiated and terminated (waited for) when the + context manager scope is about to be exited. Only the __exit__ method is blocking, meaning the + code within the context manager scope is executed in parallel to the flamegraph processes + running in the background. + + This class inherits from the ExitStack since it internally creates a lot of other context + managers that must be properly managed and exited even if an error occurs. + + :ivar fg_handles: handles to temporary output files for the created flamegraphs; pipes have + limited buffer sizes and we do not want to periodically scan for output or use blocking + methods for reading the output + :ivar diff_processes: handles of difffolded.pl (.py) processes that generate data for diff + flamegraphs + :ivar fg_processes: handles of flamegraph.pl (.py) processes + :ivar grid: a reference to the grid object for the generated flamegraphs; context managers + cannot explicitly return values, hence we use an output parameter + :ivar escape_svgs: a flag indicating the SVGs should be escaped for HTML embedding + """ + + __slots__ = "fg_handles", "diff_processes", "fg_processes", "grid", "escape_svgs" + + def __init__( + self, + grid: FlameGraphGrid, + grid_commands: FlameGraphGridCommands, + ) -> None: + """ + :param grid: [out] a grid object which will contain the generated flamegraphs + :param grid_commands: the commands for generating a flamegraph grid + """ + super().__init__() + self.fg_handles: list[type_hints.TempTextIO] = [] + self.diff_processes: list[Popen[bytes]] = [] + self.fg_processes: list[Popen[bytes]] = [] + self.grid: FlameGraphGrid = grid + self.escape_svgs: bool = grid_commands.escape_graphs + + log.major_info("Creating Flame Graph Grid (In Parallel)") + + # Here we start populating the ExitStack with context managers. + # Spawn 4 temporary files that will store the generated flamegraphs. + self.fg_handles = [ + self.enter_context(tempfile.NamedTemporaryFile(mode="w+")) for _ in range(4) + ] + # Spawn two difffolded processes: one for each diff flamegraph. + # The processes should be spawned only if we are using the original Perl scripts and not + # the optimized Python ones, which is indicated by empty difffolded commands. + if grid_commands.baseline_target_difffolded: + self.diff_processes = [ + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.baseline_target_difffolded, {"stdout": PIPE} + ) + ), + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.target_baseline_difffolded, {"stdout": PIPE} + ) + ), + ] + # Spawn four flamegraph processes: one for each flamegraph in the grid. + base_tar_kwargs: dict[str, Any] = {"stdout": self.fg_handles[2]} + tar_base_kwargs: dict[str, Any] = {"stdout": self.fg_handles[3]} + # Handle both Perl and Python versions. + if grid_commands.baseline_target_difffolded: + base_tar_kwargs["stdin"] = self.diff_processes[0].stdout + tar_base_kwargs["stdin"] = self.diff_processes[1].stdout + self.fg_processes = [ + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.baseline, {"stdout": self.fg_handles[0]} + ) + ), + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.target, {"stdout": self.fg_handles[1]} + ) + ), + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.baseline_target_fg_diff, + base_tar_kwargs, + ) + ), + self.enter_context( + processes.nonblocking_subprocess( + grid_commands.target_baseline_fg_diff, + tar_base_kwargs, + ) + ), + ] + log.minor_success("Spawning flamegraph background processes") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + """Context manager exit method that gathers the flamegraphs unless an error occurred. + + If an error occurred either somewhere in the context manager scope or within this method, + the ExitStack exits all stored context managers and the error is propagated outside. + In such case, the flamegraph grid is not guaranteed to be fully created depending on where + the error happened. + + If no error occurred, this method waits for all flamegraph processes to finish and populates + the output grid object. + + Note that this is a blocking method. + + :param exc_type: the type of the exception that occurred, if any + :param exc_val: the actual exception object, if any + :param exc_tb: the traceback of the error, if any + """ + try: + # Only wait for and gather the flamegraphs if no error occurred yet. + if exc_type is None: + # We do not want to modify the list of handles directly. Instead, we keep a list of + # handle indices corresponding to processes that have not finished yet. + running_fg_processes: list[int] = list(range(len(self.fg_processes))) + # Prepare the SVG escape tags so that we can escape the SVGs concurrently. + tags_to_idx = {tag: idx for idx, tag in enumerate(FlameGraphGrid.EscapeTags)} + while running_fg_processes: + for list_idx, proc_idx in enumerate(running_fg_processes): + try: + self.fg_processes[proc_idx].wait(timeout=0.1) + # This process has finished, store the generated flamegraph in the grid. + self.fg_handles[proc_idx].seek(0) + self.grid[proc_idx] = self.fg_handles[proc_idx].read() + if self.escape_svgs: + self.grid[proc_idx] = escape_flamegraph_svg( + FlameGraphGrid.EscapeTags[proc_idx], + self.grid[proc_idx], + tags_to_idx, + ) + log.minor_success(FlameGraphGrid.DefaultTitles[proc_idx], "generated") + running_fg_processes.pop(list_idx) + break + except TimeoutExpired: + # This process is still running, check the next one. + pass + # Cleanup the difffolded processes properly. If the diff flamegraph processes + # finished correctly, the difffolded processes must have finished as well. + for diff_process in self.diff_processes: + diff_process.wait() + log.minor_success("Flamegraph grid", "completed") + except: + # We use the catch-all except clause deliberately, as the resources should be cleaned + # no matter what errors happened. + super().__exit__(exc_type, exc_val, exc_tb) + raise + # The flamegraph grid is complete, cleanup all the context managers and resources. + return super().__exit__(exc_type, exc_val, exc_tb) + + +def build_flamegraph_grid_serially(grid_cmds: FlameGraphGridCommands) -> FlameGraphGrid: + """Create a flamegraph grid serially in this process. + + :param grid_cmds: the commands for generating a flamegraph grid + + :return: the generated flamegraph grid + """ + log.major_info("Creating Flame Graph Grid (Serially)") + + # Transform the commands into a tuple that we can index in a loop. + if grid_cmds.baseline_target_difffolded: + # Perl variant. + cmds: tuple[str, str, str, str] = ( + grid_cmds.baseline, + grid_cmds.target, + f"{grid_cmds.baseline_target_difffolded} | {grid_cmds.baseline_target_fg_diff}", + f"{grid_cmds.target_baseline_difffolded} | {grid_cmds.target_baseline_fg_diff}", + ) + else: + # Python variant. + cmds = ( + grid_cmds.baseline, + grid_cmds.target, + grid_cmds.baseline_target_fg_diff, + grid_cmds.target_baseline_fg_diff, + ) + + grid: FlameGraphGrid = FlameGraphGrid() + for idx, (fg_cmd, tag) in enumerate(zip(cmds, FlameGraphGrid.EscapeTags)): + # Execute the command and escape the resulting flamegraph. + grid[idx] = commands.run_safely_external_command(fg_cmd)[0].decode("utf-8") + if grid_cmds.escape_graphs: + grid[idx] = escape_flamegraph_svg(tag, grid[idx]) + log.minor_success(FlameGraphGrid.DefaultTitles[idx], "generated") + + log.minor_success("Flamegraph grid", "completed") + return grid + + +@contextlib.contextmanager +def build_flamegraph_grid( + grid: FlameGraphGrid, + grid_commands: FlameGraphGridCommands, + parallelize: bool, +) -> Iterator[FlameGraphGridBuilder]: + """Create a context manager for flamegraph grid serial or parallel builder. + + The context manager hides the specifics of building a flamegraph grid serially in a single + process or in several parallel background processes. + + :param grid: [out] a reference to the grid object for the generated flamegraphs; context + managers cannot explicitly return values, hence we use an output parameter + :param grid_commands: the commands for generating a flamegraph grid + :param parallelize: determines whether to build the grid serially or in parallel + + :return: the context manager wrapping the grid builder + """ + creator = FlameGraphGridParallelBuilder if parallelize else FlameGraphGridSerialBuilder + with creator(grid, grid_commands) as builder: + yield builder + + +def build_flamegraph_grid_commands( + baseline: Path, + target: Path, + settings: FlameGraphSettings, + escape_svgs: bool, + *new_flags: str, + titles: tuple[str, str, str, str] = FlameGraphGrid.DefaultTitles, + **override_kwargs: Any, +) -> FlameGraphGridCommands: + """Create a collection of commands for generating a flamegraph grid. + + :param baseline: a path to the file with baseline folded flame graph data + :param target: a path to the file with target folded flame graph data + :param settings: flamegraph configuration parameters and flags + :param escape_svgs: a flag indicating whether the SVGs should be escaped for HTML embedding + :param new_flags: additional flags that should be passed to the flamegraph script + :param titles: the titles of the respective flamegraphs + :param override_kwargs: additional parameters that should extend or override the parameter + values stored in the settings object + + :return: the flamegraph commands for generating a flamegraph grid + """ + return FlameGraphGridCommands( + escape_svgs, + core.build_flamegraph_command(baseline, settings, titles[0], *new_flags, **override_kwargs), + core.build_flamegraph_command(target, settings, titles[1], *new_flags, **override_kwargs), + *core.build_diff_flamegraph_commands( + target, baseline, settings, titles[2], "negate", *new_flags, **override_kwargs + ), + *core.build_diff_flamegraph_commands( + baseline, target, settings, titles[3], *new_flags, **override_kwargs + ), + ) + + +def escape_flamegraph_svg(tag: str, content: str, _tag_to_index: dict[str, int] = {}) -> str: + """Escapes SVG content so that multiple flame graph may be embedded inside an HTML file. + + :param tag: the tag used to prefix the functions and ids + :param content: the SVG content to escape + :param _tag_to_index: a cache-like tag -> index mapping. Used to correctly replace calls to + `getElementsByClassName("svg-content")` by + `getElementsByClassName("svg-content")[]`. + The default value is mutable intentionally: this allows the caller to control the + escaping per each generated HTML file. + :return: the escaped content + """ + tag_index = _tag_to_index.setdefault(tag, len(_tag_to_index)) + functions = [ + r"(?\s*\n<", r"><"), + ( + r"getElementsByTagName\(\"svg\"\)\[0\]", + f'getElementsByClassName("svg-content")[{tag_index}]', + ), + (r"document.", f"{tag}_svg."), + (f"{tag}_svg.createElementNS", "document.createElementNS"), + ( + f"({tag}_(svg|details|detailsName|detailsIncl|detailsExcl|matchedHoverCount|matchedHoverIncl|matchedHoverExcl|matchedSearchCount|matchedSearchIncl|matchedSearchExcl|nameTypeLabel|inclusiveLabel|exclusiveLabel|matchedHoverLabel|matchedSearchLabel|searchbtn|matchedtxt|matchedHoverTxt|ignorecaseBtn|unzoombtn)) = {tag}_svg.", + "\\1 = document.", + ), + # Huge thanks to following article: + # https://chartio.com/resources/tutorials/how-to-resize-an-svg-when-the-window-is-resized-in-d3-js/ + # Which helped to solve the issue with non-resizable flamegraphs + ( + ' None: - """Draws and saves flamegraph to file - - :param profile: profile for which we are saving flamegraph - :param filename: name of the file where the flamegraph will be saved - """ - flamegraph_content = flame.draw_flame_graph( - convert.to_flame_graph_format(profile), - flame.generate_title(profile["header"]), - flame.get_units(profile["header"]["type"]), - ) - with open(filename, "w") as file_handle: - file_handle.write(flamegraph_content) - - -@click.command() +@click.command("flamegraph") +@shared_options.flamegraph_cli_options +@shared_options.profile_postprocess_cli_options +@shared_options.profile_aggregation_cli_option @click.option( - "--filename", - "-f", - default="flame.svg", - help="Sets the output file of the resulting flame graph.", + "--output-path", + "-o", + type=pathlib.Path, + default=pathlib.Path("./flame.svg"), + help="Sets the output file path (default=./flame.svg).", ) @profile_factory.pass_profile -def flamegraph(profile: profile_factory.Profile, filename: str, **_: Any) -> None: +def flamegraph(profile: profile_factory.Profile, output_path: pathlib.Path, **kwargs: Any) -> None: """Flame graph interprets the relative and inclusive presence of the resources according to the stack depth of the origin of resources. \b - * **Limitations**: `memory` profiles generated by - :ref:`collectors-memory`. + * **Limitations**: `memory` or `time` profiles generated by + :ref:`collectors-memory` or :ref:`collectors-kperf`. * **Interpretation style**: graphical * **Visualization backend**: HTML @@ -54,10 +46,7 @@ def flamegraph(profile: profile_factory.Profile, filename: str, **_: Any) -> Non relative to others. **Acknowledgements**: Big thanks to Brendan Gregg for creating the original - perl script for creating flame graphs w.r.t simple format. If you like this - visualization technique, please check out this guy's site - (https://brendangregg.com) for more information about performance, profiling - and useful talks and visualization techniques! + perl script for creating flame graphs w.r.t folded format: https://brendangregg.com. The example output of the flamegraph is more or less as follows:: @@ -78,4 +67,12 @@ def flamegraph(profile: profile_factory.Profile, filename: str, **_: Any) -> Non :func:`perun.profile.convert.to_flame_graph_format` for more details how the profiles are converted to the flame graph format. """ - save_flamegraph(profile, filename) + params = structs.PostprocessParameters(**kwargs) + folded_stream = native_folded.native_to_folded(profile, agg_func=params.agg_func) + core.generate_flamegraph( + folded_stream, + output_path, + core.generate_title(profile["header"]), + FlameGraphSettings.from_cli(**kwargs), + params, + ) diff --git a/perun/view/flow/factory.py b/perun/view/flow/factory.py index 1cb8f3c44..4312c0488 100644 --- a/perun/view/flow/factory.py +++ b/perun/view/flow/factory.py @@ -9,7 +9,7 @@ import holoviews as hv # Perun Imports -from perun.profiles.native import convert +from perun.profiles.conversions import native_pandas from perun.utils.common import view_kit if TYPE_CHECKING: @@ -55,7 +55,7 @@ def create_from_params( view_kit.lazy_init_holoviews() # Convert profile to pandas data grid - data_frame = convert.resources_to_pandas_dataframe(profile) + data_frame = native_pandas.resources_to_pandas_dataframe(profile) data_source = construct_data_source_from( data_frame, func, of_key, by_key, through_key, accumulate ) diff --git a/perun/view/flow/run.py b/perun/view/flow/run.py index 14f8c0c61..bb2ee698f 100644 --- a/perun/view/flow/run.py +++ b/perun/view/flow/run.py @@ -9,10 +9,10 @@ import click # Perun Imports +from perun.profiles import native as profile_factory from perun.utils import log from perun.utils.common import cli_kit, common_kit, view_kit from perun.utils.exceptions import InvalidParameterException -from perun.profiles import native as profile_factory import perun.view.flow.factory as flow_factory @@ -43,9 +43,9 @@ def process_title(ctx: click.Context, _: click.Option, value: str) -> str: @click.argument( "func", required=False, - default="sum", + default=common_kit.Aggregations.SUM.value, metavar="", - type=click.Choice(common_kit.AGGREGATIONS), + type=click.Choice(common_kit.Aggregations.supported()), is_eager=True, ) @click.option( diff --git a/perun/view/scatter/factory.py b/perun/view/scatter/factory.py index 901ba02d6..0b491ea79 100644 --- a/perun/view/scatter/factory.py +++ b/perun/view/scatter/factory.py @@ -5,7 +5,7 @@ # Standard Imports from collections.abc import Iterator from operator import itemgetter -from typing import TYPE_CHECKING, Any +from typing import Any, TYPE_CHECKING # Third-Party Imports from bokeh import palettes @@ -13,8 +13,9 @@ import numpy as np # Perun Imports -from perun.postprocess.regression_analysis import data_provider -from perun.profiles.native import query, convert +from perun.postprocess.regression_analysis import data_provider, transform +from perun.profiles.conversions import native_pandas +from perun.profiles.native import helpers, query from perun.utils.common import view_kit if TYPE_CHECKING: @@ -100,10 +101,10 @@ def _generate_plot_data_slices( :returns: slices of resources (per UID) and models (per UID and interval). """ # Get resources for scatter plot points and models for curves - resource_table = convert.resources_to_pandas_dataframe(profile) + resource_table = native_pandas.resources_to_pandas_dataframe(profile) models = list(map(itemgetter(1), profile.all_models())) # Get unique uids from profile, each uid (and optionally interval) will have separate graph - uids = map(convert.flatten, query.unique_resource_values_of(profile, "uid")) + uids = map(helpers.flatten, query.unique_resource_values_of(profile, "uid")) # Process each uid data for uid_slice, uid_models in _slice_resources_by_uid(resource_table, models, uids): @@ -188,9 +189,9 @@ def _create_parametric_model(model: ProfileModel) -> hv.Curve: :returns: a Curve plot element that represents the model. """ # First transform the model type and coefficients into X and Y points that can be plotted - model_conv = convert.plot_data_from_coefficients_of(model) + model.update(transform.coefficients_to_points(**model)) # Create a Curve plot element that represents the model - return hv.Curve((model_conv["plot_x"], model_conv["plot_y"]), label=_build_model_legend(model)) + return hv.Curve((model["plot_x"], model["plot_y"]), label=_build_model_legend(model)) def _create_non_param_model(profile: Profile, model: ProfileModel) -> Iterator[hv.Curve]: diff --git a/perun/view/tableof/run.py b/perun/view/tableof/run.py index ccfb42b12..7909c8188 100644 --- a/perun/view/tableof/run.py +++ b/perun/view/tableof/run.py @@ -4,9 +4,9 @@ # Standard Imports from itertools import groupby -from typing import Callable, Any, TYPE_CHECKING import operator import os +from typing import Any, Callable, TYPE_CHECKING # Third-Party Imports import click @@ -14,8 +14,9 @@ import tabulate # Perun Imports +from perun.profiles.conversions import native_pandas +from perun.profiles.native import helpers, query from perun.utils import log -from perun.profiles.native import query, helpers, convert if TYPE_CHECKING: from perun.profiles.native import Profile @@ -306,7 +307,7 @@ def resources( profile = ctx.parent.parent.params["profile"] profile_as_table = create_table_from( profile, - convert.resources_to_pandas_dataframe, + native_pandas.resources_to_pandas_dataframe, headers, tablefmt, sort_by, @@ -369,7 +370,7 @@ def models( profile = ctx.parent.parent.params["profile"] profile_as_table = create_table_from( profile, - convert.models_to_pandas_dataframe, + native_pandas.models_to_pandas_dataframe, headers, tablefmt, sort_by, diff --git a/perun/view_diff/__init__.pyi b/perun/view_diff/__init__.pyi index d0facf021..cbdedc16a 100644 --- a/perun/view_diff/__init__.pyi +++ b/perun/view_diff/__init__.pyi @@ -1,11 +1,7 @@ -from .flamegraph import ( - generate_flamegraphs as generate_flamegraphs, - process_flamegraph_stats as process_flamegraph_stats, - generate_flamegraph_difference as generate_flamegraph_difference, -) +from . import report as report -from .report_folded import generate_report_from_folded as generate_report_from_folded +from .chatbot import generate_initial_prompt as generate_initial_prompt -from .report_native import generate_report_from_native as generate_report_from_native +from .flamegraph import generate_flamegraph_difference as generate_flamegraph_difference from .short import compare_profiles as compare_profiles diff --git a/perun/view_diff/chatbot.py b/perun/view_diff/chatbot.py new file mode 100644 index 000000000..e5a7ae528 --- /dev/null +++ b/perun/view_diff/chatbot.py @@ -0,0 +1,35 @@ +"""Functions related to chatbot support in diff views.""" + +from __future__ import annotations + +# Standard Imports +from pathlib import Path + +# Third-Party Imports + +# Perun Imports +from perun.utils import log, streams + + +def generate_initial_prompt(chatbot_enabled: bool, chatbot_contexts: tuple[str, ...] | None) -> str: + """Compose initial chatbot prompt context from possibly multiple sources. + + :param chatbot_enabled: whether chatbot support is enabled or not. + :param chatbot_contexts: sources of the prompt context; may be either strings or a file paths. + + :return: the resulting prompt context string. + """ + # Process user-defined chatbot prompt context, if provided. + if not chatbot_enabled or chatbot_contexts is None: + return "" + log.minor_info("Processing chatbot prompt context") + context_str: str = "" + for source in chatbot_contexts: + if source.endswith(".prompt"): + # The context is stored in a file, attempt to open and read it. + with streams.safely_open_and_log(Path(source), "r", fatal_fail=False) as handle: + if handle is not None: + context_str += f"\n{handle.read()}" + else: + context_str += f"\n{source}" + return context_str + "\n" diff --git a/perun/view_diff/flamegraph.py b/perun/view_diff/flamegraph.py old mode 100755 new mode 100644 index 92a31ac2a..5dba94cf5 --- a/perun/view_diff/flamegraph.py +++ b/perun/view_diff/flamegraph.py @@ -7,361 +7,146 @@ from __future__ import annotations # Standard Imports -from dataclasses import dataclass from datetime import datetime, timezone -import pathlib -from subprocess import CalledProcessError -from typing import Any -import re +from pathlib import Path +import tempfile +from typing import Any, TYPE_CHECKING # Third-Party Imports # Perun Imports import perun -from perun.profiles import native as profile +from perun.profiles import structs, stats +from perun.profiles.polars import convert, structs as pl_structs +from perun.profiles.conversions import native_polars, polars_folded from perun.logic import config from perun.templates import factory as templates -from perun.utils import log, mapping +from perun.utils import log from perun.utils.common import diff_kit +from perun.utils.structs.view_structs import FlameGraphSettings from perun.utils.structs.common_structs import WebColorPalette -from perun.utils.structs.diff_structs import FG_DEFAULT_MIN_WIDTH -from perun.view.flamegraph import flamegraph as flamegraph_factory +from perun.view.flamegraph import grid as fg_grid +from perun.view_diff import chatbot -TAGS_TO_INDEX: list[str] = [] +if TYPE_CHECKING: + from perun.profiles.native import Profile -@dataclass -class FlameGraphStats: - """A helper dataclass for storing flamegraph-related stats. +def generate_flamegraph_difference( + baseline_profile: Profile, target_profile: Profile, **cli_kwargs: Any +) -> None: + """Generates differences of two profiles as two side-by-side flamegraphs - :ivar dtype: the datatype identifier - :ivar max_trace: the longest found trace - :ivar max_filtered_trace: the longest found trace after filtering is applied - :ivar total_resource: the total consumed dtype resource + :param baseline_profile: a baseline Perun profile. + :param target_profile: a target Perun profile. + :param cli_kwargs: additional CLI arguments. """ + # Parse the input native profiles into aggregated baseline and target LazyFrames, and create + # their Polars representation. + log.major_info("Parsing Input Perun Profiles") + postprocess_params = structs.PostprocessParameters(**cli_kwargs) + func_maps = pl_structs.FunctionMaps() + baseline_lf, baseline_features = native_polars.native_to_polars_lf( + baseline_profile, postprocess_params, func_maps + ) + target_lf, target_features = native_polars.native_to_polars_lf( + target_profile, postprocess_params, func_maps + ) + func_maps.finalize() - dtype: str - max_trace: int = 0 - max_filtered_trace: int = 0 - total_resource: int = 0 - - -def escape_content(tag: str, content: str) -> str: - """Escapes content, so there are no clashes in the files - - :param tag: tag used to prefix all the functions and ids - :param content: generated svg content - :return: escaped content - """ - if tag not in TAGS_TO_INDEX: - TAGS_TO_INDEX.append(tag) - functions = [ - r"(?\s*\n<", r"><"), - ( - r"getElementsByTagName\(\"svg\"\)\[0\]", - f'getElementsByClassName("svg-content")[{TAGS_TO_INDEX.index(tag)}]', - ), - (r"document.", f"{tag}_svg."), - (f"{tag}_svg.createElementNS", "document.createElementNS"), - ( - f"({tag}_(svg|details|detailsName|detailsIncl|detailsExcl|matchedHoverCount|matchedHoverIncl|matchedHoverExcl|matchedSearchCount|matchedSearchIncl|matchedSearchExcl|nameTypeLabel|inclusiveLabel|exclusiveLabel|matchedHoverLabel|matchedSearchLabel|searchbtn|matchedtxt|matchedHoverTxt|ignorecaseBtn|unzoombtn)) = {tag}_svg.", - "\\1 = document.", - ), - # Huge thanks to following article: - # https://chartio.com/resources/tutorials/how-to-resize-an-svg-when-the-window-is-resized-in-d3-js/ - # Which helped to solve the issue with non-resizable flamegraphs - ( - ' tuple[list[tuple[str, str, str, str, str]], list[FlameGraphStats], list[FlameGraphStats]]: - """Constructs a list of tuples of flamegraphs for list of data_types - - :param lhs_profile: baseline profile - :param rhs_profile: target profile - :param data_types: list of data types (resources) - :param skip_diff: whether the flamegraph diff should be skipped or not - :param minimize: whether the flamegraph should be minimized or not - :param squash_unknown: whether recursive [unknown] frames should be squashed into a single one - :param fg_forward_kwargs: additional parameters forwarded to the flamegraph scripts - - :return: a collection of (data_type, lhs flamegraph, rhs flamegraph, lhs_rhs_diff_flamegraph, - rhs_lhs_diff_flamegraph) tuples - """ - flamegraphs = [] - lhs_stats, rhs_stats = [], [] - for i, dtype in log.progress(enumerate(data_types), description="Generating Flame Graphs"): - try: - data_type = mapping.from_readable_key(dtype) - lhs_flame = profile.to_flame_graph_format( - lhs_profile, profile_key=data_type, minimize=minimize, squash_unknown=squash_unknown - ) - rhs_flame = profile.to_flame_graph_format( - rhs_profile, profile_key=data_type, minimize=minimize, squash_unknown=squash_unknown - ) - fg_image_width = fg_forward_kwargs["width"] - fg_minwidth = fg_forward_kwargs.get("minwidth", f"{FG_DEFAULT_MIN_WIDTH}") - lhs_max_trace, lhs_max_filt_trace, lhs_max_res = flamegraph_factory.compute_max_traces( - lhs_flame, fg_image_width, fg_minwidth - ) - rhs_max_trace, rhs_max_filt_trace, rhs_max_res = flamegraph_factory.compute_max_traces( - rhs_flame, fg_image_width, fg_minwidth - ) - fg_forward_kwargs["maxtrace"] = max(lhs_max_filt_trace, rhs_max_filt_trace) - fg_forward_kwargs["total"] = max(lhs_max_res, rhs_max_res) - - lhs_stats.append( - FlameGraphStats(data_type, lhs_max_trace, lhs_max_filt_trace, lhs_max_res) - ) - rhs_stats.append( - FlameGraphStats(data_type, rhs_max_trace, rhs_max_filt_trace, rhs_max_res) - ) - - with ( - flamegraph_factory.fg_optional_tempfile(lhs_flame) as lhs_file, - flamegraph_factory.fg_optional_tempfile(rhs_flame) as rhs_file, - ): - lhs_graph = flamegraph_factory.draw_flame_graph( - lhs_file, - "Baseline Flamegraph", - **fg_forward_kwargs, - ) - escaped_lhs = escape_content(f"lhs_{i}", lhs_graph) - log.minor_success(f"Baseline flamegraph ({dtype})", "generated") - - rhs_graph = flamegraph_factory.draw_flame_graph( - rhs_file, - "Target Flamegraph", - **fg_forward_kwargs, - ) - escaped_rhs = escape_content(f"rhs_{i}", rhs_graph) - log.minor_success(f"Target flamegraph ({dtype})", "generated") - - if skip_diff: - lhs_escaped_diff, rhs_escaped_diff = "", "" - else: - lhs_rhs_diff = flamegraph_factory.draw_differential_flame_graph( - lhs_file, - rhs_file, - "Baseline-Target Diff Flamegraph", - **fg_forward_kwargs, - ) - lhs_escaped_diff = escape_content(f"lhs_diff_{i}", lhs_rhs_diff) - log.minor_success(f"Baseline-target diff flamegraph ({dtype})", "generated") + ) + minwidth_threshold = fg_settings.compute_minwidth_threshold() + + # Dump the parsed and post-processed profiles back into a folded format for flamegraph scripts. + grid = fg_grid.FlameGraphGrid() + log.minor_info("Saving post-processed profiles in temporary files.") + with ( + tempfile.NamedTemporaryFile(mode="w+") as baseline_folded, + tempfile.NamedTemporaryFile(mode="w+") as target_folded, + ): + base_maxtrace = polars_folded.store_polars_as_folded_profile( + pair_profile.baseline, baseline_folded, minwidth_threshold + ) + tar_maxtrace = polars_folded.store_polars_as_folded_profile( + pair_profile.target, target_folded, minwidth_threshold + ) + fg_settings.maxtrace = max(base_maxtrace, tar_maxtrace) + log.minor_success("Saving post-processed profiles in temporary files") - # We add the '--negate' for consistent diff colors in the rhs to lhs - # flamegraph diff - rhs_lhs_diff = flamegraph_factory.draw_differential_flame_graph( - rhs_file, - lhs_file, - "Target-Baseline Diff Flamegraph", - "samples", - "negate", - **fg_forward_kwargs, - ) - rhs_escaped_diff = escape_content(f"rhs_diff_{i}", rhs_lhs_diff) - log.minor_success(f"Target-baseline diff flamegraph ({dtype})", "generated") - flamegraphs.append( - (dtype, escaped_lhs, escaped_rhs, lhs_escaped_diff, rhs_escaped_diff) + # Build the flamegraph grid. The grid may be built serially or in parallel background + # processes. + grid_commands = fg_grid.build_flamegraph_grid_commands( + Path(baseline_folded.name), Path(target_folded.name), fg_settings, escape_svgs=True + ) + with fg_grid.build_flamegraph_grid(grid, grid_commands, fg_settings.parallelize): + # If we build the flamegraphs in the background, we can analyze and further postprocess + # the profiles in the meantime. + lhs_diff_stats, rhs_diff_stats = diff_kit.generate_diff_of_stats( + baseline_profile.all_stats(), target_profile.all_stats() ) - # Attempt to remove the leftover temporary 'palette.map' file that is no longer needed - pathlib.Path("palette.map").unlink(missing_ok=True) - except CalledProcessError as exc: - log.warn( - f"could not generate flamegraphs: {exc}\n" - f"Error message: {exc.stderr.decode('utf-8')}" + lhs_fg_diff_stats, rhs_fg_diff_stats = diff_kit.generate_diff_of_stats( + stats.features_to_stats(pair_profile.baseline.features, fg_settings.countname), + stats.features_to_stats(pair_profile.target.features, fg_settings.countname), ) - return flamegraphs, lhs_stats, rhs_stats - -def process_flamegraph_stats(fg_stats: list[FlameGraphStats]) -> list[profile.ProfileStat]: - profile_stats: list[profile.ProfileStat] = [] - max_trace, max_filtered_trace = 0, 0 - - for stat in fg_stats: - # TODO: This is a bit of a hack since the readable_key already contains the unit - name, unit = mapping.get_readable_key(stat.dtype).split("[", maxsplit=1) - name.rstrip() - unit = unit.rsplit("]", maxsplit=1)[0] - profile_stats.append( - profile.ProfileStat( - f"Total {name}", - profile.ProfileStatComparison.LOWER, - unit, - description=f"The total value of the {stat.dtype}", - value=[stat.total_resource], + prompt_ctx = chatbot.generate_initial_prompt( + cli_kwargs["chatbot_url"] is not None, + cli_kwargs.get("chatbot_prompt_context", None), ) - ) - max_trace = max(max_trace, stat.max_trace) - max_filtered_trace = max(max_filtered_trace, stat.max_filtered_trace) - - profile_stats.append( - profile.ProfileStat( - "Longest Profile Trace", - profile.ProfileStatComparison.LOWER, - "#", - description="The longest trace recorded in the profile", - value=[max_trace], - ) - ) - profile_stats.append( - profile.ProfileStat( - "Longest Flame Graph Trace", - profile.ProfileStatComparison.LOWER, - "#", - description=( - "The longest trace in the Flame Graph, which might be shorter than the longest " - "trace in the profile due to filtering." - ), - value=[max_filtered_trace], - ) - ) - return profile_stats + template = templates.get_template("diff_views/flamegraph.html.jinja2") -def generate_flamegraph_difference( - lhs_profile: profile.Profile, rhs_profile: profile.Profile, **kwargs: Any -) -> None: - """Generates differences of two profiles as two side-by-side flamegraphs - - :param lhs_profile: baseline profile - :param rhs_profile: target profile - :param kwargs: additional arguments - """ - fg_forward_kwargs = { - arg.replace("flamegraph_", ""): val - for arg, val in kwargs.items() - if arg.startswith("flamegraph_") - } - # FIXME: temporary solution before refactoring to FlameGraphSettings. - del fg_forward_kwargs["parallelize"] + is_offline = config.lookup_key_recursively("showdiff.offline", False) + utc_time_now = datetime.now(timezone.utc) + creation_time = utc_time_now.strftime("%d %b %Y, %H:%M:%S") + " UTC" - lhs_stats: list[profile.ProfileStat] = list(lhs_profile.all_stats()) - rhs_stats: list[profile.ProfileStat] = list(rhs_profile.all_stats()) - data_types = [ - mapping.get_readable_key(key) - for key in diff_kit.get_candidate_keys( - set(lhs_profile.all_resource_fields()).union(set(rhs_profile.all_resource_fields())) + # Transform the flamegraph grid into the format expected by the report template. + flamegraphs = [ + ( + f"Inclusive {fg_settings.countname} [#]", + grid.baseline, + grid.target, + grid.baseline_target_diff, + grid.target_baseline_diff, ) ] - log.major_info("Generating Flamegraph Grid") - flamegraphs, lhs_fg_stats, rhs_fg_stats = generate_flamegraphs( - lhs_profile, - rhs_profile, - data_types, - skip_diff=False, - minimize=kwargs.get("minimize", False), - squash_unknown=not kwargs.get("no_squash_unknown", False), - **fg_forward_kwargs, - ) - lhs_diff_stats, rhs_diff_stats = diff_kit.generate_diff_of_stats(lhs_stats, rhs_stats) - lhs_fg_diff_stats, rhs_fg_diff_stats = diff_kit.generate_diff_of_stats( - process_flamegraph_stats(lhs_fg_stats), process_flamegraph_stats(rhs_fg_stats) - ) - - template = templates.get_template("diff_views/flamegraph.html.jinja2") content = template.render( title="Perun Flame Graphs", perun_version=perun.__version__, - timestamp=datetime.now(timezone.utc).strftime("%d %b %Y, %H:%M:%S") + " UTC", - lhs_tag="Baseline (base)", + timestamp=creation_time, + chatbot=cli_kwargs["chatbot_url"], + chatbot_prompt_context=prompt_ctx, + lhs_tag="Baseline", lhs_fg_stats=lhs_fg_diff_stats, lhs_user_stats=lhs_diff_stats, - rhs_tag="Target (tgt)", + rhs_tag="Target", rhs_user_stats=rhs_diff_stats, rhs_fg_stats=rhs_fg_diff_stats, flamegraphs=flamegraphs, palette=WebColorPalette, - offline=config.lookup_key_recursively("showdiff.offline", False), + offline=is_offline, notes_enabled=False, + default_theme=cli_kwargs.get("default_theme", "dark"), ) - log.minor_success("Flame Graph grid template", "rendered") - output_file = diff_kit.save_diff_view( - kwargs.get("output_path"), content, "flamegraph", lhs_profile, rhs_profile + + output_path = diff_kit.generate_output_filepath( + cli_kwargs["output_path"], "flamegraph", baseline_profile, target_profile ) - log.minor_status("Output saved", log.path_style(output_file)) + diff_kit.save_diff_view(output_path, content) + + log.minor_status("Flamegraph diff view saved", log.path_style(str(output_path))) diff --git a/perun/view_diff/meson.build b/perun/view_diff/meson.build index ef2f1470f..2272533c4 100755 --- a/perun/view_diff/meson.build +++ b/perun/view_diff/meson.build @@ -3,9 +3,8 @@ perun_view_diff_dir = perun_dir / 'view_diff' perun_view_diff_files = files( '__init__.py', '__init__.pyi', + 'chatbot.py', 'flamegraph.py', - 'report_folded.py', - 'report_native.py', 'short.py', ) @@ -13,3 +12,5 @@ py3.install_sources( perun_view_diff_files, subdir: perun_view_diff_dir ) + +subdir('report') diff --git a/perun/view_diff/report/__init__.py b/perun/view_diff/report/__init__.py new file mode 100644 index 000000000..e0002a5fe --- /dev/null +++ b/perun/view_diff/report/__init__.py @@ -0,0 +1,23 @@ +"""A comprehensive HTML difference report of baseline-target profiles. + +The report calculates a number of difference metrics for both inclusive and exclusive resource +consumption: + + - proportional diff (prop_diff): reports the difference between the relative resource + consumption proportionally to the total baseline and target consumption change. For example, + if the baseline and target consumed 2M and 1M CPU cycles in total, respectively, and a + function 'foo' consumed 100K cycles in both cases, the proportional difference is +5% as 'foo' + now consumes 10% total resources up from 5%. + - absolute diff (abs_diff): reports the difference of Target - Baseline resource consumption. + For example, if the baseline and target consumed 2M and 1M CPU cycles in total, respectively, + and a function 'foo' consumed 100K and 75K cycles in baseline, resp. target, the absolute + difference is -25K. + - relative diff (rel_diff): reports the difference of Target - Baseline resource consumption in + relative terms. For example, if the baseline and target consumed 2M and 1M CPU cycles in + total, respectively, and a function 'foo' consumed 100K and 80K cycles in baseline, resp. + target, the relative difference is -25%. +""" + +import lazy_loader as lazy + +__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/perun/view_diff/report/__init__.pyi b/perun/view_diff/report/__init__.pyi new file mode 100644 index 000000000..26246b8e1 --- /dev/null +++ b/perun/view_diff/report/__init__.pyi @@ -0,0 +1,16 @@ +from .core import ( + ProfileMisc as ProfileMisc, + SelectionRow as SelectionRow, + generate_report_view as generate_report_view, +) + +from .diffs import ( + KeyDiffs as KeyDiffs, + find_top_diffs as find_top_diffs, + compute_top_diffs as compute_top_diffs, + iterate_top_diffs as iterate_top_diffs, +) + +from .folded import generate_report_from_folded_profiles as generate_report_from_folded_profiles + +from .native import generate_report_from_native_profiles as generate_report_from_native_profiles diff --git a/perun/view_diff/report/core.py b/perun/view_diff/report/core.py new file mode 100644 index 000000000..4335c397e --- /dev/null +++ b/perun/view_diff/report/core.py @@ -0,0 +1,396 @@ +"""The core logic that generates the HTML report from Polars profiles.""" + +from __future__ import annotations + +# Standard Imports +from collections.abc import Iterable, Iterator, Mapping +import dataclasses +from datetime import datetime, timezone +from pathlib import Path +import tempfile +from typing import Any, Literal + +# Third-Party Imports +import polars as pl + +# Perun Imports +import perun +from perun.logic import config +from perun.profiles import stats as p_stats, structs +from perun.profiles.conversions import polars_folded +from perun.profiles.polars import convert, structs as pl_structs +from perun.templates import factory as templates +from perun.utils import log +from perun.utils.common import common_kit, diff_kit +from perun.utils.structs.common_structs import WebColorPalette +from perun.utils.structs.view_structs import FlameGraphSettings +from perun.view.flamegraph import grid as fg_grid +from perun.view_diff import chatbot +from perun.view_diff.report import diffs + + +@dataclasses.dataclass +class ProfileMisc: + """Miscellaneous data associated with a profile. + + :ivar environment: machine properties of the machine where the profile was measured. + :ivar metadata: user-specified profile metadata. + :ivar vulnerabilities: CPU vulnerabilities mitigations. + :ivar stats: profile stats. + """ + + environment: Iterable[structs.ProfileMetadataEntry] + metadata: Iterable[structs.ProfileMetadataEntry] + vulnerabilities: Iterable[structs.ProfileMetadataEntry] + stats: Iterable[p_stats.ProfileStat] + + +@dataclasses.dataclass +class SelectionRow: + """Helper dataclass for displaying selection of data + + :ivar uid: uid of the selected graph + :ivar index: index in the sorted list of data + :ivar fresh: the state of the uid - 1) not in baseline (added in target), + 2) not in target (removed in target), or 3) possibly unchanged + :ivar stats: overview of all stat values + :ivar trace_stats: a collection of stats for traces + """ + + __slots__ = [ + "uid", + "index", + "fresh", + "main_stat", + "stats", + "trace_stats", + ] + + def __init__( + self, + uid: str, + index: int, + fresh: Literal["not in baseline", "not in target", "in both"], + stats: list[tuple[int, float, float, float, float, float]], + trace_stats: list[ + tuple[ + str, int, float, float, float, float, float, float, float, float, float, float, str + ] + ], + ) -> None: + """Initializes the selection row.""" + self.uid: str = uid + self.index: int = index + self.fresh: Literal["not in baseline", "not in target", "in both"] = fresh + # stat_type, baseline_abs, target_abs, prop_rel_delta, abs_delta, rel_delta + self.stats: list[tuple[int, float, float, float, float, float]] = [ + ( + stat[0], + common_kit.to_compact_num(stat[1]), + common_kit.to_compact_num(stat[2]), + common_kit.to_compact_num(stat[3]), + common_kit.to_compact_num(stat[4]), + common_kit.to_compact_num(stat[5]), + ) + for stat in stats + ] + # trace, stat_t, baseline_abs_incl, baseline_abs_excl, target_abs_incl, target_abs_excl, + # prop_rel_delta_incl, prop_rel_delta_excl, abs_delta_incl, abs_delta_excl, + # rel_delta_incl, rel_delta_excl, long_trace + self.trace_stats: list[ + tuple[ + str, int, float, float, float, float, float, float, float, float, float, float, str + ] + ] = [ + ( + t[0], + t[1], + common_kit.to_compact_num(t[2]), + common_kit.to_compact_num(t[3]), + common_kit.to_compact_num(t[4]), + common_kit.to_compact_num(t[5]), + common_kit.to_compact_num(t[6]), + common_kit.to_compact_num(t[7]), + common_kit.to_compact_num(t[8]), + common_kit.to_compact_num(t[9]), + common_kit.to_compact_num(t[10]), + common_kit.to_compact_num(t[11]), + t[12], + ) + for t in trace_stats + ] + + +def generate_report_view( + pair_profile: pl_structs.PolarsTraceProfilePair, + baseline: ProfileMisc, + target: ProfileMisc, + units: str, + **cli_kwargs: Any, +) -> tuple[str, datetime]: + """Generate an interactive HTML report from baseline-target profiles. + + :param pair_profile: a baseline-target Polars pair profile. + :param baseline: miscellaneous data associated with the baseline profile. + :param target: miscellaneous data associated with the target profile. + :param units: the name of the resource units stored in the profiles. + :param cli_kwargs: additional CLI parameters. + + :return: a generated HTML report and the time of its creation. + """ + fg_settings = FlameGraphSettings.from_cli( + **cli_kwargs, + countname=units, + rootnode="Maximum (Baseline, Target)", + subrootnode="Profile Total", + total=max( + pair_profile.baseline.features.total_resources, + pair_profile.target.features.total_resources, + ), + ) + minwidth_threshold = fg_settings.compute_minwidth_threshold() + + # Dump the profiles back into a folded format for flamegraph scripts. + grid = fg_grid.FlameGraphGrid() + log.minor_info("Saving post-processed profiles in temporary files.") + with ( + tempfile.NamedTemporaryFile(mode="w+") as baseline_folded, + tempfile.NamedTemporaryFile(mode="w+") as target_folded, + ): + base_maxtrace = polars_folded.store_polars_as_folded_profile( + pair_profile.baseline, baseline_folded, minwidth_threshold + ) + tar_maxtrace = polars_folded.store_polars_as_folded_profile( + pair_profile.target, target_folded, minwidth_threshold + ) + fg_settings.maxtrace = max(base_maxtrace, tar_maxtrace) + log.minor_success("Saving post-processed profiles in temporary files") + + # Build the flamegraph grid. The grid may be built serially or in parallel background + # processes. + grid_commands = fg_grid.build_flamegraph_grid_commands( + Path(baseline_folded.name), Path(target_folded.name), fg_settings, escape_svgs=True + ) + with fg_grid.build_flamegraph_grid(grid, grid_commands, fg_settings.parallelize): + # If we build the flamegraphs in the background, we can analyze and further postprocess + # the profiles in the meantime. + log.minor_info("Analyzing and comparing profiles.") + + # Merge the baseline and target profiles and obtain the top differences in traces and + # functions between the two profiles. + filter_params = structs.FilterParameters(**cli_kwargs) + merged = convert.merge_and_filter_polars_profiles( + pair_profile.baseline, pair_profile.target, filter_params + ) + trace_top_diffs = diffs.compute_top_diffs( + merged.traces.lazy(), + pair_profile.common_traces.lazy(), + "trace", + "prop_diff_incl", + "prop_diff_excl", + filter_params.top_diffs, + ) + func_top_diffs = diffs.compute_top_diffs( + merged.funcs.lazy(), + pair_profile.common_funcs.lazy(), + "func", + "prop_diff_incl", + "prop_diff_excl", + filter_params.top_diffs, + ) + + # Extend the profiles with new difference metrics and transform them such that they + # are suitable for building the traces table. + tabular_profile = convert.polars_merged_to_tabular_profiles( + merged, filter_params.max_function_traces + ) + + # Generate diffs of environments, stats, metadata, vulnerabilities, etc. + lhs_header, rhs_header = diff_kit.generate_diff_of_headers( + baseline.environment, target.environment + ) + lhs_vulnerabilities, rhs_vulnerabilities = diff_kit.generate_diff_of_headers( + baseline.vulnerabilities, target.vulnerabilities + ) + lhs_diff_stats, rhs_diff_stats = diff_kit.generate_diff_of_stats( + baseline.stats, target.stats + ) + lhs_fg_diff_stats, rhs_fg_diff_stats = diff_kit.generate_diff_of_stats( + p_stats.features_to_stats(pair_profile.baseline.features, fg_settings.countname), + p_stats.features_to_stats(pair_profile.target.features, fg_settings.countname), + ) + lhs_meta, rhs_meta = diff_kit.generate_diff_of_headers( + baseline.metadata, target.metadata + ) + + log.minor_success("Analyzing and comparing profiles") + + # Generate chatbot-related data. + prompt_ctx = chatbot.generate_initial_prompt( + cli_kwargs["chatbot_url"] is not None, + cli_kwargs.get("chatbot_prompt_context", None), + ) + + template = templates.get_template("diff_views/report.html.jinja2") + + utc_time_now = datetime.now(timezone.utc) + report_time = utc_time_now.strftime("%d %b %Y, %H:%M:%S") + " UTC" + is_report_offline = config.lookup_key_recursively("showdiff.offline", False) + report_links = list(cli_kwargs.get("link", [])) + # Transform the flamegraph grid into the format expected by the report template. + flamegraphs = [ + ( + f"Inclusive {fg_settings.countname} [#]", + grid.baseline, + grid.target, + grid.baseline_target_diff, + grid.target_baseline_diff, + ) + ] + + log.major_info("Rendering HTML Report") + + content = template.render( + title="Perun Report - Profiles Comparison", + perun_version=perun.__version__, + timestamp=report_time, + chatbot=cli_kwargs["chatbot_url"], + chatbot_prompt_context=prompt_ctx, + lhs_tag="Baseline", + lhs_header=lhs_header, + lhs_vulnerabilities=lhs_vulnerabilities, + lhs_user_stats=lhs_diff_stats, + lhs_fg_stats=lhs_fg_diff_stats, + lhs_metadata=lhs_meta, + rhs_tag="Target", + rhs_header=rhs_header, + rhs_vulnerabilities=rhs_vulnerabilities, + rhs_user_stats=rhs_diff_stats, + rhs_fg_stats=rhs_fg_diff_stats, + rhs_metadata=rhs_meta, + palette=WebColorPalette, + stat_list=[flamegraphs[0][0]], + units=[fg_settings.countname], + nodes=iter(pair_profile.baseline.maps.func_id_reverse_map.values()), + flamegraphs=flamegraphs, + selection_table=_iterate_polars_tabular( + tabular_profile, + pair_profile.common_funcs, + pair_profile.baseline.maps.func_id_reverse_map, + ), + max_traces_per_func=filter_params.max_function_traces, + offline=is_report_offline, + notes_enabled=True, + links=report_links, + default_theme=cli_kwargs.get("default_theme", "dark"), + top_diffs_trace_inclusive=diffs.iterate_top_diffs(trace_top_diffs[0]), + top_diffs_trace_exclusive=diffs.iterate_top_diffs(trace_top_diffs[1]), + top_diffs_func_inclusive=diffs.iterate_top_diffs(func_top_diffs[0]), + top_diffs_func_exclusive=diffs.iterate_top_diffs(func_top_diffs[1]), + total_baseline=pair_profile.baseline.features.total_resources, + total_target=pair_profile.target.features.total_resources, + is_folded=True, + ) + log.minor_success("HTML report", "rendered") + return content, utc_time_now + + +def _iterate_polars_tabular( + tabular_profile: pl_structs.PolarsTabularTraceProfiles, + common_functions: pl.DataFrame, + func_id_reverse_map: Mapping[int, str], +) -> Iterator[SelectionRow]: + """Iterates over the tabular profiles and generates records for the report table. + + :param tabular_profile: the polars tabular profiles. + :param common_functions: the set of function IDs common to both baseline and target profiles. + :param func_id_reverse_map: a function ID -> function name mapping. + + :return: a generator of the report table records. + """ + # Optimize dot operator access where it matters + str_split = str.split + str_rsplit = str.rsplit + str_count = str.count + tabular_funcs = tabular_profile.funcs + df_row = pl.DataFrame.row + df_iter_rows = pl.DataFrame.iter_rows + + common_funcs: set[int] = set(common_functions["func"].to_list()) + + group_df: pl.DataFrame + # Iterate over groups of traces belonging to individual functions from the lowest to the + # highest function IDs. + for idx, (_, group_df) in enumerate( + tabular_profile.traces.group_by("func", maintain_order=True) + ): + # Obtain a row corresponding to the function from the function profile. We can access it + # using an index thanks to the profile being sorted. + row = df_row(tabular_funcs, idx) + # Determine the state of the function. If it is not in the set of common functions, then we + # look at the 'inclusive' and 'inclusive_target' values to determine wheter it is + # a baseline-only or target-only function. + state: Literal["not in baseline", "not in target", "in both"] = "in both" + # row[0]: Function ID + if row[0] not in common_funcs: + # row[1]: inclusive + if row[1] == 0: + state = "not in baseline" + # row[3]: inclusive_target + elif row[3] == 0: + state = "not in target" + + # Format the top traces according to the SelectionRow requirements. + top_traces = [ + ( + # Short trace: 'firstID;lastID' + f"{str_split(r[1], ';', maxsplit=1)[0]};{str_rsplit(r[1], ';', maxsplit=1)[-1]}", + # Resource type, TODO: remove + 0, + # inclusive + r[2], + # exclusive + r[3], + # inclusive_target + r[4], + # exclusive_target + r[5], + # prop_diff_incl + r[6], + # prop_diff_excl + r[7], + # abs_diff_incl + r[8], + # abs_diff_excl + r[9], + # rel_diff_incl + r[10], + # rel_diff_excl + r[11], + # Full trace: 'firstID;...;lastID#base1;...;baseN#target1;...;targetN' + # FIXME: the part of the trace after the first '#' is not used, but the report + # still expects it. We will remove it later. + f'{r[1]}#0{";0" * str_count(r[1], ";")}#0{";0" * str_count(r[1], ";")}', + ) + for r in df_iter_rows(group_df) + ] + # Build the SelectionRow record. + # FIXME: Remove the dependency on SelectionRow. We should ideally pass the records as tuples + # directly to improve performance. + yield SelectionRow( + # Function name + func_id_reverse_map[row[0]], + # Function ID + row[0], + state, + # Inclusive and exclusive function stats: + [ + # resource type index, inclusive (baseline), inclusive_target, + # prop_diff_incl, abs_diff_incl, rel_diff_incl + (0, row[1], row[3], row[5], row[7], row[9]), + # resource type index, exclusive (baseline), exclusive_target, + # prop_diff_excl, abs_diff_excl, rel_diff_excl + (1, row[2], row[4], row[6], row[8], row[10]), + ], + top_traces, + ) diff --git a/perun/view_diff/report/diffs.py b/perun/view_diff/report/diffs.py new file mode 100644 index 000000000..c594caf12 --- /dev/null +++ b/perun/view_diff/report/diffs.py @@ -0,0 +1,155 @@ +"""A module that computes top differences in baseline-target profiles.""" + +from __future__ import annotations + +# Standard Imports +import dataclasses +from typing import Iterator + +# Third-Party Imports +import polars as pl + +# Perun Imports + + +@dataclasses.dataclass +class KeyDiffs: + """A collection of the most significant differences of a key within a partitioned profile. + + The class stores the top increases and decreases of a key (metric) for baseline-only, + target-only, and common partitions of a merged profile. + + The individual DataFrames contain only two columns: "symbol" and "diff". + - "symbol" identifies either a trace or a function by their IDs, and + - "diff" is the value of the diff metric. + + :ivar baseline_top_inc: the largest increases in baseline-only records + :ivar baseline_top_dec: the largest decreases in baseline-only records + :ivar target_top_inc: the largest increases in target-only records + :ivar target_top_dec: the largest decreases in target-only records + :ivar common_top_inc: the largest increases in common records + :ivar common_top_dec: the largest decreases in common records + """ + + __slots__ = ( + "baseline_top_inc", + "baseline_top_dec", + "target_top_inc", + "target_top_dec", + "common_top_inc", + "common_top_dec", + ) + + baseline_top_inc: pl.DataFrame + baseline_top_dec: pl.DataFrame + target_top_inc: pl.DataFrame + target_top_dec: pl.DataFrame + common_top_inc: pl.DataFrame + common_top_dec: pl.DataFrame + + +def find_top_diffs( + data_lf: pl.LazyFrame, count: int, symbol_key: str, diff_key: str, *, top: bool = True +) -> pl.LazyFrame: + """Find and format the top or bottom diffs w.r.t. the diff_key metric. + + The resulting frame will contain up to records maximal (resp. minimal) w.r.t. the + . The schema of the resulting frame will have only two columns: "symbol" and "diff" + corresponding to the and . + + :param data_lf: the data where to search for the maximal/minimal records. + :param count: the upper limit on the number of top records to search for. + :param symbol_key: the name of the column with symbols (e.g., "trace") + :param diff_key: the name of the column containing the diff metric + :param top: specifies whether to search for the maximal or minimal records. + + :return: a lazy frame containing the maximal/minimal records. + """ + search_func = pl.LazyFrame.top_k if top else pl.LazyFrame.bottom_k + + return ( + search_func(data_lf, count, by=diff_key) + .select([symbol_key, diff_key]) + .rename({symbol_key: "symbol", diff_key: "diff"}) + ) + + +def compute_top_diffs( + merged_profile: pl.LazyFrame, + common_symbols: pl.LazyFrame, + symbol_key: str, + inclusive_diff_key: str, + exclusive_diff_key: str, + records_num: int, +) -> tuple[KeyDiffs, KeyDiffs]: + """Compute the most significant inclusive and exclusive differences within a profile. + + The differences are computed for both inclusive and exclusive consumption metrics of a merged + profile. + + For example, we might want to compute top 10 baseline-only, target-only, and common traces + (or functions) that show the highest increase or decrease in the 'prop_diff_incl' and + 'prop_diff_excl' columns. + + :param merged_profile: a merged profile; it must contain the , + , and columns + :param common_symbols: a column of symbols (e.g., functions or traces) that appear in both the + baseline and target profiles + :param symbol_key: the name of the column to use for partitioning the profile into + baseline-only, target-only, and common parts + :param inclusive_diff_key: the name of the column containing inclusive consumption metric + :param exclusive_diff_key: the name of the column containing exclusive consumption metric + :param records_num: the number of top difference records to compute + + :return: a pair of inclusive and exclusive top differences + """ + # Partition the merged profile into baseline-only, target-only, and common parts based on + # the symbol_key and the set of common symbols. + common_lf = merged_profile.join(common_symbols, on=symbol_key, how="semi") + base_only_lf = merged_profile.join(common_symbols, on=symbol_key, how="anti").filter( + pl.col("inclusive_target") == 0 + ) + target_only_lf = merged_profile.join(common_symbols, on=symbol_key, how="anti").filter( + pl.col("inclusive") == 0 + ) + # We use the collect_all method to optimize the computation: all top_k and bottom_k calls + # share the same lazy frames and collect_all is able to optimize across all those computations. + dfs = pl.collect_all( + [ + # Inclusive diff metric. + find_top_diffs(base_only_lf, records_num, symbol_key, inclusive_diff_key), + find_top_diffs(base_only_lf, records_num, symbol_key, inclusive_diff_key, top=False), + find_top_diffs(target_only_lf, records_num, symbol_key, inclusive_diff_key), + find_top_diffs(target_only_lf, records_num, symbol_key, inclusive_diff_key, top=False), + find_top_diffs(common_lf, records_num, symbol_key, inclusive_diff_key), + find_top_diffs(common_lf, records_num, symbol_key, inclusive_diff_key, top=False), + # Exclusive diff metric. + find_top_diffs(base_only_lf, records_num, symbol_key, exclusive_diff_key), + find_top_diffs(base_only_lf, records_num, symbol_key, exclusive_diff_key, top=False), + find_top_diffs(target_only_lf, records_num, symbol_key, exclusive_diff_key), + find_top_diffs(target_only_lf, records_num, symbol_key, exclusive_diff_key, top=False), + find_top_diffs(common_lf, records_num, symbol_key, exclusive_diff_key), + find_top_diffs(common_lf, records_num, symbol_key, exclusive_diff_key, top=False), + ] + ) + return KeyDiffs(*dfs[:6]), KeyDiffs(*dfs[6:]) + + +def iterate_top_diffs(top_diff: KeyDiffs) -> list[Iterator[tuple[str, float]]]: + """Iterates over the top difference data frames and generates records for the report overview. + + :param top_diff: the top difference data frames + + :return: a generator of the overview table records + """ + diff_iterators: list[Iterator[tuple[str, float]]] = [] + for diff_data in [ + top_diff.baseline_top_dec, + top_diff.target_top_inc, + top_diff.common_top_inc, + top_diff.common_top_dec, + ]: + diff_iterators.append( + ((symbol, diff_metric) for symbol, diff_metric in diff_data.iter_rows()) + ) + return diff_iterators diff --git a/perun/view_diff/report/folded.py b/perun/view_diff/report/folded.py new file mode 100644 index 000000000..6d7d35c84 --- /dev/null +++ b/perun/view_diff/report/folded.py @@ -0,0 +1,197 @@ +"""A module that generates reports from external folded profiles.""" + +from __future__ import annotations + +# Standard Imports +import dataclasses +from pathlib import Path +from typing import Any, Optional + +# Third-Party Imports + +# Perun Imports +from perun.profiles import imports, structs +from perun.profiles.conversions import folded_polars +from perun.profiles.polars import convert, structs as pl_structs +from perun.utils import log +from perun.utils.common import diff_kit +from perun.view_diff.report import core + + +def _extract_environment_spec( + machine_info: dict[str, Any], + exitcodes: str | list[str] | list[int], + collector_command: str, + command: str, + label: str, +) -> list[structs.ProfileMetadataEntry]: + """Generate profile header specification. + + :param machine_info: the machine specification + :param exitcodes: possibly a collection of exit codes of profiling processes for each input + profile + :param collector_command: the name of the profiler that collected the profiles + :param command: the profiled command and its input parameters + :param label: a user-defined profile label + + :return: a collection of profile header entries corresponding to the profile specification + """ + return [ + structs.ProfileMetadataEntry( + "profile label", + label if label else "?", + "A label associated with this profile, if any.", + ), + structs.ProfileMetadataEntry( + "command", + command if command else "?", + "The profiled command and its input parameters.", + ), + structs.ProfileMetadataEntry( + "exitcode", + diff_kit.format_exit_codes(exitcodes), + "The exit code(s) that were returned by the profiling processes of each input profile.", + ), + structs.ProfileMetadataEntry( + "collector command", + collector_command, + "The collector / profiler, which collected the data.", + ), + structs.ProfileMetadataEntry( + "kernel", + machine_info.get("release", "?"), + "The underlying kernel version, where the results were measured.", + ), + structs.ProfileMetadataEntry( + "boot info", + machine_info.get("boot_info", "?"), + "The contents of `/proc/cmdline` containing boot information about kernel", + ), + structs.ProfileMetadataEntry( + "host", machine_info["host"], "The hostname, where the results were measured." + ), + structs.ProfileMetadataEntry( + "cpu (total)", + machine_info.get("cpu", {"total": "?"}).get("total", "?"), + "The total number (physical and virtual) of CPUs available on the host.", + ), + structs.ProfileMetadataEntry( + "memory (total)", + machine_info.get("memory", {"total_ram": "?"}).get("total_ram", "?"), + "The total number of RAM available on the host.", + ), + ] + + +def _extract_vulnerabilities( + vulnerabilities: dict[str, str | float], +) -> list[structs.ProfileMetadataEntry]: + """Generate vulnerabilities profile header from the machine info sub-dictionary. + + :param vulnerabilities: the machine info sub-dictionary containing CPU vulnerabilities + specification + + :return: CPU vulnerabilities as a profile header entry + """ + return [ + structs.ProfileMetadataEntry( + "vulnerabilities", + "?" if not vulnerabilities else "", + "CPU vulnerabilities summary.", + vulnerabilities, + ) + ] + + +def generate_report_from_folded_profiles( + baseline_profiles: str, target_profiles: str, **cli_kwargs: Any +) -> None: + """Generate an interactive report from external folded profiles. + + :param baseline_profiles: the specification of baseline profiles from CLI; either a CSV file + path, or a profile path with possibly an exit code and stat values + :param target_profiles: the specification of target profiles from CLI; either a CSV file path, + or a profile path with possibly an exit code and stat values + :param cli_kwargs: additional parameters from the CLI + """ + # Parse the profile specifications. + base_dir = Path(cli_kwargs.get("baseline_dir", Path.cwd())) + tar_dir = Path(cli_kwargs.get("target_dir", Path.cwd())) + baseline_specs, baseline_stats = imports.parse_import_entries( + [baseline_profiles], base_dir, cli_kwargs.get("baseline_stats_headers", None) + ) + target_specs, target_stats = imports.parse_import_entries( + [target_profiles], tar_dir, cli_kwargs.get("target_stats_headers", None) + ) + if not baseline_specs: + log.error("No valid baseline profiles specified. Terminating.") + elif not target_specs: + log.error("No valid target profiles specified. Terminating.") + + # Generate diffs of headers, stats, metadata, vulnerabilities, etc. + baseline_metadata = imports.parse_metadata( + cli_kwargs.get("baseline_metadata", tuple()), base_dir + ) + target_metadata = imports.parse_metadata(cli_kwargs.get("target_metadata", tuple()), tar_dir) + baseline_machine_info = imports.parse_machine_info( + cli_kwargs.get("baseline_machine_info", ""), base_dir + ) + target_machine_info = imports.parse_machine_info( + cli_kwargs.get("target_machine_info", ""), tar_dir + ) + + baseline_env = _extract_environment_spec( + baseline_machine_info, + [prof.exit_code for prof in baseline_specs], + cli_kwargs["baseline_collector_cmd"], + cli_kwargs["baseline_cmd"], + cli_kwargs["baseline_label"], + ) + + target_env = _extract_environment_spec( + target_machine_info, + [prof.exit_code for prof in target_specs], + cli_kwargs["target_collector_cmd"], + cli_kwargs["target_cmd"], + cli_kwargs["target_label"], + ) + + baseline_vuln = _extract_vulnerabilities(baseline_machine_info.get("cpu_vulnerabilities", {})) + target_vuln = _extract_vulnerabilities(target_machine_info.get("cpu_vulnerabilities", {})) + + baseline = core.ProfileMisc(baseline_env, baseline_metadata, baseline_vuln, baseline_stats) + target = core.ProfileMisc(target_env, target_metadata, target_vuln, target_stats) + + # Parse the input folded profiles into aggregated baseline and target LazyFrames, and create + # their Polars representation. + log.major_info("Parsing Input Folded Profiles") + postprocess_params = structs.PostprocessParameters(**cli_kwargs) + func_maps = pl_structs.FunctionMaps() + baseline_lf, baseline_features = folded_polars.folded_profiles_to_polars_lf( + [prof.path for prof in baseline_specs], postprocess_params, func_maps + ) + target_lf, target_features = folded_polars.folded_profiles_to_polars_lf( + [prof.path for prof in target_specs], postprocess_params, func_maps + ) + func_maps.finalize() + + pair_profile: pl_structs.PolarsTraceProfilePair = convert.create_polars_pair_profile( + baseline_lf, baseline_features, target_lf, target_features, func_maps + ) + log.minor_success("Parsing Input Folded Profiles") + + diff_html, creation_time = core.generate_report_view( + pair_profile, baseline, target, cli_kwargs["profiled_resource"], **cli_kwargs + ) + # FIXME: we do not have Perun profile objects that are necessary to automatically generate + # report names. Instead, we generate names with hopefully unique timestamps that should avoid + # overwriting other existing reports. + output_filename: Optional[str] = cli_kwargs.get("output_path") + if output_filename is None: + file_timestamp = creation_time.isoformat(sep="-", timespec="milliseconds").replace(":", "-") + output_path: Path = Path(f"report-folded_{file_timestamp}.html") + else: + output_path = Path(output_filename) + diff_kit.save_diff_view(output_path, diff_html) + + log.minor_status("Report saved", log.path_style(str(output_path))) diff --git a/perun/view_diff/report/meson.build b/perun/view_diff/report/meson.build new file mode 100755 index 000000000..96353d49d --- /dev/null +++ b/perun/view_diff/report/meson.build @@ -0,0 +1,15 @@ +perun_report_dir = perun_view_diff_dir / 'report' + +perun_report_files = files( + '__init__.py', + '__init__.pyi', + 'core.py', + 'diffs.py', + 'folded.py', + 'native.py', +) + +py3.install_sources( + perun_report_files, + subdir: perun_report_dir +) diff --git a/perun/view_diff/report/native.py b/perun/view_diff/report/native.py new file mode 100644 index 000000000..f58680003 --- /dev/null +++ b/perun/view_diff/report/native.py @@ -0,0 +1,150 @@ +"""A module for generating reports from Perun-native profiles.""" + +from __future__ import annotations + +# Standard Imports +from typing import Any, TYPE_CHECKING + +# Third-Party Imports + +# Perun Imports +from perun.profiles import structs +from perun.profiles.conversions import native_polars +from perun.profiles.polars import convert, structs as pl_structs +from perun.utils import log +from perun.utils.common import diff_kit +from perun.view_diff.report import core + +if TYPE_CHECKING: + from perun.profiles.native import Profile + + +def _extract_vulnerabilities(prof: Profile) -> list[structs.ProfileMetadataEntry]: + """Generates vulnerabilities from the given profile + + :param prof: profile for which we are generating the specification + :return: vulnerabilities as an entry + """ + machine_info = prof.get("machine", {}) + return [ + structs.ProfileMetadataEntry( + "vulnerabilities", + "?" if "cpu_vulnerabilities" not in machine_info else "", + "CPU vulnerabilities summary.", + machine_info.get("cpu_vulnerabilities", {}), + ) + ] + + +def _extract_environment_spec(prof: Profile) -> list[structs.ProfileMetadataEntry]: + """Generates profile specification from the given profile + + :param prof: profile for which we are generating the specification + + :return: the profile specification as a list of entries + """ + command = " ".join([prof["header"]["cmd"], prof["header"]["workload"]]).strip() + exitcode = diff_kit.format_exit_codes(prof["header"].get("exitcode", "?")) + machine_info = prof.get("machine", {}) + return [ + structs.ProfileMetadataEntry( + "origin", + prof.get("origin", "?"), + "The version control version, for which the profile was measured.", + ), + structs.ProfileMetadataEntry( + "profile label", + prof["header"].get("label", "-"), + "A label associated with this profile, if any.", + ), + structs.ProfileMetadataEntry( + "command", command, "The workload / command, for which the profile was measured." + ), + structs.ProfileMetadataEntry( + "exitcode", exitcode, "The exit code that was returned by the underlying command." + ), + structs.ProfileMetadataEntry( + "collector command", + log.collector_to_command(prof.get("collector_info", {})), + "The collector / profiler, which collected the data.", + ), + structs.ProfileMetadataEntry( + "kernel", + machine_info.get("release", "?"), + "The underlying kernel version, where the results were measured.", + ), + structs.ProfileMetadataEntry( + "boot info", + machine_info.get("boot_info", "?"), + "The contents of `/proc/cmdline` containing boot information about kernel", + ), + structs.ProfileMetadataEntry( + "host", machine_info["host"], "The hostname, where the results were measured." + ), + structs.ProfileMetadataEntry( + "cpu (total)", + machine_info.get("cpu", {"total": "?"}).get("total", "?"), + "The total number (physical and virtual) of CPUs available on the host.", + ), + structs.ProfileMetadataEntry( + "memory (total)", + machine_info.get("memory", {"total_ram": "?"}).get("total_ram", "?"), + "The total number of RAM available on the host.", + ), + ] + + +def generate_report_from_native_profiles( + baseline_profile: Profile, target_profile: Profile, **cli_kwargs: Any +) -> None: + """Generate an interactive report from Perun profiles. + + :param baseline_profile: the baseline Perun profile. + :param target_profile: the target Perun profile. + :param cli_kwargs: additional CLI parameters. + """ + + baseline = core.ProfileMisc( + _extract_environment_spec(baseline_profile), + baseline_profile.all_metadata(), + _extract_vulnerabilities(baseline_profile), + baseline_profile.all_stats(), + ) + target = core.ProfileMisc( + _extract_environment_spec(target_profile), + target_profile.all_metadata(), + _extract_vulnerabilities(target_profile), + target_profile.all_stats(), + ) + + # Parse the input native profiles into aggregated baseline and target LazyFrames, and create + # their Polars representation. + log.major_info("Parsing Input Perun Profiles") + postprocess_params = structs.PostprocessParameters(**cli_kwargs) + func_maps = pl_structs.FunctionMaps() + baseline_lf, baseline_features = native_polars.native_to_polars_lf( + baseline_profile, postprocess_params, func_maps + ) + target_lf, target_features = native_polars.native_to_polars_lf( + target_profile, postprocess_params, func_maps + ) + func_maps.finalize() + + pair_profile: pl_structs.PolarsTraceProfilePair = convert.create_polars_pair_profile( + baseline_lf, baseline_features, target_lf, target_features, func_maps + ) + log.minor_success("Parsing Input Perun Profiles") + + diff_html, creation_time = core.generate_report_view( + pair_profile, + baseline, + target, + list(baseline_profile["header"]["units"].values())[0], + **cli_kwargs, + ) + output_path = diff_kit.generate_output_filepath( + cli_kwargs["output_path"], "report", baseline_profile, target_profile + ) + diff_kit.save_diff_view(output_path, diff_html) + + log.minor_status("Report saved", log.path_style(str(output_path))) diff --git a/perun/view_diff/report_folded.py b/perun/view_diff/report_folded.py deleted file mode 100644 index f95c7b518..000000000 --- a/perun/view_diff/report_folded.py +++ /dev/null @@ -1,2472 +0,0 @@ -"""A comprehensive difference report of baseline-target external folded profiles. - -The report calculates a number of difference metrics for both inclusive and exclusive resource -consumption: - - - proportional diff (prop_diff): reports the difference between the relative resource - consumption proportionally to the total baseline and target consumption change. For example, - if the baseline and target consumed 2M and 1M CPU cycles in total, respectively, and a - function 'foo' consumed 100K cycles in both cases, the proportional difference is +5% as 'foo' - now consumes 10% total resources up from 5%. - - absolute diff (abs_diff): reports the difference of Target - Baseline resource consumption. - For example, if the baseline and target consumed 2M and 1M CPU cycles in total, respectively, - and a function 'foo' consumed 100K and 75K cycles in baseline, resp. target, the absolute - difference is -25K. - - relative diff (rel_diff): reports the difference of Target - Baseline resource consumption in - relative terms. For example, if the baseline and target consumed 2M and 1M CPU cycles in - total, respectively, and a function 'foo' consumed 100K and 80K cycles in baseline, resp. - target, the relative difference is -25%. - -""" - -from __future__ import annotations - -# Standard Imports -from collections import defaultdict -from collections.abc import Mapping, KeysView, Iterator, Sequence -import contextlib -import dataclasses -from datetime import datetime -import os -from pathlib import Path -import re -from subprocess import PIPE, TimeoutExpired -import tempfile -from types import TracebackType -from typing import ( - Any, - Protocol, - TextIO, - Literal, - Optional, - ClassVar, - TYPE_CHECKING, - Type, -) - -# Third-Party Imports -import polars as pl - -# Perun Imports -import perun - -# TODO: restructure so that we do not fetch pandas, numpy, etc. -from perun.profiles import native as profile -from perun.logic import config -from perun.profiles.native import imports -from perun.templates import factory as templates -from perun.utils import log, streams -from perun.utils.common import common_kit, diff_kit, script_kit -from perun.utils.external import processes, commands -from perun.utils.structs import diff_structs -from perun.utils.structs.common_structs import WebColorPalette -from perun.view_diff import flamegraph, report_native - -if TYPE_CHECKING: - from subprocess import Popen - - # The tempfile.NamedTemporaryFile context manager does not have a public type hint. - # Hence, we use the private tempfile object and alias it so that we avoid multiple warnings. - TempTextIO = tempfile._TemporaryFileWrapper[str] - - -class PolarsAggregate(Protocol): - """Polars aggregation callable type.""" - - def __call__(self, *args: str) -> pl.Expr: ... - - -def func_to_polars_callable(agg_func: str) -> PolarsAggregate: - """Maps aggregation function name to a polars callable. - - If the function name does not have a valid mapping, the median function is returned. - - :param agg_func: aggregation function name - :return: a polars callable - """ - try: - return { - "sum": pl.sum, - "avg": pl.mean, - "mean": pl.mean, - "med": pl.median, - "median": pl.median, - "min": pl.min, - "max": pl.max, - }[agg_func] - except KeyError: - log.warn( - f"Unsupported aggregation function '{agg_func}'. Using the default function 'median'." - ) - return pl.median - - -class ParseParameters: - """Profile parsing parameters. - - :ivar hide_generics: hide type generics in function names - :ivar squash_recursion: squash recursive calls into a single function record - :ivar squash_pattern: a regex pattern to filter functions to squash - :ivar aggregation_func: the aggregation method for multiple baseline or target profiles - """ - - __slots__ = "hide_generics", "squash_recursion", "squash_pattern", "aggregation_func" - - def __init__(self, hide_generics: bool, squash: bool, squash_regex: str, **_: Any) -> None: - """ - :param hide_generics: a flag indicating whether to hide type generics in function names - :param squash: squash recursive calls into a single function record - :param squash_regex: a regex pattern to filter functions to squash - """ - self.hide_generics: bool = hide_generics - self.squash_recursion: bool = squash - self.squash_pattern: Optional[re.Pattern[str]] = None - # Pre-compile non-default squash patterns. The default squash pattern matches everything - # so there is no need to do regex matching at all. - if squash and squash_regex != diff_structs.DEFAULT_SQUASH_RE: - self.squash_pattern = re.compile(squash_regex) - self.aggregation_func: PolarsAggregate = func_to_polars_callable( - config.lookup_key_recursively( - "profile.aggregation", default=diff_structs.DEFAULT_AGGREGATE_FUNC - ) - ) - - -class FilterParameters: - """Performance data filtering parameters. - - Large profiles contain too much data to efficiently process and visualize. We thus allow the - users to exclude data based on several thresholds and limits. The assumption is that if certain - functions and traces take very little total resources (time, samples, etc.), then they are - likely not the culprit of possible performance degradations. - - :ivar function_threshold: exclude functions that consumed less than the threshold ratio of the - total resources in *both* baseline and target - :ivar traces_threshold: exclude traces that consumed less than the threshold ratio of the - total resources in *both* baseline and target - :ivar max_function_traces: limit the number of most expensive traces stored per function - :ivar top_diffs: limit the number of overall most expensive traces and function diffs - """ - - __slots__ = "function_threshold", "traces_threshold", "max_function_traces", "top_diffs" - - def __init__( - self, - function_threshold: float, - traces_threshold: float, - max_function_traces: int, - top_diffs: int, - **_: Any, - ) -> None: - """ - :param function_threshold: the % threshold for function total resource consumption - :param traces_threshold: the % threshold for trace resource consumption - :param max_function_traces: the maximum number of most expensive traces to keep per function - :param top_diffs: the number of overall most expensive traces and function diffs - """ - # The thresholds are given as percentages, adjust to [0, 1] ratio. - self.function_threshold: float = function_threshold * 0.01 - self.traces_threshold: float = traces_threshold * 0.01 - self.max_function_traces: int = max_function_traces - self.top_diffs: int = top_diffs - - -class FunctionMaps: - """A collection of mappings between function names and IDs created during parsing of profiles. - - The mappings are used to map function names to their IDs and back, which optimizes both memory - and time of profile representation and manipulation. - - The maps may be reused when parsing multiple profiles (e.g., multiple baseline and target - profiles), which leads to faster and easier matching when merging the profiles. - - :ivar func_id_map: a mapping of function name -> function ID - :ivar func_id_reverse_map: a mapping of function ID -> function name; sorted by function ID - :ivar squashed_id_map: a mapping of squashed function ID -> function ID; squashed functions - refer to artificial function names representing merged recursive calls, e.g. 'func{x5}' - """ - - __slots__ = "func_id_map", "func_id_reverse_map", "squashed_id_map" - - def __init__(self) -> None: - self.func_id_map: defaultdict[str, int] = defaultdict(lambda: len(self.func_id_map)) - self.func_id_reverse_map: dict[int, str] = {} - self.squashed_id_map: dict[int, int] = {} - - def finalize(self) -> None: - """Construct the reverse map. - - The reverse map is sorted in ascending order according to the function IDs, i.e., the keys. - - The finalization should be done after all profiles are parsed. - """ - # The func_id_map is sorted by values, i.e., the function IDs (see the defaultdict factory - # lambda). Hence, the reverse map will be sorted as well as long as it is constructed by - # iterating over the func_id_map. - self.func_id_reverse_map = {f_id: f_name for f_name, f_id in self.func_id_map.items()} - - -class ProfileFeatures: - """A collection of profile features. - - The features are computed before any filtering of performance data takes place. - - :ivar total_resources: the total number of consumed resources - :ivar max_trace_len: the longest seen trace - :ivar measured_traces_count: the number of unique traces that have been measured; this does not - include traces that have no measured resource consumption, e.g., sub-traces of recorded - traces - :ivar measured_functions_count: the number of unique functions that have been measured in any - calling context; this does not include functions that have no measured resource - consumption, e.g., functions seen in traces but with no measured consumption - :ivar seen_traces_count: the number of all traces seen in a profile, including those that have - no recorded exclusive resource consumption - :ivar seen_functions_count: the number of all functions seen in a profile, including those that - have no recorded exclusive resource consumption - """ - - __slots__ = ( - "total_resources", - "max_trace_len", - "measured_traces_count", - "measured_functions_count", - "seen_traces_count", - "seen_functions_count", - ) - - def __init__(self) -> None: - self.total_resources: int = 0 - self.max_trace_len: int = 0 - self.measured_traces_count: int = 0 - self.measured_functions_count: int = 0 - self.seen_traces_count: int = 0 - self.seen_functions_count: int = 0 - - -@dataclasses.dataclass -class PolarsTraceProfile: - """A Polars DataFrame representation of a single profile containing traces (e.g., perf folded). - - The profile DataFrame has the following columns: 'func', 'trace', 'inclusive', 'exclusive' - where 'func' is the ID of the last function the trace, 'trace' is a semicolon-delimited string - of function IDs representing the trace, and 'inclusive' and 'exclusive' store the resource - consumption of the trace. - - For example, the profile DataFrame might look like this: - - ``` - shape: (369_033, 4) - ┌──────┬─────────────────────────────────┬───────────┬───────────┐ - │ func ┆ trace ┆ inclusive ┆ exclusive │ - │ --- ┆ --- ┆ --- ┆ --- │ - │ u32 ┆ str ┆ i64 ┆ i64 │ - ╞══════╪═════════════════════════════════╪═══════════╪═══════════╡ - │ 8 ┆ 0;3;2;4;5;6;7;8 ┆ 657306 ┆ 657306 │ - │ 15 ┆ 0;9;2;4;5;6;10;11;12;13;14;15 ┆ 475601 ┆ 475601 │ - │ 27 ┆ 0;17;16;4;5;18;19;20;21;22;23;… ┆ 683530 ┆ 683530 │ - │ … ┆ … ┆ … ┆ … │ - │ 6201 ┆ 6202;657;658;6186;6187;6200;62… ┆ 1360700 ┆ 1360700 │ - │ 453 ┆ 6202;657;658;6186;6187;6203;29… ┆ 78594 ┆ 78594 │ - │ 106 ┆ 6202;657;658;6186;6187;6203;29… ┆ 271800 ┆ 271800 │ - └──────┴─────────────────────────────────┴───────────┴───────────┘ - ``` - - :ivar profile: a DataFrame representation of a trace profile - :ivar features: a set of features describing and summarizing the profile - :ivar maps: function maps created during the parsing of the profile - """ - - __slots__ = "profile", "features", "maps" - - profile: pl.DataFrame - features: ProfileFeatures - maps: FunctionMaps - - -@dataclasses.dataclass -class PolarsTraceProfilePair: - """A Polars representation of a pair of baseline and target trace profiles. - - This representation should be preferred when working with a pair of profiles that are expected - to be compared and analyzed, as it also stores sets of functions and traces that appear in both - profiles. These sets take into account all functions and traces that appeared in both profiles, - even if they have no measured exclusive resource consumption (and as such are omitted from the - PolarsTraceProfile). Currently, only functions and traces that are exact match are considered - to be common to both profiles. - - :ivar baseline: the baseline trace profile - :ivar target: the target trace profile - :ivar common_traces: a DataFrame containing only one column 'trace' with traces that appear in - both profiles - :ivar common_funcs: a DataFrame containing only one column 'func' with function IDs that appear - in both profiles - """ - - __slots__ = "baseline", "target", "common_traces", "common_funcs" - - baseline: PolarsTraceProfile - target: PolarsTraceProfile - common_traces: pl.DataFrame - common_funcs: pl.DataFrame - - -@dataclasses.dataclass -class PolarsMergedTraceProfiles: - """A Polars representation of merged baseline-target trace and function profiles. - - This representation stores both per-trace and per-function profiles that contain merged rows - for some baseline and target profiles. - - *Structure*: Both DataFrames contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', - 'exclusive_target', 'prop_diff_incl', and 'prop_diff_excl' columns. The merged traces profile - additionally contains the 'trace' column. See the module docstring for description of the - difference metrics. - - The merged function profile contains aggregated resource consumption (aggregated separately for - baseline and target profiles) across all traces associated with a function. Furthermore, it - omits functions that have 0 exclusive cost and do not meet filtering thresholds - (see FilterParameters). - - The merged trace profile omits all traces that belong to filtered functions, and all traces that - have 0 exclusive cost and do not meet filtering thresholds (see FilterParameters). - - For example, the merged traces and merged function DataFrames might look like this: - - ``` - shape: (32_342, 8) - ┌──────┬───────────┬───────────┬───┬──────────────────┬────────────────┬────────────────┐ - │ func ┆ trace ┆ inclusive ┆ … ┆ exclusive_target ┆ prop_diff_incl ┆ prop_diff_excl │ - │ --- ┆ --- ┆ --- ┆ … ┆ --- ┆ --- ┆ --- │ - │ u32 ┆ str ┆ i64 ┆ … ┆ i64 ┆ f32 ┆ f32 │ - ╞══════╪═══════════╪═══════════╪═══╪══════════════════╪════════════════╪════════════════╡ - │ 4 ┆ 971;4 ┆ 808775194 ┆ … ┆ 0 ┆ -0.010969 ┆ -0.000006 │ - │ 5 ┆ 971;4;5 ┆ 808331377 ┆ … ┆ 0 ┆ -0.010963 ┆ -0.000009 │ - │ 238 ┆ 971;4;5;… ┆ 246640065 ┆ … ┆ 0 ┆ -0.003345 ┆ -0.000063 │ - │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ - │ 135 ┆ 1554;155… ┆ 0 ┆ … ┆ 24230903 ┆ 0.001149 ┆ 0.001077 │ - │ 6235 ┆ 4;5;6235 ┆ 0 ┆ … ┆ 2683915 ┆ 0.014867 ┆ 0.000119 │ - │ 185 ┆ 3471;29… ┆ 0 ┆ … ┆ 81537127 ┆ 0.003625 ┆ 0.003625 │ - └──────┴───────────┴───────────┴───┴──────────────────┴────────────────┴────────────────┘ - - shape: (482, 7) - ┌──────┬──────────────┬──────────────┬───┬──────────────────┬────────────────┬────────────────┐ - │ func ┆ inclusive ┆ exclusive ┆ … ┆ exclusive_target ┆ prop_diff_incl ┆ prop_diff_excl │ - │ --- ┆ --- ┆ --- ┆ … ┆ --- ┆ --- ┆ --- │ - │ u32 ┆ i64 ┆ i64 ┆ … ┆ i64 ┆ f32 ┆ f32 │ - ╞══════╪══════════════╪══════════════╪═══╪══════════════════╪════════════════╪════════════════╡ - │ 545 ┆ 905453006855 ┆ 203226689 ┆ … ┆ 162086637 ┆ 34.676102 ┆ 0.004449 │ - │ 1200 ┆ 4591717786 ┆ 486459792 ┆ … ┆ 243767256 ┆ 0.304794 ┆ 0.004239 │ - │ 3823 ┆ 108197824030 ┆ 104876145383 ┆ … ┆ 23210060569 ┆ -0.403142 ┆ -0.390618 │ - │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ - │ 1569 ┆ 49300568157 ┆ 208229571 ┆ … ┆ 54313042 ┆ -0.368518 ┆ -0.00041 │ - │ 6931 ┆ 0 ┆ 0 ┆ … ┆ 79892101 ┆ 0.184407 ┆ 0.003551 │ - │ 777 ┆ 25804957237 ┆ 12916984718 ┆ … ┆ 1312987872 ┆ -0.24692 ┆ -0.116819 │ - └──────┴──────────────┴──────────────┴───┴──────────────────┴────────────────┴────────────────┘ - ``` - - Note that if a function or trace shows 0 resource consumption in target or baseline, it does not - mean that such function or trace is baseline-only or target-only, respectively, since the - baseline and target profiles have already omitted functions and traces that exist in the profile - but have no exclusive resource consumption (see PolarsTraceProfile for more details). Use the - 'common_traces' and 'common_funcs' to determine whether a 0 resource consumption indicates - baseline-only or target-only records. - - :ivar traces: a merged trace profile - :ivar funcs: a merged per-function profile - """ - - __slots__ = "traces", "funcs" - - traces: pl.DataFrame - funcs: pl.DataFrame - - -@dataclasses.dataclass -class PolarsTabularTraceProfiles: - """A Polars representation of baseline-target trace and function tabular profiles. - - The tabular trace and function profiles are similar to the merged profiles but contain fewer - records, additional difference metrics, and are sorted such that they can be easily iterated in - a lockstep to generate tabular records for diff reports. - - *Structure*: Both DataFrames contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', - 'exclusive_target', 'prop_diff_incl', 'prop_diff_excl', 'abs_diff_incl', 'abs_diff_excl', - 'rel_diff_incl', and 'rel_diff_excl' columns. The traces profile additionally contains the - 'trace' column. See the module docstring for description of the difference metrics. - - *Filtered*: The trace profile will have only up to top 'max_traces_per_func' traces per each - function w.r.t. the 'prop_diff_incl' and 'prop_diff_excl' metric. Note that the traces are - deduplicated; for example, if `max_traces_per_func` = 10 and some function has a total of 15 - traces, but only 13 of them appear in both top selections, the resulting table will keep only - the 13 traces instead of 20 (7 of which would be duplicated). - - *Sorted*: Both profiles are sorted by the function IDs in the ascending order. - - For example, the tabular traces and function DataFrames might look like this: - - ``` - shape: (3_447, 12) - ┌──────┬─────────────┬───────────────┬───┬───────────────┬───────────────┬───────────────┐ - │ func ┆ trace ┆ inclusive ┆ … ┆ abs_diff_excl ┆ rel_diff_incl ┆ rel_diff_excl │ - │ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- │ - │ u32 ┆ str ┆ i64 ┆ ┆ i64 ┆ f32 ┆ f32 │ - ╞══════╪═════════════╪═══════════════╪═══╪═══════════════╪═══════════════╪═══════════════╡ - │ 4 ┆ 1;268;4 ┆ 1246557813681 ┆ … ┆ -39682724 ┆ -1.382918 ┆ -68.930069 │ - │ 4 ┆ 1;124;4 ┆ 0 ┆ … ┆ 1900490 ┆ 100.0 ┆ 100.0 │ - │ 4 ┆ 1;1174;4 ┆ 0 ┆ … ┆ 260703 ┆ 100.0 ┆ 100.0 │ - │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ - │ 7483 ┆ 5284;5304;… ┆ 0 ┆ … ┆ 14016473 ┆ 100.0 ┆ 100.0 │ - │ 7483 ┆ 5284;5290;… ┆ 0 ┆ … ┆ 8070493 ┆ 100.0 ┆ 100.0 │ - │ 7483 ┆ 5284;5290;… ┆ 0 ┆ … ┆ 8065807 ┆ 100.0 ┆ 100.0 │ - └──────┴─────────────┴───────────────┴───┴───────────────┴───────────────┴───────────────┘ - - shape: (482, 11) - ┌──────┬───────────────┬───┬────────────────┬───────────────┬───────────────┬───────────────┐ - │ func ┆ inclusive ┆ … ┆ abs_diff_incl ┆ abs_diff_excl ┆ rel_diff_incl ┆ rel_diff_excl │ - │ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │ - │ u32 ┆ i64 ┆ ┆ i64 ┆ i64 ┆ f32 ┆ f32 │ - ╞══════╪═══════════════╪═══╪════════════════╪═══════════════╪═══════════════╪═══════════════╡ - │ 4 ┆ 4238716948387 ┆ … ┆ -2653980496373 ┆ -52677630 ┆ -62.612827 ┆ -42.958553 │ - │ 5 ┆ 4247354710131 ┆ … ┆ -2658348948953 ┆ -97869614 ┆ -62.588345 ┆ -20.323265 │ - │ 11 ┆ 196590265531 ┆ … ┆ -196590265531 ┆ -1199560386 ┆ -100.0 ┆ -100.0 │ - │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │ - │ 6941 ┆ 0 ┆ … ┆ 24743188735 ┆ 95707629 ┆ 100.0 ┆ 100.0 │ - │ 6942 ┆ 0 ┆ … ┆ 24044566859 ┆ 225687075 ┆ 100.0 ┆ 100.0 │ - │ 7483 ┆ 0 ┆ … ┆ 3459034839 ┆ 71846967 ┆ 100.0 ┆ 100.0 │ - └──────┴───────────────┴───┴────────────────┴───────────────┴───────────────┴───────────────┘ - ``` - - Note that if a function or trace shows 0 resource consumption in target or baseline, it does not - mean that such function or trace is baseline-only or target-only, respectively, since the - baseline and target profiles have already omitted functions and traces that exist in the profile - but have no exclusive resource consumption (see PolarsTraceProfile for more details). Use the - 'common_traces' and 'common_funcs' to determine whether a 0 resource consumption indicates - baseline-only or target-only records. - - :ivar traces: a merged trace profile - :ivar funcs: a merged per-function profile - """ - - __slots__ = "traces", "funcs" - - traces: pl.DataFrame - funcs: pl.DataFrame - - -@dataclasses.dataclass -class KeyDiffs: - """A collection of the most significant differences of a key within a partitioned profile. - - The class stores the top increases and decreases of a key (metric) for baseline-only, - target-only, and common partitions of a merged profile. - - The individual DataFrames contain only two columns: "symbol" and "diff". - - "symbol" identifies either a trace or a function by their IDs, and - - "diff" is the value of the diff metric. - - :ivar baseline_top_inc: the largest increases in baseline-only records - :ivar baseline_top_dec: the largest decreases in baseline-only records - :ivar target_top_inc: the largest increases in target-only records - :ivar target_top_dec: the largest decreases in target-only records - :ivar common_top_inc: the largest increases in common records - :ivar common_top_dec: the largest decreases in common records - """ - - __slots__ = ( - "baseline_top_inc", - "baseline_top_dec", - "target_top_inc", - "target_top_dec", - "common_top_inc", - "common_top_dec", - ) - - baseline_top_inc: pl.DataFrame - baseline_top_dec: pl.DataFrame - target_top_inc: pl.DataFrame - target_top_dec: pl.DataFrame - common_top_inc: pl.DataFrame - common_top_dec: pl.DataFrame - - -class FlameGraphSettings: - """A collection of flamegraph parameters and settings to be used when rendering flamegraphs. - - :ivar width: the width of the flamegraph image - :ivar height: the height of each function frame in the flamegraph - :ivar min_width: the minimum width of a function frame, either pixels or percentage of time - :ivar maxtrace: the longest trace in the flamegraph, should account for the min_width filtering - :ivar fonttype: the font type - :ivar fontsize: the font size - :ivar countname: the resource type used in the profile, e.g., samples or CPU cycles - :ivar colors: the color palette to use for frames - :ivar bgcolors: the image background color - :ivar inverted: whether an icicle graph should be rendered instead - :ivar rootnode: the root node name - :ivar subrootnode: the sub-root node name - :ivar total: the total amount of consumed resources - :ivar normalize: normalize the sample counts in differential graphs - :ivar parallelize: parallelize the creation of flamegraph grids - :ivar use_perl: use the Perl variants of flame graph scripts - :ivar fg_script_path: the path to the flamegraph script - :ivar difffolded_path: the path to the difffolded script - :ivar difffolded_path: the path to the diff_flamegraph script - """ - - __slots__ = ( - "width", - "height", - "min_width", - "maxtrace", - "fonttype", - "fontsize", - "countname", - "colors", - "bgcolors", - "inverted", - "rootnode", - "subrootnode", - "total", - "normalize", - "parallelize", - "use_perl", - "fg_script_path", - "difffolded_path", - "diff_fg_path", - ) - - # Default flamegraph parameters reconstructed from the flamegraph.pl script. - DefaultImageWidth: ClassVar[int] = 1200 - DefaultFrameHeight: ClassVar[int] = 16 - DefaultMinWidth: ClassVar[str] = "0.1" - DefaultMaxTrace: ClassVar[int] = 0 - DefaultFontType: ClassVar[str] = "Verdana" - DefaultFontSize: ClassVar[int] = 12 - DefaultCountName: ClassVar[str] = "samples" - DefaultColors: ClassVar[str] = "hot" - DefaultBgColors: ClassVar[str] = "" - DefaultInverted: ClassVar[bool] = False - DefaultRootNode: ClassVar[str] = "all" - DefaultSubRootNode: ClassVar[str] = "subtotal" - DefaultTotal: ClassVar[int] = 0 - - # The map links the attribute names and their default values for easier iteration over the - # keyword parameters. This map ignores the flags, e.g., 'inverted'. - KwAttributeMap: ClassVar[dict[str, int | str]] = { - "width": DefaultImageWidth, - "height": DefaultFrameHeight, - "min_width": DefaultMinWidth, - "maxtrace": DefaultMaxTrace, - "fonttype": DefaultFontType, - "fontsize": DefaultFontSize, - "countname": DefaultCountName, - "colors": DefaultColors, - "bgcolors": DefaultBgColors, - "rootnode": DefaultRootNode, - "subrootnode": DefaultSubRootNode, - "total": DefaultTotal, - } - - def __init__( - self, - width: int = DefaultImageWidth, - height: int = DefaultFrameHeight, - min_width: str = DefaultMinWidth, - maxtrace: int = DefaultMaxTrace, - fonttype: str = DefaultFontType, - fontsize: int = DefaultFontSize, - countname: str = DefaultCountName, - colors: str = DefaultColors, - bgcolors: str = DefaultBgColors, - inverted: bool = DefaultInverted, - rootnode: str = DefaultRootNode, - subrootnode: str = DefaultSubRootNode, - total: int = DefaultTotal, - normalize: bool = False, - parallelize: bool = True, - use_perl_scripts: bool = False, - **_: Any, - ) -> None: - """ - :param width: the width of the flamegraph image - :param height: the height of each function frame in the flamegraph - :param min_width: the minimum width of a function frame, either pixels or percentage of time - :param maxtrace: the longest trace in the flamegraph after the min_width filtering - :param fonttype: the font type - :param fontsize: the font size - :param countname: the resource type used in the profile, e.g., samples or CPU cycles - :param colors: the color palette to use for frames - :param bgcolors: the image background color - :param inverted: whether an icicle graph should be rendered instead - :param rootnode: the root node name - :param subrootnode: the sub-root node name - :param total: the total amount of consumed resources - :param parallelize: parallelize the creation of flamegraph grids - :param use_perl_scripts: use the Perl variants of flame graph scripts - """ - # We call the type conversion functions since the parameters may be supplied from CLI - # where the types do not necessarily have to match. - self.width: int = int(width) - self.height: int = int(height) - self.min_width: str = str(min_width) - self.maxtrace: int = int(maxtrace) - self.fonttype: str = str(fonttype) - self.fontsize: int = int(fontsize) - self.countname: str = str(countname) - self.colors: str = str(colors) - self.bgcolors: str = str(bgcolors) - self.inverted: bool = bool(inverted) - self.rootnode: str = str(rootnode) - self.subrootnode: str = str(subrootnode) - self.total: int = int(total) - self.normalize: bool = bool(normalize) - - self.parallelize: bool = parallelize - self.use_perl = use_perl_scripts - suffix: str = ".pl" if use_perl_scripts else ".py" - self.fg_script_path: Path = Path(script_kit.get_script(f"flamegraph{suffix}")) - self.difffolded_path: Path = Path(script_kit.get_script("difffolded.pl")) - self.diff_fg_path: Path = Path(script_kit.get_script("diff_flamegraph.py")) - - @classmethod - def from_cli(cls, **cli_kwargs: Any) -> FlameGraphSettings: - """Initialize the flamegraph settings from CLI and init parameters. - - The parameters supplied through CLI will have a 'flamegraph_' prefix. Parameters supplied - additionally by the caller (e.g., to overwrite certain CLI parameters) do not have to use - the prefix. However, the order of specification matters: in order to overwrite CLI - parameters, the additional parameters need to be specified after the CLI parameters. - - :param cli_kwargs: the CLI parameters - :return: an initialized FlameGraphSettings instance - """ - return cls( - **{ - arg_name.replace("flamegraph_", ""): arg_value - for arg_name, arg_value in cli_kwargs.items() - if arg_value is not None - } - ) - - def get_nondefault_kw_attributes(self) -> dict[str, str | int]: - """Construct a collection of keyword attributes that have non-default values. - - :return: a dictionary of all keyword attributes with non-default values - """ - params: dict[str, str | int] = {} - for attr_name, attr_default_value in self.KwAttributeMap.items(): - if (value := getattr(self, attr_name)) != attr_default_value: - params[attr_name] = value - return params - - def compute_minwidth_threshold(self) -> float: - """Compute the minimum width threshold for flamegraph blocks to be displayed. - - The threshold is needed to correctly compute the maxtrace value so that flamegraphs in - a grid are correctly aligned and have the appropriate height. - - Reconstructed from the flamegraph.pl script. - - :return: the minimum width threshold - """ - try: - # The minimum width was provided as a percentage - if self.min_width.endswith("%"): - return self.total * float(self.min_width[:-1]) / 100 - # The minimum width is set in pixels - x_padding: int = 10 - width_per_time: float = (self.width - 2 * x_padding) / self.total - return float(self.min_width) / width_per_time - except ZeroDivisionError: - # No total provided, we set the threshold so that it does not filter anything. - return 0.0 - - -@dataclasses.dataclass -class FlameGraphGrid: - """A collection of flamegraphs that form a 2x2 grid of baseline, target and their diffs. - - The stored flamegraphs are already properly escaped and as such may be directly embedded into - an HTML report. - - :ivar baseline: the baseline flamegraph - :ivar target: the target flamegraph - :ivar baseline_target_diff: the baseline-target difference flamegraph - :ivar target_baseline_diff: the target-baseline difference flamegraph - """ - - __slots__ = "baseline", "target", "baseline_target_diff", "target_baseline_diff" - - # The default flamegraph titles used when generating the flamegraphs. - DefaultTitles: ClassVar[tuple[str, str, str, str]] = ( - "Baseline Flamegraph", - "Target Flamegraph", - "Baseline > Target Diff Flamegraph", - "Target > Baseline Diff Flamegraph", - ) - # The tags used for escaping flamegraphs in the grid. - EscapeTags: ClassVar[tuple[str, str, str, str]] = ("lhs_0", "rhs_0", "lhs_diff_0", "rhs_diff_0") - - def __init__( - self, - baseline: str = "", - target: str = "", - baseline_target_diff: str = "", - target_baseline_diff: str = "", - ) -> None: - """ - :param baseline: the baseline flamegraph - :param target: the target flamegraph - :param baseline_target_diff: the baseline-target difference flamegraph - :param target_baseline_diff: the target-baseline difference flamegraph - """ - self.baseline: str = baseline - self.target: str = target - self.baseline_target_diff: str = baseline_target_diff - self.target_baseline_diff: str = target_baseline_diff - - def __setitem__(self, index: int, flame_graph: str) -> None: - """Set a new flamegraph according to the index. - - A helper method for access to individual flamegraphs, e.g., in a loop. Supported indices - are [0, 3] and correspond to the baseline, target, baseline_target_diff, and - target_baseline_diff flamegraphs. - - :param index: the index of the flamegraph; must be within the [0, 3] range - :param flame_graph: the new flamegraph - """ - setattr(self, self.__slots__[index], flame_graph) - - def __getitem__(self, index: int) -> str: - """Get the flamegraph currently stored at an index. - - A helper method for access to individual flamegraphs, e.g., in a loop. Supported indices - are [0, 3] and correspond to the baseline, target, baseline_target_diff, and - target_baseline_diff flamegraphs. - - :param index: the index of the flamegraph to retrieve; must be within the [0, 3] range - - :return: the flamegraph currently stored at the index - """ - return getattr(self, self.__slots__[index]) - - def copy_from(self, grid: FlameGraphGrid) -> None: - """Copies flamegraphs from another grid into this grid. - - :param grid: the other grid to copy the flamegraphs from - """ - self.baseline = grid.baseline - self.target = grid.target - self.baseline_target_diff = grid.baseline_target_diff - self.target_baseline_diff = grid.target_baseline_diff - - -@dataclasses.dataclass -class FlameGraphGridCommands: - """A collection of commands for generating a flamegraph grid. - - :ivar baseline: the command to generate the baseline flamegraph - :ivar target: the command to generate the target flamegraph - :ivar baseline_target_difffolded: the command to diff baseline and target folded profiles - :ivar baseline_target_fg_diff: the command to generate baseline-target difference flamegraph - :ivar target_baseline_difffolded: the command to diff target and baseline folded profiles - :ivar target_baseline_fg_diff: the command to generate target-baseline difference flamegraph - """ - - __slots__ = ( - "baseline", - "target", - "baseline_target_difffolded", - "baseline_target_fg_diff", - "target_baseline_difffolded", - "target_baseline_fg_diff", - ) - - baseline: str - target: str - baseline_target_difffolded: str - baseline_target_fg_diff: str - target_baseline_difffolded: str - target_baseline_fg_diff: str - - -class FlameGraphGridBuilder(Protocol): - """A context manager interface for flamegraph grid creation. - - This context manager interface serves as a wrapper to simplify and unify the code for both - serial and parallel creation of flamegraph grids. Although a flamegraph grid can be built - serially without the need for a context manager, building the grid in parallel background - processes needs a context manager that properly terminates and cleans-up the processes and - other resources when needed. - """ - - def __enter__(self): ... - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: ... - - -class FlameGraphGridSerialBuilder: - """A context manager wrapper over serial flamegraph grid creation. - - The flamegraph grid creation begins when the class is instantiated. When the context manager - is entered, the flamegraph grid is already available. - - See FlameGraphGridBuilder for more details. - """ - - def __init__( - self, - grid: FlameGraphGrid, - grid_commands: FlameGraphGridCommands, - ) -> None: - """ - :param grid: [out] a reference to the grid object for the generated flamegraphs; context - managers cannot explicitly return values, hence we use an output parameter - :param grid_commands: the commands for generating a flamegraph grid - """ - # We cannot simply assign the grids as it would not propagate outside the object. - grid.copy_from(build_flamegraph_grid_serially(grid_commands)) - - def __enter__(self): - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: - return None - - -class FlameGraphGridParallelBuilder(contextlib.ExitStack): - """A context manager for parallel background flamegraph grid creation. - - Each flamegraph in the grid is created using a separate process that runs in the background. - The processes are spawned when the class is instantiated and terminated (waited for) when the - context manager scope is about to be exited. Only the __exit__ method is blocking, meaning the - code within the context manager scope is executed in parallel to the flamegraph processes - running in the background. - - This class inherits from the ExitStack since it internally creates a lot of other context - managers that must be properly managed and exited even if an error occurs. - - :ivar fg_handles: handles to temporary output files for the created flamegraphs; pipes have - limited buffer sizes and we do not want to periodically scan for output or use blocking - methods for reading the output - :ivar diff_processes: handles of difffolded.pl (.py) processes that generate data for diff - flamegraphs - :ivar fg_processes: handles of flamegraph.pl (.py) processes - :ivar grid: a reference to the grid object for the generated flamegraphs; context managers - cannot explicitly return values, hence we use an output parameter - """ - - __slots__ = "fg_handles", "diff_processes", "fg_processes", "grid" - - def __init__( - self, - grid: FlameGraphGrid, - grid_commands: FlameGraphGridCommands, - ) -> None: - """ - :param grid: [out] a grid object which will contain the generated flamegraphs - :param grid_commands: the commands for generating a flamegraph grid - """ - super().__init__() - self.fg_handles: list[TempTextIO] = [] - self.diff_processes: list[Popen[bytes]] = [] - self.fg_processes: list[Popen[bytes]] = [] - self.grid: FlameGraphGrid = grid - - log.major_info("Creating Flame Graph Grid (In Parallel)") - - # Here we start populating the ExitStack with context managers. - # Spawn 4 temporary files that will store the generated flamegraphs. - self.fg_handles = [ - self.enter_context(tempfile.NamedTemporaryFile(mode="w+")) for _ in range(4) - ] - # Spawn two difffolded processes: one for each diff flamegraph. - # The processes should be spawned only if we are using the original Perl scripts and not - # the optimized Python ones, which is indicated by empty difffolded commands. - if grid_commands.baseline_target_difffolded: - self.diff_processes = [ - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.baseline_target_difffolded, {"stdout": PIPE} - ) - ), - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.target_baseline_difffolded, {"stdout": PIPE} - ) - ), - ] - # Spawn four flamegraph processes: one for each flamegraph in the grid. - base_tar_kwargs: dict[str, Any] = {"stdout": self.fg_handles[2]} - tar_base_kwargs: dict[str, Any] = {"stdout": self.fg_handles[3]} - # Handle both Perl and Python versions. - if grid_commands.baseline_target_difffolded: - base_tar_kwargs["stdin"] = self.diff_processes[0].stdout - tar_base_kwargs["stdin"] = self.diff_processes[1].stdout - self.fg_processes = [ - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.baseline, {"stdout": self.fg_handles[0]} - ) - ), - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.target, {"stdout": self.fg_handles[1]} - ) - ), - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.baseline_target_fg_diff, - base_tar_kwargs, - ) - ), - self.enter_context( - processes.nonblocking_subprocess( - grid_commands.target_baseline_fg_diff, - tar_base_kwargs, - ) - ), - ] - log.minor_success("Spawning flamegraph background processes") - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: - """Context manager exit method that gathers the flamegraphs unless an error occurred. - - If an error occurred either somewhere in the context manager scope or within this method, - the ExitStack exits all stored context managers and the error is propagated outside. - In such case, the flamegraph grid is not guaranteed to be fully created depending on where - the error happened. - - If no error occurred, this method waits for all flamegraph processes to finish and populates - the output grid object. - - Note that this is a blocking method. - - :param exc_type: the type of the exception that occurred, if any - :param exc_val: the actual exception object, if any - :param exc_tb: the traceback of the error, if any - """ - try: - # Only wait for and gather the flamegraphs if no error occurred yet. - if exc_type is None: - # We do not want to modify the list of handles directly. Instead, we keep a list of - # handle indices corresponding to processes that have not finished yet. - running_fg_processes: list[int] = list(range(len(self.fg_processes))) - while running_fg_processes: - for list_idx, proc_idx in enumerate(running_fg_processes): - try: - self.fg_processes[proc_idx].wait(timeout=0.1) - # This process has finished, store the generated flamegraph in the grid. - self.fg_handles[proc_idx].seek(0) - self.grid[proc_idx] = self.fg_handles[proc_idx].read() - log.minor_success(FlameGraphGrid.DefaultTitles[proc_idx], "generated") - running_fg_processes.pop(list_idx) - break - except TimeoutExpired: - # This process is still running, check the next one. - pass - # The flamegraphs stored in the grid are not escaped yet. The escaping is not - # concurrency-safe, hence we must escape the flamegraphs only after all of them are - # created. - for idx, tag in enumerate(FlameGraphGrid.EscapeTags): - self.grid[idx] = flamegraph.escape_content(tag, self.grid[idx]) - - # Cleanup the difffolded processes properly. If the diff flamegraph processes - # finished correctly, the difffolded processes must have finished as well. - for diff_process in self.diff_processes: - diff_process.wait() - log.minor_success("Flamegraph grid", "completed") - except: - # We use the catch-all except clause deliberately, as the resources should be cleaned - # no matter what errors happened. - super().__exit__(exc_type, exc_val, exc_tb) - raise - # The flamegraph grid is complete, cleanup all the context managers and resources. - return super().__exit__(exc_type, exc_val, exc_tb) - - -def generate_report_from_folded( - baseline_profiles: str, target_profiles: str, **cli_kwargs: Any -) -> None: - """Generate an interactive report from external folded profiles. - - :param baseline_profiles: the specification of baseline profiles from CLI; either a CSV file - path, or a profile path with possibly an exit code and stat values - :param target_profiles: the specification of target profiles from CLI; either a CSV file path, - or a profile path with possibly an exit code and stat values - :param cli_kwargs: additional parameters from the CLI - """ - # Parse the profile specifications. - base_dir = Path(cli_kwargs.get("baseline_dir", os.getcwd())) - tar_dir = Path(cli_kwargs.get("target_dir", os.getcwd())) - base_profiles, base_stats = imports.parse_perf_import_entries( - [baseline_profiles], base_dir, cli_kwargs.get("baseline_stats_headers", None) - ) - tar_profiles, tar_stats = imports.parse_perf_import_entries( - [target_profiles], tar_dir, cli_kwargs.get("target_stats_headers", None) - ) - if not base_profiles: - log.error("No valid baseline profiles specified. Terminating.") - elif not tar_profiles: - log.error("No valid target profiles specified. Terminating.") - - # Transform some of the CLI kwargs into proper structures. - # TODO: create another structure for profile specifications, e.g., headers, metadata, etc. - parse_params: ParseParameters = ParseParameters(**cli_kwargs) - filter_params: FilterParameters = FilterParameters(**cli_kwargs) - fg_settings: FlameGraphSettings = FlameGraphSettings.from_cli( - **cli_kwargs, - countname=cli_kwargs["profiled_resource"], - rootnode="Maximum (Baseline, Target)", - subrootnode="Profile Total", - ) - - # Parse the input profiles and create their Polars representation. - log.major_info("Parsing Input Folded Profiles") - pair_profile: PolarsTraceProfilePair = folded_profiles_to_polars_pair_profile( - [prof.path for prof in base_profiles], [prof.path for prof in tar_profiles], parse_params - ) - log.minor_success("Parsing input folded profiles") - - fg_settings.total = max( - pair_profile.baseline.features.total_resources, pair_profile.target.features.total_resources - ) - minwidth_threshold = fg_settings.compute_minwidth_threshold() - - # Dump the parsed and post-processed profiles back into a folded format for flamegraph scripts. - grid: FlameGraphGrid = FlameGraphGrid() - log.minor_info("Saving post-processed profiles in temporary files.") - with ( - tempfile.NamedTemporaryFile(mode="w+") as baseline_folded, - tempfile.NamedTemporaryFile(mode="w+") as target_folded, - ): - base_maxtrace = polars_traces_to_folded_profile( - pair_profile.baseline, baseline_folded, minwidth_threshold - ) - tar_maxtrace = polars_traces_to_folded_profile( - pair_profile.target, target_folded, minwidth_threshold - ) - fg_settings.maxtrace = max(base_maxtrace, tar_maxtrace) - log.minor_success("Saving post-processed profiles in temporary files") - - # Build the flamegraph grid. The grid may be built serially or in parallel background - # processes. - grid_commands = build_flamegraph_grid_commands( - Path(baseline_folded.name), Path(target_folded.name), fg_settings - ) - with build_flamegraph_grid(grid, grid_commands, fg_settings.parallelize): - # If we build the flamegraphs in the background, we can analyze and further postprocess - # the profiles in the meantime. - log.minor_info("Analyzing and comparing profiles.") - - # Merge the baseline and target profiles and obtain the top differences in traces and - # functions between the two profiles. - merged = merge_and_filter_polars_profiles( - pair_profile.baseline, pair_profile.target, filter_params - ) - trace_top_diffs = compute_top_diffs( - merged.traces.lazy(), - pair_profile.common_traces.lazy(), - "trace", - "prop_diff_incl", - "prop_diff_excl", - filter_params.top_diffs, - ) - func_top_diffs = compute_top_diffs( - merged.funcs.lazy(), - pair_profile.common_funcs.lazy(), - "func", - "prop_diff_incl", - "prop_diff_excl", - filter_params.top_diffs, - ) - - # Extend the profiles with new difference metrics and transform them such that they - # are suitable for building the traces table. - tabular_profile = polars_merged_to_tabular_profiles( - merged, filter_params.max_function_traces - ) - - # Generate diffs of headers, stats, metadata, vulnerabilities, etc. - base_metadata = imports.parse_metadata( - cli_kwargs.get("baseline_metadata", tuple()), base_dir - ) - tar_metadata = imports.parse_metadata( - cli_kwargs.get("target_metadata", tuple()), tar_dir - ) - base_machine_info = imports.get_machine_info( - cli_kwargs.get("baseline_machine_info", ""), base_dir - ) - tar_machine_info = imports.get_machine_info( - cli_kwargs.get("target_machine_info", ""), tar_dir - ) - - lhs_header, rhs_header = diff_kit.generate_diff_of_headers( - generate_specification( - base_machine_info, - [prof.exit_code for prof in base_profiles], - cli_kwargs["baseline_collector_cmd"], - cli_kwargs["baseline_cmd"], - cli_kwargs["baseline_label"], - ), - generate_specification( - tar_machine_info, - [prof.exit_code for prof in tar_profiles], - cli_kwargs["target_collector_cmd"], - cli_kwargs["target_cmd"], - cli_kwargs["target_label"], - ), - ) - lhs_vulnerabilities, rhs_vulnerabilities = diff_kit.generate_diff_of_headers( - generate_vulnerabilities(base_machine_info.get("cpu_vulnerabilities", {})), - generate_vulnerabilities(tar_machine_info.get("cpu_vulnerabilities", {})), - ) - lhs_diff_stats, rhs_diff_stats = diff_kit.generate_diff_of_stats(base_stats, tar_stats) - lhs_fg_diff_stats, rhs_fg_diff_stats = diff_kit.generate_diff_of_stats( - features_to_stats(pair_profile.baseline.features, fg_settings.countname), - features_to_stats(pair_profile.target.features, fg_settings.countname), - ) - lhs_meta, rhs_meta = diff_kit.generate_diff_of_headers(base_metadata, tar_metadata) - - log.minor_success("Analyzing and comparing profiles") - - # Process user-defined chatbot prompt context, if provided. - prompt_ctx = "" - if cli_kwargs["chatbot_url"] is not None: - prompt_ctx_sources: Optional[tuple[str, ...]] = cli_kwargs.get( - "chatbot_prompt_context", None - ) - if prompt_ctx_sources is not None: - log.minor_info("Processing chatbot prompt context") - prompt_ctx = report_native.compose_chatbot_contexts(prompt_ctx_sources) - - template = templates.get_template("diff_views/report.html.jinja2") - - utc_time_now = datetime.utcnow() - report_time = utc_time_now.strftime("%d %b %Y, %H:%M:%S") + " UTC" - is_report_offline = config.lookup_key_recursively("showdiff.offline", False) - report_links = list(cli_kwargs.get("link", [])) - # Transform the flamegraph grid into the format expected by the report template. - flamegraphs = [ - ( - f"Inclusive {fg_settings.countname} [#]", - grid.baseline, - grid.target, - grid.baseline_target_diff, - grid.target_baseline_diff, - ) - ] - - log.major_info("Rendering HTML Report") - - content = template.render( - title="Perun Report - Profiles Comparison", - perun_version=perun.__version__, - timestamp=report_time, - chatbot=cli_kwargs["chatbot_url"], - chatbot_prompt_context=prompt_ctx, - lhs_tag="Baseline", - lhs_header=lhs_header, - lhs_vulnerabilities=lhs_vulnerabilities, - lhs_user_stats=lhs_diff_stats, - lhs_fg_stats=lhs_fg_diff_stats, - lhs_metadata=lhs_meta, - rhs_tag="Target", - rhs_header=rhs_header, - rhs_vulnerabilities=rhs_vulnerabilities, - rhs_user_stats=rhs_diff_stats, - rhs_fg_stats=rhs_fg_diff_stats, - rhs_metadata=rhs_meta, - palette=WebColorPalette, - stat_list=[flamegraphs[0][0]], - units=[fg_settings.countname], - nodes=iter(pair_profile.baseline.maps.func_id_reverse_map.values()), - use_perl=fg_settings.use_perl, - flamegraphs=flamegraphs, - selection_table=iterate_polars_tabular( - tabular_profile, - pair_profile.common_funcs, - pair_profile.baseline.maps.func_id_reverse_map, - fg_settings.countname, - ), - max_traces_per_func=filter_params.max_function_traces, - offline=is_report_offline, - notes_enabled=True, - links=report_links, - default_theme=cli_kwargs.get("default_theme", "dark"), - top_diffs_trace_inclusive=iterate_top_diffs(trace_top_diffs[0]), - top_diffs_trace_exclusive=iterate_top_diffs(trace_top_diffs[1]), - top_diffs_func_inclusive=iterate_top_diffs(func_top_diffs[0]), - top_diffs_func_exclusive=iterate_top_diffs(func_top_diffs[1]), - total_baseline=pair_profile.baseline.features.total_resources, - total_target=pair_profile.target.features.total_resources, - is_folded=True, - ) - log.minor_success("HTML report", "rendered") - - # FIXME: we do not have Perun profile objects that are necessary to automatically generate - # report names. Instead, we generate names with hopefully unique timestamps that should avoid - # overwriting other existing reports. - output_filename: Optional[str] = cli_kwargs.get("output_path") - if output_filename is None: - # Get the report time with millisecond precision so that we do not risk overwriting reports. - file_timestamp = utc_time_now.isoformat(sep="-", timespec="milliseconds").replace(":", "-") - output_filename = f"report-folded_{file_timestamp}" + ".html" - if not output_filename.endswith("html"): - output_filename += ".html" - - with open(output_filename, "w", encoding="utf-8") as template_out: - template_out.write(content) - - log.minor_status("Report saved", log.path_style(output_filename)) - - -@contextlib.contextmanager -def open_folded_profile(filepath: Path) -> Iterator[TextIO]: - """Open a (possibly gzipped) folded profile for reading. - - Regardless if the file is compressed or not, the file is opened in a text mode and can be read - line by line in a streaming manner. - - :param filepath: a path to the folded profile - :return: the file handle - """ - open_func = ( - streams.safely_open_and_log_gz - if filepath.suffix.lower() == ".gz" - else streams.safely_open_and_log - ) - - # DO NOT simplify the mode to "r": the gzip library interprets "r" as a binary mode. - with open_func(filepath, "rt", fatal_fail=True, encoding="utf-8") as folded_handle: - yield folded_handle - - -def folded_profiles_to_polars_pair_profile( - baseline_folded_profiles: Sequence[Path], - target_folded_profiles: Sequence[Path], - parse_params: ParseParameters, -) -> PolarsTraceProfilePair: - """Parse baseline and target folded profiles into the polars pair profile. - - Prefer this function to constructing the pair profile directly: this function makes sure that - all auxiliary data, e.g., profile features, function maps, or the common traces and functions, - are computed correctly. - - :param baseline_folded_profiles: a collection of baseline profiles to aggregate into a single - baseline trace profile - :param target_folded_profiles: a collection of target profiles to aggregate into a single - target trace profile - :param parse_params: the parsing configuration and parameters - - :return: a polars trace profile pair - """ - # Parse the folded profiles into aggregated baseline and target LazyFrames. - func_maps = FunctionMaps() - baseline_lf, baseline_features = _load_folded_profiles_to_polars_lf( - baseline_folded_profiles, parse_params, func_maps - ) - target_lf, target_features = _load_folded_profiles_to_polars_lf( - target_folded_profiles, parse_params, func_maps - ) - func_maps.finalize() - # Note that only some of the baseline and target features are computed yet, as computing them - # eagerly during the parsing is too expensive. Instead, we can compute them more efficiently - # later on. - - # We need to reuse the parsed profiles in a lot of computations, hence we use the collect_all - # to avoid repeated materialization of the same LazyFrames, and to optimize and parallelize the - # computations as much as possible. - baseline_df, target_df, common_traces, common_funcs, *features = pl.collect_all( - [ - # Materialize the baseline trace profile. - baseline_lf, - # Materialize the target trace profile. - target_lf, - # Find traces that appear in both profiles; this includes traces with 0 exclusive costs. - baseline_lf.select("trace").join(target_lf.select("trace"), on="trace", how="inner"), - # Find functions that appear in both profiles; this includes functions with 0 exclusive - # costs. - baseline_lf.select("func") - .unique() - .join(target_lf.select("func").unique(), on="func", how="inner"), - # Compute the number of unique traces seen in the baseline profile. - baseline_lf.select("trace").count(), - # Compute the number of unique functions seen in the baseline profile. - baseline_lf.select("func").unique().count(), - # Compute the number of unique baseline traces that have non-zero exclusive cost. - baseline_lf.filter(pl.col("exclusive") != 0).select("trace").count(), - # Compute the number of unique baseline functions that have non-zero exclusive cost. - baseline_lf.filter(pl.col("exclusive") != 0).select("func").unique().count(), - # Compute the number of unique traces seen in the target profile. - target_lf.select("trace").count(), - # Compute the number of unique functions seen in the target profile. - target_lf.select("func").unique().count(), - # Compute the number of unique target traces that have non-zero exclusive cost. - target_lf.filter(pl.col("exclusive") != 0).select("trace").count(), - # Compute the number of unique target functions that have non-zero exclusive cost. - target_lf.filter(pl.col("exclusive") != 0).select("func").unique().count(), - ] - ) - if baseline_df.height == 0: - log.error("Baseline profile is empty. Terminating.") - elif target_df.height == 0: - log.error("Target profile is empty. Terminating.") - - # Update the remaining profile features now that it is cheap. - _update_profile_features( - baseline_features, - baseline_df, - len(baseline_folded_profiles), - parse_params.aggregation_func, - *features[:4], - ) - _update_profile_features( - target_features, - target_df, - len(target_folded_profiles), - parse_params.aggregation_func, - *features[4:], - ) - return PolarsTraceProfilePair( - PolarsTraceProfile(baseline_df, baseline_features, func_maps), - PolarsTraceProfile(target_df, target_features, func_maps), - common_traces, - common_funcs, - ) - - -def polars_traces_to_folded_profile( - trace_profile: PolarsTraceProfile, - folded_file: TextIO | TempTextIO, - min_width_threshold: float = 0.0, -) -> int: - """Store a Polars trace profile into a folded profile. - - As a by-product, we also compute the maximum length of traces that will be rendered in - flamegraphs of this profile given the input width threshold. - - :param trace_profile: a Polars trace profile - :param folded_file: a handle to the output file - :param min_width_threshold: the rendering threshold for flamegraphs - - :return: the maximum trace length w.r.t. the filtering threshold - """ - # Optimize dot operator access - str_split = str.split - str_join = str.join - func_id_reverse_map = trace_profile.maps.func_id_reverse_map - - max_filtered_len: int = 0 - - # Only keep the relevant columns - for trace_str, exclusive, inclusive in trace_profile.profile.select( - ["trace", "exclusive", "inclusive"] - ).iter_rows(): - trace_parts = str_split(trace_str, ";") - if exclusive: - # Translate compact trace format to the verbose one, e.g., "1;2;10" -> "main;foo;bar" - translated = str_join( - ";", (func_id_reverse_map[int(func_id)] for func_id in trace_parts) - ) - folded_file.write(f"{translated} {exclusive}\n") - # Update the maximum trace length - if inclusive >= min_width_threshold: - max_filtered_len = max(max_filtered_len, len(trace_parts)) - folded_file.flush() - return max_filtered_len - - -def merge_and_filter_polars_profiles( - baseline: PolarsTraceProfile, target: PolarsTraceProfile, filter_params: FilterParameters -) -> PolarsMergedTraceProfiles: - """Merge two Polars trace profiles into per-trace and per-function Polars merged profiles. - - During the merge process, we also filter out functions and traces that have 0 exclusive cost - and do not meet thresholds for inclusive resource consumption specified in the filtering - parameters. - - :param baseline: the baseline trace profile - :param target: the target trace profile - :param filter_params: filtering parameters - - :return: merged per-trace and per-function Polars profiles - """ - baseline_total = baseline.features.total_resources - target_total = target.features.total_resources - # Compute the function and trace thresholds - base_traces_threshold = filter_params.traces_threshold * baseline_total - tar_traces_threshold = filter_params.traces_threshold * target_total - base_func_threshold = filter_params.function_threshold * baseline_total - tar_func_threshold = filter_params.function_threshold * target_total - - # Merge the baseline and target DataFrames such that each unique trace has exactly one row. - merged_traces: pl.LazyFrame = ( - baseline.profile.lazy() - .filter(pl.col("exclusive") != 0) - .join( - target.profile.lazy().filter(pl.col("exclusive") != 0), - on=("func", "trace"), - how="full", - coalesce=True, - suffix="_target", - ) - # Traces that were measured only in baseline or target will have 0 exclusive and inclusive - # resource consumption - .fill_null(0) - ) - - # Polars expressions for computing the proportional inclusive and exclusive diffs. - proportional_diff_expressions: tuple[pl.Expr, pl.Expr] = ( - ((pl.col("inclusive_target") / target_total - pl.col("inclusive") / baseline_total) * 100) - .cast(pl.Float32) - .alias("prop_diff_incl"), - ((pl.col("exclusive_target") / target_total - pl.col("exclusive") / baseline_total) * 100) - .cast(pl.Float32) - .alias("prop_diff_excl"), - ) - - # Create the per-trace merged profile. - filtered_merged_funcs: pl.LazyFrame = ( - merged_traces.select(pl.exclude("trace")) - .group_by("func") - .sum() - # Remove functions that consume too few resources and are likely uninteresting. - .filter( - (pl.col("inclusive") >= base_func_threshold) - | (pl.col("inclusive_target") >= tar_func_threshold) - ) - # Compute the proportional diffs. - .with_columns(*proportional_diff_expressions) - ) - - filtered_merged_traces: pl.LazyFrame = ( - # Keep only traces of functions that made it to the per-function merged profile after - # filtering out the cheap functions. - merged_traces.join(filtered_merged_funcs, on="func", how="semi") - # Additionally remove traces that consume too few resources and are likely uninteresting. - .filter( - (pl.col("inclusive") >= base_traces_threshold) - | (pl.col("inclusive_target") >= tar_traces_threshold) - ) - # Compute the proportional diffs. - .with_columns(*proportional_diff_expressions) - ) - - return PolarsMergedTraceProfiles( - *pl.collect_all([filtered_merged_traces, filtered_merged_funcs]) - ) - - -def find_top_diffs( - data_lf: pl.LazyFrame, count: int, symbol_key: str, diff_key: str, *, top: bool = True -) -> pl.LazyFrame: - """Find and format the top or bottom diffs w.r.t. the diff_key metric. - - The resulting frame will contain up to records maximal (resp. minimal) w.r.t. the - . The schema of the resulting frame will have only two columns: "symbol" and "diff" - corresponding to the and . - - :param data_lf: the data where to search for the maximal/minimal records. - :param count: the upper limit on the number of top records to search for. - :param symbol_key: the name of the column with symbols (e.g., "trace") - :param diff_key: the name of the column containing the diff metric - :param top: specifies whether to search for the maximal or minimal records. - - :return: a lazy frame containing the maximal/minimal records. - """ - search_func = pl.LazyFrame.top_k if top else pl.LazyFrame.bottom_k - - return ( - search_func(data_lf, count, by=diff_key) - .select([symbol_key, diff_key]) - .rename({symbol_key: "symbol", diff_key: "diff"}) - ) - - -def compute_top_diffs( - merged_profile: pl.LazyFrame, - common_symbols: pl.LazyFrame, - symbol_key: str, - inclusive_diff_key: str, - exclusive_diff_key: str, - records_num: int, -) -> tuple[KeyDiffs, KeyDiffs]: - """Compute the most significant inclusive and exclusive differences within a profile. - - The differences are computed for both inclusive and exclusive consumption metrics of a merged - profile. - - For example, we might want to compute top 10 baseline-only, target-only, and common traces - (or functions) that show the highest increase or decrease in the 'prop_diff_incl' and - 'prop_diff_excl' columns. - - :param merged_profile: a merged profile; it must contain the , - , and columns - :param common_symbols: a column of symbols (e.g., functions or traces) that appear in both the - baseline and target profiles - :param symbol_key: the name of the column to use for partitioning the profile into - baseline-only, target-only, and common parts - :param inclusive_diff_key: the name of the column containing inclusive consumption metric - :param exclusive_diff_key: the name of the column containing exclusive consumption metric - :param records_num: the number of top difference records to compute - - :return: a pair of inclusive and exclusive top differences - """ - # Partition the merged profile into baseline-only, target-only, and common parts based on - # the symbol_key and the set of common symbols. - common_lf = merged_profile.join(common_symbols, on=symbol_key, how="semi") - base_only_lf = merged_profile.join(common_symbols, on=symbol_key, how="anti").filter( - pl.col("inclusive_target") == 0 - ) - target_only_lf = merged_profile.join(common_symbols, on=symbol_key, how="anti").filter( - pl.col("inclusive") == 0 - ) - # We use the collect_all method to optimize the computation: all top_k and bottom_k calls - # share the same lazy frames and collect_all is able to optimize across all those computations. - dfs = pl.collect_all( - [ - # Inclusive diff metric. - find_top_diffs(base_only_lf, records_num, symbol_key, inclusive_diff_key), - find_top_diffs(base_only_lf, records_num, symbol_key, inclusive_diff_key, top=False), - find_top_diffs(target_only_lf, records_num, symbol_key, inclusive_diff_key), - find_top_diffs(target_only_lf, records_num, symbol_key, inclusive_diff_key, top=False), - find_top_diffs(common_lf, records_num, symbol_key, inclusive_diff_key), - find_top_diffs(common_lf, records_num, symbol_key, inclusive_diff_key, top=False), - # Exclusive diff metric. - find_top_diffs(base_only_lf, records_num, symbol_key, exclusive_diff_key), - find_top_diffs(base_only_lf, records_num, symbol_key, exclusive_diff_key, top=False), - find_top_diffs(target_only_lf, records_num, symbol_key, exclusive_diff_key), - find_top_diffs(target_only_lf, records_num, symbol_key, exclusive_diff_key, top=False), - find_top_diffs(common_lf, records_num, symbol_key, exclusive_diff_key), - find_top_diffs(common_lf, records_num, symbol_key, exclusive_diff_key, top=False), - ] - ) - return KeyDiffs(*dfs[:6]), KeyDiffs(*dfs[6:]) - - -def polars_merged_to_tabular_profiles( - merged: PolarsMergedTraceProfiles, max_traces_per_func: int -) -> PolarsTabularTraceProfiles: - """Transform Polars merged profiles to a format suitable for trace table. - - The trace table profile will have only up to top 'max_traces_per_func' traces per each function - w.r.t. the inclusive proportional diff metric. Furthermore, both profiles will be sorted by the - function IDs in the ascending order. - - Both DataFrames will contain the 'func', 'inclusive', 'exclusive', 'inclusive_target', - 'exclusive_target', 'prop_diff_incl', 'prop_diff_excl', 'abs_diff_incl', 'abs_diff_excl', - 'rel_diff_incl', and 'rel_diff_excl' columns. The traces profile will additionally contain the - 'trace' column. See the module docstring for description of the difference metrics. - - Both function and trace table profiles will have additional 'abs_diff_incl', 'abs_diff_excl', - 'rel_diff_incl', and 'rel_diff_excl' diff columns - - :param merged: the Polars merged profiles to transform - :param max_traces_per_func: the maximum number of top traces to keep per function - - :return: a pair of trace and function table profiles - """ - # Polars expressions for computing the absolute and relative diffs for both inclusive and - # exclusive consumption. - abs_rel_diff_expressions: tuple[pl.Expr, pl.Expr, pl.Expr, pl.Expr] = ( - # Absolute inclusive diff. - (pl.col("inclusive_target") - pl.col("inclusive")).alias("abs_diff_incl").cast(pl.Int64), - # Absolute exclusive diff. - (pl.col("exclusive_target") - pl.col("exclusive")).alias("abs_diff_excl").cast(pl.Int64), - # Relative inclusive diff. - ( - (pl.col("inclusive_target") - pl.col("inclusive")) - / pl.max_horizontal("inclusive", "inclusive_target") - * 100 - ) - .cast(pl.Float32) - .alias("rel_diff_incl"), - # Relative inclusive diff. - ( - (pl.col("exclusive_target") - pl.col("exclusive")) - / pl.max_horizontal("exclusive", "exclusive_target") - * 100 - ) - .cast(pl.Float32) - .alias("rel_diff_excl"), - ) - - # Build merged func profile with additional absolute and relative diff metrics, and sort the - # DataFrame rows from the lowest function IDs to the highest ones. - table_funcs_lf = ( - merged.funcs.lazy().with_columns(*abs_rel_diff_expressions).sort("func", descending=False) - ) - - # Build merged trace profile with additional absolute and relative diff metrics, where each - # function keeps only up to top 'max_traces_per_func' traces w.r.t. both inclusive and - # exclusive proportional diff metric. - # TODO: calculate the top traces also for other metrics, e.g., absolute delta or relative - # delta. This will, however, require some hook on sort operation in our trace tables to - # always retrieve the correct set of functions. - top_incl_traces = ( - # This groups traces on a per-function basis and then filters up to top N rows (traces) - # for each function w.r.t. the inclusive proportional delta. - merged.traces.lazy() - .group_by("func") - .agg( - pl.struct(pl.exclude("func")) - .top_k_by("prop_diff_incl", k=max_traces_per_func) - .alias("rows") - ) - .explode("rows") - .unnest("rows") - ) - - top_excl_traces = ( - # Similar to the previous step, but filters based on th exclusive proportional delta. - merged.traces.lazy() - .group_by("func") - .agg( - pl.struct(pl.exclude("func")) - .top_k_by("prop_diff_excl", k=max_traces_per_func) - .alias("rows") - ) - .explode("rows") - .unnest("rows") - ) - - # Concatenate and deduplicate both tables. For example, if `max_traces_per_func` = 10 and some - # function has a total of 15 traces, but only 13 of them appear in both top selections, the - # resulting table will keep only the 13 traces instead of 20 (7 of which would be duplicated). - table_traces_lf = ( - pl.concat([top_incl_traces, top_excl_traces]) - .unique(subset=["func", "trace"]) - .with_columns(*abs_rel_diff_expressions) - .sort("func", descending=False) - ) - - return PolarsTabularTraceProfiles(*pl.collect_all([table_traces_lf, table_funcs_lf])) - - -def iterate_top_diffs(top_diff: KeyDiffs) -> list[Iterator[tuple[str, float]]]: - """Iterates over the top difference data frames and generates records for the report overview. - - :param top_diff: the top difference data frames - - :return: a generator of the overview table records - """ - diff_iterators: list[Iterator[tuple[str, float]]] = [] - for diff_data in [ - top_diff.baseline_top_dec, - top_diff.target_top_inc, - top_diff.common_top_inc, - top_diff.common_top_dec, - ]: - diff_iterators.append( - ((symbol, diff_metric) for symbol, diff_metric in diff_data.iter_rows()) - ) - return diff_iterators - - -def iterate_polars_tabular( - tabular_profile: PolarsTabularTraceProfiles, - common_functions: pl.DataFrame, - func_id_reverse_map: Mapping[int, str], - resource_type: str, -) -> Iterator[report_native.SelectionRow]: - """Iterates over the tabular profiles and generates records for the report table. - - :param tabular_profile: the polars tabular profiles - :param common_functions: the set of function IDs common to both baseline and target profiles - :param func_id_reverse_map: a function ID -> function name mapping - :param resource_type: the resource type of the profiles; this should contain both the resource - name, e.g., 'CPU Cycles' and the unit, e.g., '[#]' - - :return: a generator of the report table records - """ - # Optimize dot operator access where it matters - str_split = str.split - str_rsplit = str.rsplit - str_count = str.count - tabular_funcs = tabular_profile.funcs - df_row = pl.DataFrame.row - df_iter_rows = pl.DataFrame.iter_rows - # FIXME: Remove the dependency on SelectRow. We should ideally pass the records as tuples - # directly to improve performance. - SelectRow = report_native.SelectionRow - - common_funcs: set[int] = set(common_functions["func"].to_list()) - - inclusive_resource_type = f"Inclusive {resource_type}" - # FIXME: Temporary hack so that we can use the SelectionRow class. - report_native.Stats.SortedStats = [inclusive_resource_type, f"Exclusive {resource_type}"] - group_df: pl.DataFrame - # Iterate over groups of traces belonging to individual functions from the lowest to the - # highest function IDs. - for idx, (_, group_df) in enumerate( - tabular_profile.traces.group_by("func", maintain_order=True) - ): - # Obtain a row corresponding to the function from the function profile. We can access it - # using an index thanks to the profile being sorted. - row = df_row(tabular_funcs, idx) - # Determine the state of the function. If it is not in the set of common functions, then we - # look at the 'inclusive' and 'inclusive_target' values to determine wheter it is - # a baseline-only or target-only function. - state: Literal["not in baseline", "not in target", "in both"] = "in both" - # row[0]: Function ID - if row[0] not in common_funcs: - # row[1]: inclusive - if row[1] == 0: - state = "not in baseline" - # row[3]: inclusive_target - elif row[3] == 0: - state = "not in target" - - # Format the top traces according to the SelectionRow requirements. - top_traces = [ - ( - # Short trace: 'firstID;lastID' - f"{str_split(r[1], ';', maxsplit=1)[0]};{str_rsplit(r[1], ';', maxsplit=1)[-1]}", - # Resource type - inclusive_resource_type, - # inclusive - r[2], - # exclusive - r[3], - # inclusive_target - r[4], - # exclusive_target - r[5], - # prop_diff_incl - r[6], - # prop_diff_excl - r[7], - # abs_diff_incl - r[8], - # abs_diff_excl - r[9], - # rel_diff_incl - r[10], - # rel_diff_excl - r[11], - # Full trace: 'firstID;...;lastID#base1;...;baseN#target1;...;targetN' - # FIXME: the part of the trace after the first '#' is not used, but the report - # still expects it. We will remove it later. - f'{r[1]}#0{";0" * str_count(r[1], ";")}#0{";0" * str_count(r[1], ";")}', - ) - for r in df_iter_rows(group_df) - ] - # Build the SelectionRow record. - yield SelectRow( - # Function name - func_id_reverse_map[row[0]], - # Function ID - row[0], - state, - # Inclusive and exclusive function stats: - [ - # resource type index, inclusive (baseline), inclusive_target, - # prop_diff_incl, abs_diff_incl, rel_diff_incl - (0, row[1], row[3], row[5], row[7], row[9]), - # resource type index, exclusive (baseline), exclusive_target, - # prop_diff_excl, abs_diff_excl, rel_diff_excl - (1, row[2], row[4], row[6], row[8], row[10]), - ], - top_traces, - ) - - -def build_flamegraph_command( - input_path: Optional[Path], - settings: FlameGraphSettings, - title: str, - *new_flags: str, - **override_kwargs: Any, -) -> str: - """Create a command to generate a flame graph. - - :param input_path: a path to the file with folded flame graph data; may be omitted in which case - the input data should be supplied to the flamegraph process via stdin - :param settings: flamegraph configuration parameters and flags - :param title: the title of the flame graph - :param new_flags: additional flags that should be passed to the flamegraph script - :param override_kwargs: additional parameters that should extend or override the parameter - values stored in the settings object - - :return: the command for generating a flame graph - """ - cmd = [ - str(settings.fg_script_path), - str(input_path) if input_path is not None else "", - "--title", - f"'{title}'", - ] - # Extend the command with parameters. - cmd.extend(_add_flamegraph_params(settings, *new_flags, **override_kwargs)) - return " ".join(cmd) - - -def build_diff_flamegraph_py_command( - baseline_path: Path, - target_path: Path, - settings: FlameGraphSettings, - title: str, - *new_flags: str, - **override_kwargs: Any, -) -> str: - """Create a command to generate a diff flame graph using our helper script. - - :param baseline_path: a path to the file with baseline folded flame graph data - :param target_path: a path to the file with target folded flame graph data - :param settings: flamegraph configuration parameters and flags - :param title: the title of the flame graph - :param new_flags: additional flags that should be passed to the flamegraph script - :param override_kwargs: additional parameters that should extend or override the parameter - values stored in the settings object - - :return: the command for generating a flame graph - """ - cmd = [ - str(settings.diff_fg_path), - str(baseline_path), - str(target_path), - "--title", - f"'{title}'", - ] - # Extend the command with parameters. - cmd.extend(_add_flamegraph_params(settings, *new_flags, **override_kwargs)) - return " ".join(cmd) - - -def _add_flamegraph_params( - settings: FlameGraphSettings, - *new_flags: str, - **override_kwargs: Any, -) -> list[str]: - """Construct a collection of flags and keyword arguments for a flamegraph script. - - :param settings: flamegraph configuration parameters and flags - :param new_flags: additional flags that should be passed to the flamegraph script - :param override_kwargs: additional parameters that should extend or override the parameter - values stored in the settings object - - :return: a list of flags and parameters - """ - params: list[str] = [] - # Extend the command with flags. - flags: set[str] = set(new_flags) - if settings.inverted and "inverted" not in new_flags: - flags.add("inverted") - params.extend(f"--{flag}" for flag in flags) - - # Extend the command with flamegraph parameters that have non-default values. - # Although we could supply the parameters with default values as well, it would needlessly - # clutter the resulting command. - kw_params: dict[str, str | int] = settings.get_nondefault_kw_attributes() - # The parameters may be overridden and extended by the caller. - kw_params.update(override_kwargs) - if settings.use_perl: - # Sub-root is unsupported by Perl. - del kw_params["subrootnode"] - for key, val in kw_params.items(): - if val is not None: - params.append(f"--{key}") - params.append(f"'{val}'") - return params - - -def build_differential_flamegraph_commands( - baseline: Path, - target: Path, - settings: FlameGraphSettings, - title: str, - *new_flags: str, - **override_kwargs: Any, -) -> tuple[str, str]: - """Create diffing and flamegraph commands to generate a differential flame graph. - - :param baseline: a path to the file with baseline folded flame graph data - :param target: a path to the file with target folded flame graph data - :param settings: flamegraph configuration parameters and flags - :param title: the title of the flame graph - :param new_flags: additional flags that should be passed to the flamegraph script - :param override_kwargs: additional parameters that should extend or override the parameter - values stored in the settings object - - :return: the difffolded and flamegraph commands for generating a differential flame graph - """ - if settings.use_perl: - normalize_flag = "" - if settings.normalize or "normalize" in new_flags: - normalize_flag = "-n" - diff_cmd = f"{settings.difffolded_path} {normalize_flag} {baseline} {target}" - fg_cmd = build_flamegraph_command(None, settings, title, *new_flags, **override_kwargs) - else: - diff_cmd = "" - extended_flags = list(new_flags) - if settings.normalize and "normalize" not in new_flags: - extended_flags.append("normalize") - fg_cmd = build_diff_flamegraph_py_command( - baseline, target, settings, title, *extended_flags, **override_kwargs - ) - return diff_cmd, fg_cmd - - -def build_flamegraph_grid_commands( - baseline: Path, - target: Path, - settings: FlameGraphSettings, - *new_flags: str, - titles: tuple[str, str, str, str] = FlameGraphGrid.DefaultTitles, - **override_kwargs: Any, -) -> FlameGraphGridCommands: - """Create a collection of commands for generating a flamegraph grid. - - :param baseline: a path to the file with baseline folded flame graph data - :param target: a path to the file with target folded flame graph data - :param settings: flamegraph configuration parameters and flags - :param new_flags: additional flags that should be passed to the flamegraph script - :param titles: the titles of the respective flamegraphs - :param override_kwargs: additional parameters that should extend or override the parameter - values stored in the settings object - - :return: the flamegraph commands for generating a flamegraph grid - """ - return FlameGraphGridCommands( - build_flamegraph_command(baseline, settings, titles[0], *new_flags, **override_kwargs), - build_flamegraph_command(target, settings, titles[1], *new_flags, **override_kwargs), - *build_differential_flamegraph_commands( - target, baseline, settings, titles[2], "negate", *new_flags, **override_kwargs - ), - *build_differential_flamegraph_commands( - baseline, target, settings, titles[3], *new_flags, **override_kwargs - ), - ) - - -def build_flamegraph_grid_serially(grid_cmds: FlameGraphGridCommands) -> FlameGraphGrid: - """Create a flamegraph grid serially in this process. - - :param grid_cmds: the commands for generating a flamegraph grid - - :return: the generated flamegraph grid - """ - log.major_info("Creating Flame Graph Grid (Serially)") - - # Transform the commands into a tuple that we can index in a loop. - if grid_cmds.baseline_target_difffolded: - # Perl variant. - cmds: tuple[str, str, str, str] = ( - grid_cmds.baseline, - grid_cmds.target, - f"{grid_cmds.baseline_target_difffolded} | {grid_cmds.baseline_target_fg_diff}", - f"{grid_cmds.target_baseline_difffolded} | {grid_cmds.target_baseline_fg_diff}", - ) - else: - # Python variant. - cmds = ( - grid_cmds.baseline, - grid_cmds.target, - grid_cmds.baseline_target_fg_diff, - grid_cmds.target_baseline_fg_diff, - ) - - grid: FlameGraphGrid = FlameGraphGrid() - for idx, (fg_cmd, tag) in enumerate(zip(cmds, FlameGraphGrid.EscapeTags)): - # Execute the command and escape the resulting flamegraph. - grid[idx] = flamegraph.escape_content( - tag, commands.run_safely_external_command(fg_cmd)[0].decode("utf-8") - ) - log.minor_success(FlameGraphGrid.DefaultTitles[idx], "generated") - - log.minor_success("Flamegraph grid", "completed") - return grid - - -@contextlib.contextmanager -def build_flamegraph_grid( - grid: FlameGraphGrid, - grid_commands: FlameGraphGridCommands, - parallelize: bool, -) -> Iterator[FlameGraphGridBuilder]: - """Create a context manager for flamegraph grid serial or parallel builder. - - The context manager hides the specifics of building a flamegraph grid serially in a single - process or in several parallel background processes. - - :param grid: [out] a reference to the grid object for the generated flamegraphs; context - managers cannot explicitly return values, hence we use an output parameter - :param grid_commands: the commands for generating a flamegraph grid - :param parallelize: determines whether to build the grid serially or in parallel - - :return: the context manager wrapping the grid builder - """ - creator = FlameGraphGridParallelBuilder if parallelize else FlameGraphGridSerialBuilder - with creator(grid, grid_commands) as builder: - yield builder - - -def generate_specification( - machine_info: dict[str, Any], - exitcodes: str | list[str] | list[int], - collector_command: str, - command: str, - label: str, -) -> list[profile.ProfileHeaderEntry]: - """Generate profile header specification. - - :param machine_info: the machine specification - :param exitcodes: possibly a collection of exit codes of profiling processes for each input - profile - :param collector_command: the name of the profiler that collected the profiles - :param command: the profiled command and its input parameters - :param label: a user-defined profile label - - :return: a collection of profile header entries corresponding to the profile specification - """ - return [ - profile.ProfileHeaderEntry( - "profile label", - label if label else "?", - "A label associated with this profile, if any.", - ), - profile.ProfileHeaderEntry( - "command", - command if command else "?", - "The profiled command and its input parameters.", - ), - profile.ProfileHeaderEntry( - "exitcode", - diff_kit.format_exit_codes(exitcodes), - "The exit code(s) that were returned by the profiling processes of each input profile.", - ), - profile.ProfileHeaderEntry( - "collector command", - collector_command, - "The collector / profiler, which collected the data.", - ), - profile.ProfileHeaderEntry( - "kernel", - machine_info.get("release", "?"), - "The underlying kernel version, where the results were measured.", - ), - profile.ProfileHeaderEntry( - "boot info", - machine_info.get("boot_info", "?"), - "The contents of `/proc/cmdline` containing boot information about kernel", - ), - profile.ProfileHeaderEntry( - "host", machine_info["host"], "The hostname, where the results were measured." - ), - profile.ProfileHeaderEntry( - "cpu (total)", - machine_info.get("cpu", {"total": "?"}).get("total", "?"), - "The total number (physical and virtual) of CPUs available on the host.", - ), - profile.ProfileHeaderEntry( - "memory (total)", - machine_info.get("memory", {"total_ram": "?"}).get("total_ram", "?"), - "The total number of RAM available on the host.", - ), - ] - - -def generate_vulnerabilities( - vulnerabilities: dict[str, str | float], -) -> list[profile.ProfileHeaderEntry]: - """Generate vulnerabilities profile header from the machine info sub-dictionary. - - :param vulnerabilities: the machine info sub-dictionary containing CPU vulnerabilities - specification - - :return: CPU vulnerabilities as a profile header entry - """ - return [ - profile.ProfileHeaderEntry( - "vulnerabilities", - "?" if not vulnerabilities else "", - "CPU vulnerabilities summary.", - vulnerabilities, - ) - ] - - -def features_to_stats(features: ProfileFeatures, resource_name: str) -> list[profile.ProfileStat]: - """Transform profile features into profile stats. - - :param features: profile features - :param resource_name: profile resource name - - :return: a collection of profile stats derived from the features - """ - return [ - profile.ProfileStat( - f"Total {resource_name}", - profile.ProfileStatComparison.LOWER, - "#", - description=f"The total amount of {resource_name} accounted for in the profile.", - value=[features.total_resources], - ), - profile.ProfileStat( - "Unique Functions", - profile.ProfileStatComparison.LOWER, - "#", - description="The number of unique function symbols seen in the profile. This includes " - "functions that have no measured exclusive resource consumption but were " - "seen in traces.", - value=[features.seen_functions_count], - ), - profile.ProfileStat( - "Unique Traces", - profile.ProfileStatComparison.LOWER, - "#", - description="The number of unique traces seen in the profile. This includes traces " - "that have no measured exclusive resource consumption but were seen in the " - "profile.", - value=[features.seen_traces_count], - ), - profile.ProfileStat( - "Measured Functions", - profile.ProfileStatComparison.LOWER, - "#", - description="The number of unique function symbols that have exclusive resource " - "consumption recorded in the profile.", - value=[features.measured_functions_count], - ), - profile.ProfileStat( - "Measured Traces", - profile.ProfileStatComparison.LOWER, - "#", - description="The number of unique traces that have exclusive resource consumption " - "recorded in the profile.", - value=[features.measured_traces_count], - ), - profile.ProfileStat( - "Longest Profile Trace", - profile.ProfileStatComparison.LOWER, - "#", - description="The longest trace recorded in the profile.", - value=[features.max_trace_len], - ), - ] - - -def _load_folded_profiles_to_polars_lf( - folded_profiles: Sequence[Path], parse_params: ParseParameters, maps: FunctionMaps -) -> tuple[pl.LazyFrame, ProfileFeatures]: - """Load folded profile(s) into a Polars LazyFrame according to the parse parameters. - - If multiple folded profiles are given, we aggregate them into a single LazyFrame according to - the aggregation function specified in the parse parameters. - - Note that the returned profile features contain only those features that can be computed - cheaply during the parsing. - - :param folded_profiles: possibly a collection of folded profile paths - :param parse_params: parsing parameters and configuration - :param maps: function maps - - :return: a (possibly aggregated) LazyFrame and incomplete profile features - """ - features: ProfileFeatures = ProfileFeatures() - # Check whether we should squash recursion and select the parse function accordingly. - parse_func = _parse_folded_squash if parse_params.squash_recursion else _parse_folded_no_squash - parsed_traces: list[pl.LazyFrame] = [ - parse_func(profile_path, maps, parse_params, features) for profile_path in folded_profiles - ] - - if len(parsed_traces) == 1: - # No need to aggregate data for a single input profile. - return parsed_traces[0], features - # We have parsed multiple profiles. Hence, each trace might have more than 1 value that we - # need to aggregate. We do that by concatenating the profiles, grouping the same traces and - # then aggregating the values. - return ( - pl.concat(parsed_traces) - .group_by("func", "trace") - .agg( - [ - parse_params.aggregation_func("inclusive").cast(pl.Int64), - parse_params.aggregation_func("exclusive").cast(pl.Int64), - ] - ), - features, - ) - - -def _parse_folded_no_squash( - filepath: Path, maps: FunctionMaps, params: ParseParameters, profile_features: ProfileFeatures -) -> pl.LazyFrame: - """Parse a folded profile into a polars frame without squashing recursive calls. - - We implement two separate parsing functions for squash/no-squash variants. The no-squash - variant is simpler, and we want to keep it as fast as possible. - - :param filepath: a path to the folded profile - :param maps: function name and ID maps - :param params: parsing parameters - :param profile_features: profile features that describe the parsed profile - - :return: a parsed profile lazyframe with 'func', 'trace', 'inclusive', and 'exclusive' columns - """ - # trace -> inclusive resource consumption. - inclusive: defaultdict[str, int] = defaultdict(int) - # trace -> exclusive resource consumption. - exclusive: defaultdict[str, int] = defaultdict(int) - max_trace_len = 0 - total = 0 - - # Optimize dot operator access. - str_split = str.split - str_rsplit = str.rsplit - hide_generics_func = common_kit.hide_generics - func_id_map = maps.func_id_map - hide_generics = params.hide_generics - - with open_folded_profile(filepath) as folded_handle: - for record in folded_handle: - # Parse the line, obtain the performance metric and the trace as individual frames. - frames: list[str] = str_split(record, ";") - frames[-1], data = str_rsplit(frames[-1], " ", maxsplit=1) - max_trace_len = max(max_trace_len, len(frames)) - data_int = int(data) - total += data_int - - # Process the first frame of the trace. - trace = str(func_id_map[frames[0]]) - inclusive[trace] += data_int - # Process the rest of the trace. - for frame in frames[1:]: - if hide_generics: - frame = hide_generics_func(frame) - trace += f";{func_id_map[frame]}" - inclusive[trace] += data_int - exclusive[trace] += data_int - log.minor_success(log.path_style(str(filepath)), "parsed") - # Update the profile features. - profile_features.total_resources += total - profile_features.max_trace_len = max(max_trace_len, profile_features.max_trace_len) - return _build_trace_profile(inclusive, exclusive, None) - - -def _parse_folded_squash( - filepath: Path, maps: FunctionMaps, params: ParseParameters, profile_features: ProfileFeatures -) -> pl.LazyFrame: - """Parse a folded profile into a polars frame while squashing recursive calls. - - We implement two separate parsing functions for squash/no-squash variants. The squash variant - is more complicated and expensive, so we keep it separately to not slow down the no-squash - implementation. - - :param filepath: a path to the folded profile - :param maps: a collection of parsing maps - :param params: parsing parameters - :param profile_features: profile features that describe the parsed profile - - :return: a parsed profile lazyframe with 'func', 'trace', 'inclusive', and 'exclusive' columns - """ - # trace -> inclusive resource consumption. - inclusive: defaultdict[str, int] = defaultdict(int) - # trace -> exclusive resource consumption. - exclusive: defaultdict[str, int] = defaultdict(int) - max_trace_len = 0 - total = 0 - - # Optimize dot operator access. - re_search = re.search - str_split = str.split - str_rsplit = str.rsplit - hide_generics_func = common_kit.hide_generics - func_id_map = maps.func_id_map - squashed_id_map = maps.squashed_id_map - squash_pattern = params.squash_pattern - hide_generics = params.hide_generics - - with open_folded_profile(filepath) as folded_handle: - for record in folded_handle: - # Parse the line, obtain the performance metric and the trace as individual frames. - frames: list[str] = str_split(record, ";") - frames[-1], data = str_rsplit(frames[-1], " ", maxsplit=1) - max_trace_len = max(max_trace_len, len(frames)) - data_int = int(data) - total += data_int - - # The algorithm does not immediately write the processed frame into the compact trace - # string. Instead, it remembers the last processed frame and either writes it in the - # next step, or merges it with the successor if squash conditions are satisfied. - last_frame = func_id_map[frames[0]] - trace: str = "" - recursive_count = 1 - for idx, frame in enumerate(frames[1:]): - if hide_generics: - frame = hide_generics_func(frame) - func_id = func_id_map[frame] - - # We want to merge the frames if they represent the same function, and they match - # the squash pattern. Regex matching is done only once for each sequence of - # identical frames, and only for non-default patterns (default pattern matches - # everything). - if func_id == last_frame and ( - squash_pattern is None - or (recursive_count > 1 or re_search(squash_pattern, frame)) - ): - # This frame is part of a recursive call chain. - recursive_count += 1 - elif recursive_count == 1: - # The previous frame was not part of a recursive call chain; write it. - if trace: - trace += f";{last_frame}" - else: - trace = str(last_frame) - inclusive[trace] += data_int - last_frame = func_id - else: - # A recursive call chain has ended. We create a new ID for the squashed - # function with the number of recursive calls. - squashed_id = func_id_map[f"{frames[idx]}{{x{recursive_count}}}"] - trace += f";{squashed_id}" - squashed_id_map[squashed_id] = last_frame - inclusive[trace] += data_int - last_frame = func_id - recursive_count = 1 - # We must still process the last frame. - if recursive_count > 1: - squashed_id = func_id_map[f"{frames[-1]}{{x{recursive_count}}}"] - trace += f";{squashed_id}" - squashed_id_map[squashed_id] = last_frame - elif trace: - trace += f";{last_frame}" - else: - trace = str(last_frame) - inclusive[trace] += data_int - exclusive[trace] += data_int - log.minor_success(log.path_style(str(filepath)), "parsed") - # Update the profile features. - profile_features.total_resources += total - profile_features.max_trace_len = max(max_trace_len, profile_features.max_trace_len) - return _build_trace_profile(inclusive, exclusive, squashed_id_map) - - -def _build_trace_profile( - inclusive: defaultdict[str, int], - exclusive: defaultdict[str, int], - squashed_id_map: Optional[Mapping[int, int]], -) -> pl.LazyFrame: - """Build a per-trace profile from inclusive and exclusive resource consumption records. - - No filtering takes place at this point; although we could, e.g., filter records with no - exclusive consumption, we would not be able to correctly compute some profile features or lose - some precision when detecting common functions to baseline and target profiles. - - Both the and maps should have keys in the form of 'id1;id2;id3;...' - which represent traces in a compact format where id1, id2, and id3 correspond to function IDs. - - If recursion squashing took place, a should be provided as well. - - :param inclusive: a mapping of trace -> inclusive resource consumption - :param exclusive: a mapping of trace -> inclusive resource consumption - :param squashed_id_map: a mapping of squashed function ID -> function ID; if not provided, - we assume no squashing was done - - :return: a per-trace profile with 'func', 'trace', 'inclusive', and 'exclusive' columns - """ - return pl.LazyFrame( - { - "func": _generate_func_ids(inclusive.keys(), squashed_id_map), - "trace": inclusive.keys(), - "inclusive": inclusive.values(), - # The .get() method avoids needlessly growing the defaultdict. - "exclusive": (exclusive.get(t, 0) for t in inclusive), - }, - schema={ - "func": pl.UInt32, - "trace": pl.String, - "inclusive": pl.Int64, - "exclusive": pl.Int64, - }, - ) - - -def _generate_func_ids( - traces: KeysView[str], squashed_id_map: Optional[Mapping[int, int]] -) -> Iterator[int]: - """A helper function that extracts the ID of the last function from a possibly squashed trace. - - For squashed traces, the function returns the ID of the non-squashed function symbol. For - example, if the ID of last function in a trace corresponds to the 'foo{x8}' symbol, the - function will return the ID of the original 'foo' symbol. - - :param traces: a set of traces to extract the function ID from - :param squashed_id_map: a mapping of squashed function ID -> function ID; if not provided, - we assume no squashing was done - - :return: a generator over the extracted function IDs - """ - # Optimize dot operator access. - str_rsplit = str.rsplit - - if squashed_id_map is None: - # No squashing was done, simply extract and return the last ID in the trace. - for trace in traces: - yield int(str_rsplit(trace, ";", maxsplit=1)[-1]) - else: - # Some of the traces may have been squashed. - for trace in traces: - last_func_id = int(str_rsplit(trace, ";", maxsplit=1)[-1]) - # Check if the last ID corresponds to a squashed function that needs to be translated. - try: - yield squashed_id_map[last_func_id] - except KeyError: - yield last_func_id - - -def _update_profile_features( - features: ProfileFeatures, - prof: pl.DataFrame, - profiles_count: int, - aggregation_func: PolarsAggregate, - seen_traces: pl.DataFrame, - seen_functions: pl.DataFrame, - measured_traces: pl.DataFrame, - measured_functions: pl.DataFrame, -) -> None: - """Update the incomplete profile features with the missing features. - - The features object is initialized only partially during the parsing. The missing features - should be supplied after they are computed more cheaply after postprocessing the profiles a bit. - - :param features: an incomplete profile features object - :param prof: a profile DataFrame without traces with 0 exclusive cost - :param profiles_count: the number of input profiles that were parsed to create the DataFrame - :param aggregation_func: the aggregation function used to aggregate multiple profiles - :param seen_traces: a 1x1 DataFrame containing the number of all the unique traces that - appeared in the profile - :param seen_functions: a 1x1 DataFrame containing the number of all the unique functions that - appeared in the profile - :param measured_traces: a 1x1 DataFrame containing the number of the unique traces that have - non-zero exclusive cost - :param measured_functions: a 1x1 DataFrame containing the number of the unique functions that - have non-zero exclusive cost - """ - # Obtain the single value in the DataFrames as a scalar integer. - features.seen_traces_count = seen_traces.item() - features.seen_functions_count = seen_functions.item() - # The number of unique traces with non-zero exclusive cost is determined by the height of the - # profile DataFrame. - features.measured_traces_count = measured_traces.item() - features.measured_functions_count = measured_functions.item() - # If we aggregated multiple folded profiles into one, and the values were not simply summed, - # we need to recompute the total number of consumed resources. - if profiles_count > 1 and aggregation_func != pl.sum: - features.total_resources = int(prof["exclusive"].sum()) diff --git a/perun/view_diff/report_native.py b/perun/view_diff/report_native.py deleted file mode 100755 index 7ddb49276..000000000 --- a/perun/view_diff/report_native.py +++ /dev/null @@ -1,891 +0,0 @@ -"""A comprehensive difference report of baseline-target profiles. - -The report includes general overview of the environment (kernel, boot info, machine specs, ...), -a flame graph grid, aggregate table of the most expensive traces, etc. - -The report also supports embedding user-defined annotations within the report sections or concrete -values. -""" - -from __future__ import annotations - -# Standard Imports -import array -from collections import defaultdict -from dataclasses import dataclass -from datetime import datetime, timezone -from operator import itemgetter -from pathlib import Path -import sys -from typing import Any, Literal - -# Third-Party Imports - -# Perun Imports -import perun -from perun.profiles import native as profile -from perun.logic import config -from perun.templates import factory as templates -from perun.utils import log, mapping, streams -from perun.utils.common import diff_kit, common_kit -from perun.utils.structs.common_structs import WebColorPalette -from perun.utils.structs.diff_structs import Config -from perun.view_diff import flamegraph - - -@dataclass -class TraceStat: - """Statistics for single trace - - :ivar trace: list of called UID - :ivar baseline_cost: overall cost of the traces - :ivar target_cost: overall cost of the traces - :ivar baseline_partial_costs: list of partial costs of the trace for baseline; - the partial costs are the particular results for each of the pair of callee - and caller in the trace. - :ivar target_partial_costs: list of partial costs of the trace for target - """ - - __slots__ = [ - "trace", - "trace_id", - "baseline_cost", - "target_cost", - "baseline_partial_costs", - "target_partial_costs", - ] - trace: list[str] - trace_id: int - baseline_cost: array.array[float] - target_cost: array.array[float] - baseline_partial_costs: list[array.array[float]] - target_partial_costs: list[array.array[float]] - - -@dataclass -class SelectionRow: - """Helper dataclass for displaying selection of data - - :ivar uid: uid of the selected graph - :ivar index: index in the sorted list of data - :ivar fresh: the state of the uid - 1) not in baseline (added in target), - 2) not in target (removed in target), or 3) possibly unchanged - :ivar stats: overview of all stat values - :ivar trace_stats: a collection of stats for traces - """ - - __slots__ = [ - "uid", - "index", - "fresh", - "main_stat", - "stats", - "trace_stats", - ] - - def __init__( - self, - uid: str, - index: int, - fresh: Literal["not in baseline", "not in target", "in both"], - stats: list[tuple[int, float, float, float, float, float]], - trace_stats: list[ - tuple[ - str, str, float, float, float, float, float, float, float, float, float, float, str - ] - ], - ) -> None: - """Initializes the selection row""" - self.uid: str = uid - self.index: int = index - self.fresh: Literal["not in baseline", "not in target", "in both"] = fresh - # stat_type, baseline_abs, target_abs, prop_rel_delta, abs_delta, rel_delta - self.stats: list[tuple[int, float, float, float, float, float]] = [ - ( - stat[0], - common_kit.to_compact_num(stat[1]), - common_kit.to_compact_num(stat[2]), - common_kit.to_compact_num(stat[3]), - common_kit.to_compact_num(stat[4]), - common_kit.to_compact_num(stat[5]), - ) - for stat in stats - ] - if len(stats) < 2: - # Add dummy exclusive consumption when unavailable (native report). - self.stats.append((1, 0.0, 0.0, 0.0, 0.0, 0.0)) - all_stats = Stats.all_stats() - # trace, stat_t, baseline_abs_incl, baseline_abs_excl, target_abs_incl, target_abs_excl, - # prop_rel_delta_incl, prop_rel_delta_excl, abs_delta_incl, abs_delta_excl, - # rel_delta_incl, rel_delta_excl, long_trace - self.trace_stats: list[ - tuple[ - str, int, float, float, float, float, float, float, float, float, float, float, str - ] - ] = [ - ( - t[0], - all_stats.index(t[1]), - common_kit.to_compact_num(t[2]), - common_kit.to_compact_num(t[3]), - common_kit.to_compact_num(t[4]), - common_kit.to_compact_num(t[5]), - common_kit.to_compact_num(t[6]), - common_kit.to_compact_num(t[7]), - common_kit.to_compact_num(t[8]), - common_kit.to_compact_num(t[9]), - common_kit.to_compact_num(t[10]), - common_kit.to_compact_num(t[11]), - t[12], - ) - for t in trace_stats - ] - - -class Graph: - """Represents single sankey graph - - :ivar nodes: mapping of uids#pos to concrete sankey nodes - :ivar uid_to_nodes: mapping of uid to list of its uid#pos, i.e. uid in different contexts - :ivar uid_to_id: mapping of uid to unique identifiers - :ivar stats_to_id: mapping of stats to unique identifiers - """ - - __slots__ = ["nodes", "uid_to_nodes", "uid_to_id", "stats_to_id", "uid_to_traces"] - - nodes: dict[str, Node] - uid_to_traces: dict[str, list[list[str]]] - uid_to_nodes: dict[str, list[Node]] - uid_to_id: dict[str, int] - stats_to_id: dict[str, int] - - def __init__(self): - """Initializes empty graph""" - self.nodes = {} - self.uid_to_nodes = defaultdict(list) - self.uid_to_traces = defaultdict(list) - self.stats_to_id = {} - self.uid_to_id = {} - - def get_node(self, node: str) -> Node: - """For give uid#pos returns its corresponding node - - If the uid is not yet assigned unique id, we assign it. - If the node has corresponding node created we return it. - - :param node: node in form of uid#pos - :return: corresponding node in sankey graph - """ - uid = node.split("#")[0] - if uid not in self.uid_to_id: - self.uid_to_id[uid] = len(self.uid_to_id) - if node not in self.nodes: - self.nodes[node] = Node(node) - self.uid_to_nodes[uid].append(self.nodes[node]) - return self.nodes[node] - - def translate_stats(self, stats: str) -> int: - """Translates string representation of stats to unique id - - :param stats: stats represented as string - :return: unique id for the string - """ - if stats not in self.stats_to_id: - self.stats_to_id[stats] = len(self.stats_to_id) - return self.stats_to_id[stats] - - def translate_node(self, node: str) -> str: - """Translates the node to its unique id - - :param node: node which we are translating to id - :return: unique identifier for the node uid - """ - if "#" in node: - return f"{self.uid_to_id[node.split('#')[0]]}" - return str(self.uid_to_id[node]) - - def get_caller_stats(self, src: str, tgt: str) -> Stats: - """Returns stats for callers of the src - - :param src: source node - :param tgt: target caller node - :return: stats - """ - # Create node if it does not already exist - src_node = self.get_node(src) - - # Get link if it does not already exist - if tgt not in src_node.callers: - tgt_node = self.get_node(tgt) - src_node.callers[tgt] = Link(tgt_node, Stats()) - - return src_node.callers[tgt].stats - - def get_callee_stats(self, src: str, tgt: str) -> Stats: - """Returns stats for callees of the src - - :param src: source node - :param tgt: target callee node - :return: stats - """ - # Create node if it does not already exist - src_node = self.get_node(src) - - # Get link if it does not already exist - if tgt not in src_node.callees: - tgt_node = self.get_node(tgt) - src_node.callees[tgt] = Link(tgt_node, Stats()) - - return src_node.callees[tgt].stats - - def to_jinja_string(self, link_type: Literal["callees", "callers"] = "callees") -> str: - """Since jinja seems to be awfully slow with this, we render the result ourselves - - 1. Target nodes of "uid#pos" are simplified to "uid", - since you can infer pos to be pos+1 of source - 2. Stats are merged together: first half is for baseline, second half is for target - - TODO: switch callers to callees and callees to callers - - :param link_type: either callees for callees or caller for callers - :return: string representation of the callee or caller relation - """ - - def comma_control(commas_list: list[bool], pos: int) -> str: - """Helper function for comma control - - :param pos: position in the nesting - :param commas_list: list of boolean flags for comma control (true = we should output) - """ - if commas_list[pos]: - return "," - commas_list[pos] = True - return "" - - output = ["{"] - commas = [False, False, False] - for uid, nodes in log.progress( - self.uid_to_nodes.items(), description="Converting Nodes To Jinja" - ): - output.extend([comma_control(commas, 0), f"{self.translate_node(uid)}:", "{"]) - commas[1] = False - for node in nodes: - output.extend([comma_control(commas, 1), f"{node.get_order()}:", "{"]) - commas[2] = False - for link in node.get_links(link_type).values(): - assert link_type == "callers" or int(node.get_order()) + 1 == int( - link.target.get_order() - ) - assert ( - link_type == "callees" - or int(node.get_order()) == int(link.target.get_order()) + 1 - ) - output.extend( - [comma_control(commas, 2), f"{self.translate_node(link.target.uid)}:"] - ) - base_and_tgt = link.stats.to_array("baseline") + link.stats.to_array("target") - stats = f"[{','.join(base_and_tgt)}]" - output.append(str(self.translate_stats(stats))) - output.append("}") - output.append("}") - output.append("}") - return "".join(output) - - -@dataclass -class Node: - """Single node in sankey graph - - :ivar uid: unique identifier of the node (the label) - :ivar callees: map of positions to edge relation for callees - :ivar callers: map of positions to edge relation for callers - :ivar stats: statistics for the given node - """ - - __slots__ = ["uid", "callees", "callers", "stats"] - - uid: str - callees: dict[str, Link] - callers: dict[str, Link] - stats: Stats - - def __init__(self, uid: str): - """Initializes the node""" - self.uid = uid - self.callees = {} - self.callers = {} - self.stats = Stats() - - def get_links(self, link_type: Literal["callees", "callers"]) -> dict[str, Link]: - """Returns linkage based on given type - - :param link_type: either callees or callers - :return: linkage of the given ty pe - """ - if link_type == "callees": - return self.callees - assert link_type == "callers" - return self.callers - - def get_order(self) -> int: - """Gets position/order in the call traces - - :return: order/position in the call traces - """ - return int(self.uid.split("#")[1]) - - -@dataclass -class Link: - """Helper dataclass for linking two nodes - - :ivar target: target of the edge - :ivar stats: stats of the edge - """ - - __slots__ = ["stats", "target"] - target: Node - stats: Stats - - -class Stats: - """Statistics for a given edge or node - - :ivar baseline: baseline stats - :ivar target: target stats - """ - - __slots__ = ["baseline", "target"] - KnownStatsSet: set[str] = set() - SortedStats: list[str] = [] - - def __init__(self) -> None: - """Initializes the stat""" - self.baseline: dict[str, float] = defaultdict(float) - self.target: dict[str, float] = defaultdict(float) - - def add_stat( - self, stat_type: Literal["baseline", "target"], stat_key: str, stat_val: int | float - ) -> None: - """Adds stat of given type - - :ivar stat_type: type of the stat (either baseline or target) - :ivar stat_key: type of the metric - :ivar stat_val: value of the metric - """ - Stats.KnownStatsSet.add(stat_key) - if stat_type == "baseline": - self.baseline[stat_key] += stat_val - else: - self.target[stat_key] += stat_val - - @staticmethod - def all_stats() -> list[str]: - """Returns sorted list of stats""" - if not Stats.SortedStats: - Stats.SortedStats = sorted(list(Stats.KnownStatsSet)) - return Stats.SortedStats - - def to_array(self, stat_type: Literal["baseline", "target"]) -> list[str]: - """Converts stats to single compact array""" - stats = self.baseline if stat_type == "baseline" else self.target - return [ - common_kit.compact_convert_num_to_str(stats.get(stat, 0), 2) - for stat in Stats.all_stats() - ] - - -def process_max( - counts: dict[str, float], - resource: dict[str, Any], -) -> None: - """Accumulates resources - - :param counts: counts of resources - :param resource: consumed resources - """ - for key in resource: - amount = common_kit.try_convert(resource[key], [float]) - if amount is None or key in ("time", "command", "uid"): - continue - readable_key = mapping.get_readable_key(key) - counts[readable_key] += amount - - -def process_node( - graph: Graph, - profile_type: Literal["baseline", "target"], - resource: dict[str, Any], - uid: str, -) -> None: - """Processes single edge with given resources - - :param graph: sankey graph - :param profile_type: type of the profile - :param resource: consumed resources - :param uid: the node uid - """ - node = graph.get_node(uid) - for key in resource: - amount = common_kit.try_convert(resource[key], [float]) - if amount is None or key in ("time", "command", "uid"): - continue - readable_key = mapping.get_readable_key(key) - node.stats.add_stat(profile_type, readable_key, amount) - - -def process_edge( - graph: Graph, - profile_type: Literal["baseline", "target"], - resource: dict[str, Any], - src: str, - tgt: str, -) -> None: - """Processes single edge with given resources - - :param graph: sankey graph - :param profile_type: type of the profile - :param resource: consumed resources - :param src: callee - :param tgt: caller - """ - src_stats = graph.get_callee_stats(src, tgt) - tgt_stats = graph.get_caller_stats(tgt, src) - for key in resource: - amount = common_kit.try_convert(resource[key], [float]) - if amount is None or key in ("time", "command", "uid"): - continue - readable_key = mapping.get_readable_key(key) - src_stats.add_stat(profile_type, readable_key, amount) - tgt_stats.add_stat(profile_type, readable_key, amount) - - -def process_traces( - prof: profile.Profile, profile_type: Literal["baseline", "target"], graph: Graph -) -> None: - """Processes all traces in the profile - - Iterates through all traces and creates edges for each pair of source and target. - - :param prof: input profile - :param profile_type: type of the profile - :param graph: sankey graph - """ - max_trace = 0 - max_samples: dict[str, float] = defaultdict(float) - for _, resource in log.progress(prof.all_resources(), description="Processing Traces"): - full_trace = [profile.to_uid(t, Config().minimize) for t in resource.get("trace", {})] - full_trace.append(profile.to_uid(resource["uid"], Config().minimize)) - trace_len = len(full_trace) - max_trace = max(max_trace, trace_len) - # Process edge stats - if trace_len > 1: - if Config().trace_is_inclusive: - for i in range(0, trace_len - 1): - src = f"{full_trace[i]}#{i}" - tgt = f"{full_trace[i + 1]}#{i + 1}" - process_edge(graph, profile_type, resource, src, tgt) - else: - src = f"{full_trace[-2]}#{trace_len - 2}" - tgt = f"{full_trace[-1]}#{trace_len - 1}" - process_edge(graph, profile_type, resource, src, tgt) - for uid in full_trace: - graph.uid_to_traces[uid].append(full_trace) - # Process node stats TODO: Return to this, as this was fixed very quickly - if Config().trace_is_inclusive: - process_max(max_samples, resource) - for i in range(0, trace_len): - src = f"{full_trace[i]}#{i}" - process_node(graph, profile_type, resource, src) - else: - tgt = f"{full_trace[-1]}#{trace_len - 1}" - process_node(graph, profile_type, resource, tgt) - - saved_maxima = Config().max_per_resource - for key in max_samples.keys(): - saved_maxima[key] = max(saved_maxima[key], max_samples[key]) - # TODO: This is a bit of a hack since the key already contains the unit - name, unit = key.split("[", maxsplit=1) - name.rstrip() - unit = unit.rsplit("]", maxsplit=1)[0] - Config().profile_stats[profile_type].append( - profile.ProfileStat( - f"Overall {name}", - profile.ProfileStatComparison.LOWER, - unit, - description=f"The overall value of the {name} for the root value", - value=[int(max_samples[key])], - ) - ) - Config().profile_stats[profile_type].append( - profile.ProfileStat( - "Maximum Trace Length", - profile.ProfileStatComparison.LOWER, - "#", - description="Maximum length of the trace in the profile", - value=[max_trace], - ) - ) - Config().max_seen_trace = max(max_trace, Config().max_seen_trace) - - -def generate_trace_stats(graph: Graph) -> dict[str, list[TraceStat]]: - """Generates trace stats - - :param graph: sankey graph - :return: trace stats - """ - trace_stats = defaultdict(list) - log.minor_info("Generating stats for traces") - trace_cache: dict[str, TraceStat] = {} - trace_counter: int = 0 - for uid, traces in log.progress( - graph.uid_to_traces.items(), description="Generating Trace Stats" - ): - processed = set() - for trace in [trace for trace in traces if len(trace) > 1]: - key = ",".join(trace) - if key not in processed: - # High memory consumption - processed.add(key) - if key in trace_cache: - trace_stats[uid].append(trace_cache[key]) - continue - trace_len = len(trace) - stat_len = len(Stats.all_stats()) - base_sum: array.array[float] = array.array( - "d", [sys.float_info.max if Config().trace_is_inclusive else 0.0] * stat_len - ) - base_partial: list[array.array[float]] = [ - array.array("d", [0.0] * (trace_len - 1)) for _ in range(0, stat_len) - ] - tgt_sum: array.array[float] = array.array( - "d", [sys.float_info.max if Config().trace_is_inclusive else 0.0] * stat_len - ) - tgt_partial: list[array.array[float]] = [ - array.array("d", [0.0] * (trace_len - 1)) for _ in range(0, stat_len) - ] - for i in range(0, trace_len - 1): - src, tgt = f"{trace[i]}#{i}", f"{trace[i + 1]}#{i + 1}" - node = graph.get_node(src) - if tgt in node.callees: - stats = node.callees[tgt].stats - for j, stat in enumerate(Stats.all_stats()): - base_stat, tgt_stat = stats.baseline[stat], stats.target[stat] - base_partial[j][i] += base_stat - tgt_partial[j][i] += tgt_stat - if Config().trace_is_inclusive: - base_sum[j] = common_kit.try_min(base_sum[j], base_stat) - tgt_sum[j] = common_kit.try_min(tgt_sum[j], tgt_stat) - else: - base_sum[j] += base_stat - tgt_sum[j] += tgt_stat - - trace_stat = TraceStat( - trace, trace_counter, base_sum, tgt_sum, base_partial, tgt_partial - ) - trace_counter += 1 - trace_cache[key] = trace_stat - trace_stats[uid].append(trace_stat) - return trace_stats - - -def _get_baseline_target_total() -> tuple[list[float], list[float]]: - """A helper function to obtain baseline target total values for all stats. - - :return: collections of baseline and target total values - """ - # TODO: Nasty, but currently the only reasonable way to access the maxima. - return [ - float(stat.value[0]) - for stat in Config().profile_stats["baseline"] - if stat.name.startswith("Overall") - ], [ - float(stat.value[0]) - for stat in Config().profile_stats["target"] - if stat.name.startswith("Overall") - ] - - -def _to_percentage(value: float) -> float: - """Transforms a value to a percentage and round it to 2 decimal places. - - :param value: the value to transform and round - - :return: the transformed value - """ - return round(100 * value, 2) - - -def generate_selection(graph: Graph, trace_stats: dict[str, list[TraceStat]]) -> list[SelectionRow]: - """Generates selection table - - :param graph: sankey graph - :param trace_stats: stats for traces for each uid - :return: list of selection rows for table - """ - selection = [] - log.minor_info("Generating selection table") - trace_stat_cache: dict[ - str, - tuple[str, str, float, float, float, float, float, float, float, float, float, float, str], - ] = {} - stat_len = len(Stats.all_stats()) - baseline_max, target_max = _get_baseline_target_total() - for uid, nodes in log.progress( - graph.uid_to_nodes.items(), description="Generating Selection Rows" - ): - baseline_overall: array.array[float] = array.array("d", [0.0] * stat_len) - target_overall: array.array[float] = array.array("d", [0.0] * stat_len) - # StatID, baseline abs, target abs, proportional delta, abs delta, rel delta - stats: list[tuple[int, float, float, float, float, float]] = [] - for node in nodes: - for i, known_stat in enumerate(Stats.all_stats()): - baseline_overall[i] += node.stats.baseline[known_stat] - target_overall[i] += node.stats.target[known_stat] - for i in range(0, stat_len): - baseline, target = baseline_overall[i], target_overall[i] - prop_diff = _to_percentage(target / target_max[i] - baseline / baseline_max[i]) - abs_diff = target - baseline - try: - rel_diff = _to_percentage(abs_diff / max(baseline, target)) - except ZeroDivisionError: - # Skip this record, both baseline and target are 0 - continue - stats.append((i, baseline, target, prop_diff, abs_diff, rel_diff)) - stats = sorted(stats, key=itemgetter(3)) - - if stats: - state: Literal["not in baseline", "not in target", "in both"] = "in both" - if all(val == 0 or val == 0.0 for val in baseline_overall): - state = "not in baseline" - elif all(val == 0 or val == 0.0 for val in target_overall): - state = "not in target" - - # Prepare trace stats - long_trace_stats = extract_stats_from_trace(graph, trace_stats[uid], trace_stat_cache) - selection.append( - SelectionRow(uid, graph.uid_to_id[uid], state, stats, long_trace_stats) - ) - return selection - - -def extract_stats_from_trace( - graph: Graph, - uid_stats: list[TraceStat], - cache: dict[ - str, - tuple[str, str, float, float, float, float, float, float, float, float, float, float, str], - ], -) -> list[ - tuple[str, str, float, float, float, float, float, float, float, float, float, float, str] -]: - """Extracts stats from trace - - :param graph: sankey graph - :param uid_stats: stats for each uid in the graph - :param cache: helper cache for reducing the statistics - """ - uid_trace_stats: list[ - tuple[str, str, float, float, float, float, float, float, float, float, float, float, str] - ] = [] - top_n_limit, sort_by_key, relative_thresh, threshold_idx = ( - Config().top_n_traces, - 4, - Config().relative_threshold, - 6, - ) - baseline_max, target_max = _get_baseline_target_total() - for trace in uid_stats: - # Trace is in form of [short_trace, stat_type, baseline abs, target abs, proportional delta, - # abs delta, rel delta, long_trace] - for i, stat in enumerate(Stats.all_stats()): - key = f"{trace.trace_id}#{stat}" - if key not in cache: - target_cost, baseline_cost = trace.target_cost[i], trace.baseline_cost[i] - abs_amount = target_cost - baseline_cost - try: - rel_amount = _to_percentage(abs_amount / max(baseline_cost, target_cost)) - except ZeroDivisionError: - continue - prop_diff = _to_percentage( - target_cost / target_max[i] - baseline_cost / baseline_max[i] - ) - - short_id = f"{graph.uid_to_id[trace.trace[0]]};{graph.uid_to_id[trace.trace[-1]]}" - long_trace = ";".join([f"{graph.uid_to_id[t]}" for t in trace.trace]) - long_baseline_stats = ";".join( - common_kit.compact_convert_list_to_str(trace.baseline_partial_costs[i]) - ) - long_target_stats = ";".join( - common_kit.compact_convert_list_to_str(trace.target_partial_costs[i]) - ) - long_data = f"{long_trace}#{long_baseline_stats}#{long_target_stats}" - # FIXME: This uses dummy values for exclusive trace metrics. - cache[key] = ( - short_id, - stat, - baseline_cost, - 0.0, - target_cost, - 0.0, - prop_diff, - 0.0, - abs_amount, - 0.0, - rel_amount, - 0.0, - long_data, - ) - if float(cache[key][threshold_idx]) >= relative_thresh: - common_kit.add_to_sorted( - uid_trace_stats, cache[key], itemgetter(sort_by_key), top_n_limit - ) - return uid_trace_stats - - -def compose_chatbot_contexts(sources: tuple[str, ...]) -> str: - """Compose the additional chatbot prompt context from possibly multiple sources. - - :param sources: sources of the prompt context; may be either strings or a file paths. - :return: the resulting prompt context string. - """ - context_str: str = "" - for source in sources: - if source.endswith(".prompt"): - # The context is stored in a file, attempt to open and read it. - with streams.safely_open_and_log(Path(source), "r", fatal_fail=False) as handle: - if handle is not None: - context_str += f"\n{handle.read()}" - else: - context_str += f"\n{source}" - return context_str + "\n" - - -def generate_report_from_native( - lhs_profile: profile.Profile, rhs_profile: profile.Profile, **kwargs: Any -) -> None: - """Generates differences of two profiles as sankey diagram - - :param lhs_profile: baseline profile - :param rhs_profile: target profile - :param kwargs: additional arguments - """ - fg_forward_kwargs = { - arg.replace("flamegraph_", ""): val - for arg, val in kwargs.items() - if arg.startswith("flamegraph_") - } - # FIXME: temporary solution before refactoring to FlameGraphSettings. - del fg_forward_kwargs["parallelize"] - del fg_forward_kwargs["normalize"] - - # We automatically set the value of True for kperf, which samples - Config().minimize = kwargs.get("minimize", False) - Config().top_n_traces = kwargs.get("top_n", Config().DefaultTopN) - Config().relative_threshold = kwargs.get( - "filter_by_relative", Config().DefaultRelativeThreshold - ) - if lhs_profile.get("collector_info", {}).get("name") == "kperf": - Config().trace_is_inclusive = True - else: - Config().trace_is_inclusive = kwargs.get("trace_is_inclusive", False) - - graph = Graph() - - process_traces(lhs_profile, "baseline", graph) - process_traces(rhs_profile, "target", graph) - lhs_stats = list(lhs_profile.all_stats()) - rhs_stats = list(rhs_profile.all_stats()) - - trace_stats = generate_trace_stats(graph) - selection_table = generate_selection(graph, trace_stats) - flamegraphs, lhs_fg_stats, rhs_fg_stats = flamegraph.generate_flamegraphs( - lhs_profile, - rhs_profile, - Stats.all_stats(), - skip_diff=False, - minimize=Config().minimize, - squash_unknown=not kwargs.get("no_squash_unknown", False), - **fg_forward_kwargs, - ) - lhs_fg_diff_stats, rhs_fg_diff_stats = diff_kit.generate_diff_of_stats( - flamegraph.process_flamegraph_stats(lhs_fg_stats), - flamegraph.process_flamegraph_stats(rhs_fg_stats), - ) - - lhs_header, rhs_header = diff_kit.generate_diff_of_headers( - diff_kit.generate_specification(lhs_profile), diff_kit.generate_specification(rhs_profile) - ) - lhs_vulnerabilities, rhs_vulnerabilities = diff_kit.generate_diff_of_headers( - diff_kit.generate_vulnerabilities(lhs_profile), - diff_kit.generate_vulnerabilities(rhs_profile), - ) - lhs_diff_stats, rhs_diff_stats = diff_kit.generate_diff_of_stats(lhs_stats, rhs_stats) - lhs_meta, rhs_meta = diff_kit.generate_diff_of_headers( - lhs_profile.all_metadata(), rhs_profile.all_metadata() - ) - - # Process user-defined chatbot prompt context, if provided. - prompt_ctx = "" - if kwargs["chatbot_url"] is not None: - prompt_ctx_sources: tuple[str, ...] | None = kwargs.get("chatbot_prompt_context", None) - if prompt_ctx_sources is not None: - log.minor_info("Processing chatbot prompt context") - prompt_ctx = compose_chatbot_contexts(prompt_ctx_sources) - - template = templates.get_template("diff_views/report.html.jinja2") - baseline_total, target_total = _get_baseline_target_total() - content = template.render( - title="Perun Report - Profiles Comparison", - perun_version=perun.__version__, - timestamp=datetime.now(timezone.utc).strftime("%d %b %Y, %H:%M:%S") + " UTC", - chatbot=kwargs["chatbot_url"], - chatbot_prompt_context=prompt_ctx, - lhs_tag="Baseline (base)", - lhs_header=lhs_header, - lhs_vulnerabilities=lhs_vulnerabilities, - lhs_user_stats=lhs_diff_stats, - lhs_fg_stats=lhs_fg_diff_stats, - lhs_metadata=lhs_meta, - rhs_tag="Target (tgt)", - rhs_header=rhs_header, - rhs_vulnerabilities=rhs_vulnerabilities, - rhs_user_stats=rhs_diff_stats, - rhs_fg_stats=rhs_fg_diff_stats, - rhs_metadata=rhs_meta, - palette=WebColorPalette, - callee_graph=graph.to_jinja_string("callees"), - caller_graph=graph.to_jinja_string("callers"), - stat_list=Stats.all_stats(), - units=[mapping.get_unit(s) for s in Stats.all_stats()], - stats="[" - + ",".join( - list(map(itemgetter(0), sorted(list(graph.stats_to_id.items()), key=itemgetter(1)))) - ) - + "]", - nodes=map(itemgetter(0), sorted(list(graph.uid_to_id.items()), key=itemgetter(1))), - node_map=[ - sorted([node.get_order() for node in nodes]) - for nodes in map( - itemgetter(1), - sorted(list(graph.uid_to_nodes.items()), key=lambda x: graph.uid_to_id[x[0]]), - ) - ], - flamegraphs=flamegraphs, - selection_table=selection_table, - offline=config.lookup_key_recursively("showdiff.offline", False), - height=Config().max_seen_trace * Config().DefaultHeightCoefficient + 200, - container_height=Config().max_seen_trace * Config().DefaultHeightCoefficient + 200, - notes_enabled=True, - links=list(kwargs.get("link", [])), - default_theme=kwargs.get("default_theme", "dark"), - total_baseline=baseline_total[0], - total_target=target_total[0], - is_folded=False, - ) - log.minor_success("HTML template", "rendered") - output_file = diff_kit.save_diff_view( - kwargs.get("output_path"), content, "report", lhs_profile, rhs_profile - ) - log.minor_status("Output saved", log.path_style(output_file)) diff --git a/perun/view_diff/short.py b/perun/view_diff/short.py index 65c0ece92..aa5785bd5 100755 --- a/perun/view_diff/short.py +++ b/perun/view_diff/short.py @@ -16,16 +16,19 @@ # Standard Imports from dataclasses import dataclass -from typing import Any import itertools +from typing import Any # Third-Party Imports import pandas import tabulate # Perun Imports +from perun.logic import config from perun.profiles import native as profile +from perun.profiles.conversions import native_pandas from perun.utils import log +from perun.utils.common import common_kit PRECISION: int = 2 @@ -75,7 +78,7 @@ def print_header(lhs_profile: profile.Profile, rhs_profile: profile.Profile) -> """ log.major_info("Difference Summary") command = " ".join([lhs_profile["header"]["cmd"], lhs_profile["header"]["workload"]]).strip() - data = [ + data: list[list[Any]] = [ ["baseline origin", lhs_profile.get("origin")], ["target origin", rhs_profile.get("origin")], ["command", command], @@ -94,7 +97,13 @@ def get_top_n_records( :param aggregated_key: key for aggregation of the top table :return: list of top N records """ - df = profile.resources_to_pandas_dataframe(prof) + df = native_pandas.resources_to_pandas_dataframe(prof) + # Compact kperf representation has the entire trace in "uid". Split the column if needed. + if not {"command", "trace"}.issubset(df.columns): + df_parts = df["uid"].str.split(";") + df["command"] = df_parts.str[0] + df["trace"] = df_parts.str[1:-1].str.join(";") + df["uid"] = df_parts.str[-1] if filters := kwargs.get("filters"): df = filter_df(df, filters) @@ -165,6 +174,15 @@ def compare_profiles( # Print short header with some information print_header(lhs_profile, rhs_profile) + # Aggregate the resources in LHS and RHS profiles. + aggregation: common_kit.Aggregations = common_kit.Aggregations.from_string( + config.lookup_key_recursively( + "profile.aggregation", default=common_kit.Aggregations.default_name() + ) + ) + lhs_profile.apply(aggregation) + rhs_profile.apply(aggregation) + # Compare top-N resources top_n_lhs = get_top_n_records(lhs_profile, **kwargs) top_n_rhs = get_top_n_records(rhs_profile, **kwargs) diff --git a/tests-perf/test_performance.py b/tests-perf/test_performance.py index c45debc53..4573fd3fa 100644 --- a/tests-perf/test_performance.py +++ b/tests-perf/test_performance.py @@ -13,8 +13,8 @@ import sys import tabulate import perun.logic.store as store +from perun.profiles.conversions import native_pandas from perun.utils.common import common_kit -from perun.profiles.native import convert import perun.utils.log as log import perun.utils.streams as streams @@ -73,7 +73,7 @@ def performance_test(bench_dir, file, store_dir, executed_tests): if "convert" in executed_tests: before = time.time() - _ = convert.resources_to_pandas_dataframe(profile) + _ = native_pandas.resources_to_pandas_dataframe(profile) elapsed = time.time() - before results.append(elapsed) log.write( diff --git a/tests/conftest.py b/tests/conftest.py index b1ed6ae8c..52c77acf4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,6 @@ from perun.utils import decorators, log, metrics, streams from perun.utils.common import common_kit import perun.testing.utils as test_utils -from perun.view_diff import report_native if TYPE_CHECKING: from perun.profiles.native import Profile @@ -554,6 +553,4 @@ def setup(): # We disable the metrics by default, since they might slow down tests metrics.Metrics.enabled = False - report_native.Stats.KnownStatsSet.clear() - report_native.Stats.SortedStats = [] yield diff --git a/tests/profiles/diff_profiles/kperf-baseline-stats-metadata.perf b/tests/profiles/diff_profiles/kperf-baseline-stats-metadata.perf index 7f1647359..8cf3a185a 100644 --- a/tests/profiles/diff_profiles/kperf-baseline-stats-metadata.perf +++ b/tests/profiles/diff_profiles/kperf-baseline-stats-metadata.perf @@ -2,9070 +2,1886 @@ "resources": { "insert_work#0": { "amount": [ - 788525, 788525, 788525 + 788525, + 788525, + 788525 ] }, "dequeue_entity#0": { "amount": [ - 1081404, 1081404, 1081404 + 1081404, + 1081404, + 1081404 ] }, "__intel_pmu_enable_all.isra.0#0": { "amount": [ - 173, 173, 173 + 173, + 173, + 173 ] }, "__perf_event_task_sched_in#0": { "amount": [ - 301, 301, 301 + 301, + 301, + 301 ] }, "is_cpu_allowed#0": { "amount": [ - 198140, 198140, 198140 + 198140, + 198140, + 198140 ] }, "__intel_pmu_enable_all.isra.0#1": { "amount": [ - 49, 49, 49 + 49, + 49, + 49 ] }, "__intel_pmu_enable_all.isra.0#2": { "amount": [ - 90, 90, 90 + 90, + 90, + 90 ] }, "[libc.so.6]#0": { "amount": [ - 647901, 647901, 647901 + 647901, + 647901, + 647901 ] }, "[ls]#0": { "amount": [ - 407860, 407860, 407860 + 407860, + 407860, + 407860 ] }, "[ls]#1": { "amount": [ - 2065754, 2065754, 2065754 + 2065754, + 2065754, + 2065754 ] }, "path_getxattr#0": { "amount": [ - 386145, 386145, 386145 + 386145, + 386145, + 386145 ] }, "strncpy_from_user#0": { "amount": [ - 360662, 360662, 360662 + 360662, + 360662, + 360662 ] }, "get_cached_acl#0": { "amount": [ - 255439, 255439, 255439 + 255439, + 255439, + 255439 ] }, "dput#0": { "amount": [ - 822718, 822718, 822718 + 822718, + 822718, + 822718 ] }, "rcu_all_qs#0": { "amount": [ - 282305, 282305, 282305 + 282305, + 282305, + 282305 ] }, "__srcu_read_lock#0": { "amount": [ - 570839, 570839, 570839 + 570839, + 570839, + 570839 ] }, "__inode_wait_for_writeback#0": { "amount": [ - 252971, 252971, 252971 + 252971, + 252971, + 252971 ] }, "memcmp#0": { "amount": [ - 370723, 370723, 370723 + 370723, + 370723, + 370723 ] }, "p9_client_clunk#0": { "amount": [ - 304183, 304183, 304183 + 304183, + 304183, + 304183 ] }, "p9_client_rpc#0": { "amount": [ - 276682, 276682, 276682 + 276682, + 276682, + 276682 ] }, "update_curr#0": { "amount": [ - 626576, 626576, 626576 + 626576, + 626576, + 626576 ] }, "prepare_to_wait_event#0": { "amount": [ - 6759, 6759, 6759 + 6759, + 6759, + 6759 ] }, "__schedule#0": { "amount": [ - 303056, 303056, 303056 + 303056, + 303056, + 303056 ] }, "intel_pmu_disable_all#0": { "amount": [ - 436514, 436514, 436514 + 436514, + 436514, + 436514 ] }, "__perf_event_task_sched_in#1": { "amount": [ - 481539, 481539, 481539 + 481539, + 481539, + 481539 ] }, "__intel_pmu_enable_all.isra.0#3": { "amount": [ - 296791, 296791, 296791 + 296791, + 296791, + 296791 ] }, "radix_tree_delete_item#0": { "amount": [ - 309327, 309327, 309327 + 309327, + 309327, + 309327 ] }, "always_delete_dentry#0": { "amount": [ - 293584, 293584, 293584 + 293584, + 293584, + 293584 ] }, "get_obj_cgroup_from_current#0": { "amount": [ - 869880, 869880, 869880 + 869880, + 869880, + 869880 ] }, "kmem_cache_alloc#0": { "amount": [ - 410931, 410931, 410931 + 410931, + 410931, + 410931 ] }, "___slab_alloc.constprop.0#0": { "amount": [ - 261659, 261659, 261659 + 261659, + 261659, + 261659 ] }, "p9_fd_request#0": { "amount": [ - 387472, 387472, 387472 + 387472, + 387472, + 387472 ] }, "try_to_wake_up#0": { "amount": [ - 395389, 395389, 395389 + 395389, + 395389, + 395389 ] }, "_raw_spin_lock_irqsave#0": { "amount": [ - 436355, 436355, 436355 + 436355, + 436355, + 436355 ] }, "dequeue_entity#1": { "amount": [ - 322790, 322790, 322790 + 322790, + 322790, + 322790 ] }, "__perf_event_task_sched_in#2": { "amount": [ - 633, 633, 633 + 633, + 633, + 633 ] }, "__intel_pmu_enable_all.isra.0#4": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "_raw_spin_lock_irqsave#1": { "amount": [ - 267141, 267141, 267141 + 267141, + 267141, + 267141 ] }, "___slab_alloc.constprop.0#1": { "amount": [ - 283948, 283948, 283948 + 283948, + 283948, + 283948 ] }, "idr_remove#0": { "amount": [ - 284277, 284277, 284277 + 284277, + 284277, + 284277 ] }, "p9_client_rpc#1": { "amount": [ - 275066, 275066, 275066 + 275066, + 275066, + 275066 ] }, "memcpy_erms#0": { "amount": [ - 386198, 386198, 386198 + 386198, + 386198, + 386198 ] }, "p9pdu_vwritef#0": { "amount": [ - 377213, 377213, 377213 + 377213, + 377213, + 377213 ] }, "__radix_tree_lookup#0": { "amount": [ - 408615, 408615, 408615 + 408615, + 408615, + 408615 ] }, "idr_find#0": { "amount": [ - 302336, 302336, 302336 + 302336, + 302336, + 302336 ] }, "insert_work#1": { "amount": [ - 316781, 316781, 316781 + 316781, + 316781, + 316781 ] }, "_raw_spin_lock_irqsave#2": { "amount": [ - 398270, 398270, 398270 + 398270, + 398270, + 398270 ] }, "is_cpu_allowed#1": { "amount": [ - 373799, 373799, 373799 + 373799, + 373799, + 373799 ] }, "prepare_to_wait_event#1": { "amount": [ - 1176, 1176, 1176 + 1176, + 1176, + 1176 ] }, "finish_task_switch.isra.0#0": { "amount": [ - 353516, 353516, 353516 + 353516, + 353516, + 353516 ] }, "__intel_pmu_enable_all.isra.0#5": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "acct_account_cputime#0": { "amount": [ - 315318, 315318, 315318 + 315318, + 315318, + 315318 ] }, "kmem_cache_free.part.0#0": { "amount": [ - 296132, 296132, 296132 + 296132, + 296132, + 296132 ] }, "_raw_spin_lock_irqsave#3": { "amount": [ - 299381, 299381, 299381 + 299381, + 299381, + 299381 ] }, "__raw_callee_save___pv_queued_spin_unlock#0": { "amount": [ - 387460, 387460, 387460 + 387460, + 387460, + 387460 ] }, "p9pdu_readf#0": { "amount": [ - 287855, 287855, 287855 + 287855, + 287855, + 287855 ] }, "v9fs_cache_inode_get_cookie#0": { "amount": [ - 371528, 371528, 371528 + 371528, + 371528, + 371528 ] }, "v9fs_init_inode#0": { "amount": [ - 404430, 404430, 404430 + 404430, + 404430, + 404430 ] }, "v9fs_stat2inode_dotl#0": { "amount": [ - 394569, 394569, 394569 + 394569, + 394569, + 394569 ] }, "__virt_addr_valid#0": { "amount": [ - 278799, 278799, 278799 + 278799, + 278799, + 278799 ] }, "syscall_exit_to_user_mode#0": { "amount": [ - 793104, 793104, 793104 + 793104, + 793104, + 793104 ] }, "localeconv@@GLIBC_2.2.5#0": { "amount": [ - 989952, 989952, 989952 + 989952, + 989952, + 989952 ] }, "_int_malloc#0": { "amount": [ - 743942, 743942, 743942 + 743942, + 743942, + 743942 ] }, "__vdso_clock_gettime#0": { "amount": [ - 373595, 373595, 373595 + 373595, + 373595, + 373595 ] }, "_IO_old_init#0": { "amount": [ - 746955, 746955, 746955 + 746955, + 746955, + 746955 ] }, "kfree#0": { "amount": [ - 187300, 187300, 187300 + 187300, + 187300, + 187300 ] }, "memcpy_erms#1": { "amount": [ - 193571, 193571, 193571 + 193571, + 193571, + 193571 ] }, "try_to_wake_up#1": { "amount": [ - 837102, 837102, 837102 + 837102, + 837102, + 837102 ] }, "set_next_buddy#0": { "amount": [ - 371293, 371293, 371293 + 371293, + 371293, + 371293 ] }, "__libc_calloc#0": { "amount": [ - 1139742, 1139742, 1139742 + 1139742, + 1139742, + 1139742 ] }, "schedule#0": { "amount": [ - 105467, 105467, 105467 + 105467, + 105467, + 105467 ] }, "__hrtimer_run_queues#0": { "amount": [ - 110150, 110150, 110150 + 110150, + 110150, + 110150 ] }, "__strlen_avx2#0": { "amount": [ - 404165, 404165, 404165 + 404165, + 404165, + 404165 ] }, "cfree@GLIBC_2.2.5#0": { "amount": [ - 845160, 845160, 845160 + 845160, + 845160, + 845160 ] }, "entry_SYSCALL_64#0": { "amount": [ - 277906, 277906, 277906 + 277906, + 277906, + 277906 ] }, "strncpy_from_user#1": { "amount": [ - 744276, 744276, 744276 + 744276, + 744276, + 744276 ] }, "p9_client_rpc#2": { "amount": [ - 483062, 483062, 483062 + 483062, + 483062, + 483062 ] }, "_raw_spin_lock#0": { "amount": [ - 550419, 550419, 550419 + 550419, + 550419, + 550419 ] }, "try_to_wake_up#2": { "amount": [ - 328041, 328041, 328041 + 328041, + 328041, + 328041 ] }, "kmem_cache_free.part.0#1": { "amount": [ - 372972, 372972, 372972 + 372972, + 372972, + 372972 ] }, "__list_add_valid#0": { "amount": [ - 317189, 317189, 317189 + 317189, + 317189, + 317189 ] }, "_raw_spin_lock_irqsave#4": { "amount": [ - 626576, 626576, 626576 + 626576, + 626576, + 626576 ] }, "p9pdu_readf#1": { "amount": [ - 972738, 972738, 972738 + 972738, + 972738, + 972738 ] }, "memcpy_erms#2": { "amount": [ - 376612, 376612, 376612 + 376612, + 376612, + 376612 ] }, "finish_task_switch.isra.0#1": { "amount": [ - 270022, 270022, 270022 + 270022, + 270022, + 270022 ] }, "p9_fid_create#0": { "amount": [ - 457930, 457930, 457930 + 457930, + 457930, + 457930 ] }, "__list_del_entry_valid#0": { "amount": [ - 304152, 304152, 304152 + 304152, + 304152, + 304152 ] }, "wake_up_bit#0": { "amount": [ - 282569, 282569, 282569 + 282569, + 282569, + 282569 ] }, "wake_up_bit#1": { "amount": [ - 303405, 303405, 303405 + 303405, + 303405, + 303405 ] }, "_raw_spin_lock_irqsave#5": { "amount": [ - 396841, 396841, 396841 + 396841, + 396841, + 396841 ] }, "__queue_work#0": { "amount": [ - 443619, 443619, 443619 + 443619, + 443619, + 443619 ] }, "kthread_is_per_cpu#0": { "amount": [ - 289566, 289566, 289566 + 289566, + 289566, + 289566 ] }, "update_min_vruntime#0": { "amount": [ - 326866, 326866, 326866 + 326866, + 326866, + 326866 ] }, "clear_buddies#0": { "amount": [ - 356942, 356942, 356942 + 356942, + 356942, + 356942 ] }, "__list_del_entry_valid#1": { "amount": [ - 422374, 422374, 422374 + 422374, + 422374, + 422374 ] }, "memcg_slab_free_hook#0": { "amount": [ - 564812, 564812, 564812 + 564812, + 564812, + 564812 ] }, "__lookup_slow#0": { "amount": [ - 570839, 570839, 570839 + 570839, + 570839, + 570839 ] }, "get_obj_cgroup_from_current#1": { "amount": [ - 412936, 412936, 412936 + 412936, + 412936, + 412936 ] }, "__d_rehash#0": { "amount": [ - 374824, 374824, 374824 + 374824, + 374824, + 374824 ] }, "idr_get_free#0": { "amount": [ - 377157, 377157, 377157 + 377157, + 377157, + 377157 ] }, "kmem_cache_alloc#1": { "amount": [ - 288831, 288831, 288831 + 288831, + 288831, + 288831 ] }, "___slab_alloc.constprop.0#2": { "amount": [ - 834354, 834354, 834354 + 834354, + 834354, + 834354 ] }, "enqueue_task_fair#0": { "amount": [ - 324380, 324380, 324380 + 324380, + 324380, + 324380 ] }, "ttwu_do_wakeup#0": { "amount": [ - 263927, 263927, 263927 + 263927, + 263927, + 263927 ] }, "update_rq_clock#0": { "amount": [ - 297309, 297309, 297309 + 297309, + 297309, + 297309 ] }, "p9pdu_readf#2": { "amount": [ - 287020, 287020, 287020 + 287020, + 287020, + 287020 ] }, "__schedule#1": { "amount": [ - 402974, 402974, 402974 + 402974, + 402974, + 402974 ] }, "intel_pmu_disable_all#1": { "amount": [ - 270780, 270780, 270780 + 270780, + 270780, + 270780 ] }, "x86_pmu_disable#0": { "amount": [ - 578521, 578521, 578521 + 578521, + 578521, + 578521 ] }, "finish_task_switch.isra.0#2": { "amount": [ - 264024, 264024, 264024 + 264024, + 264024, + 264024 ] }, "__radix_tree_replace#0": { "amount": [ - 671442, 671442, 671442 + 671442, + 671442, + 671442 ] }, "idr_get_free#1": { "amount": [ - 300146, 300146, 300146 + 300146, + 300146, + 300146 ] }, "p9_req_put#0": { "amount": [ - 701944, 701944, 701944 + 701944, + 701944, + 701944 ] }, "p9pdu_readf#3": { "amount": [ - 315014, 315014, 315014 + 315014, + 315014, + 315014 ] }, "kmem_cache_alloc#2": { "amount": [ - 457930, 457930, 457930 + 457930, + 457930, + 457930 ] }, "p9_fd_request#1": { "amount": [ - 394836, 394836, 394836 + 394836, + 394836, + 394836 ] }, "enqueue_entity#0": { "amount": [ - 516319, 516319, 516319 + 516319, + 516319, + 516319 ] }, "memcpy_erms#3": { "amount": [ - 400593, 400593, 400593 + 400593, + 400593, + 400593 ] }, "__raw_callee_save___pv_queued_spin_unlock#1": { "amount": [ - 378607, 378607, 378607 + 378607, + 378607, + 378607 ] }, "pick_next_task_fair#0": { "amount": [ - 285677, 285677, 285677 + 285677, + 285677, + 285677 ] }, "__fscache_acquire_cookie#0": { "amount": [ - 504914, 504914, 504914 + 504914, + 504914, + 504914 ] }, "kmem_cache_alloc#3": { "amount": [ - 439744, 439744, 439744 + 439744, + 439744, + 439744 ] }, "v9fs_stat2inode_dotl#1": { "amount": [ - 339890, 339890, 339890 + 339890, + 339890, + 339890 ] }, "down_read#0": { "amount": [ - 410430, 410430, 410430 + 410430, + 410430, + 410430 ] }, "__check_object_size#0": { "amount": [ - 833353, 833353, 833353 + 833353, + 833353, + 833353 ] }, "syscall_exit_to_user_mode#1": { "amount": [ - 303686, 303686, 303686 + 303686, + 303686, + 303686 ] }, "syscall_return_via_sysret#0": { "amount": [ - 841935, 841935, 841935 + 841935, + 841935, + 841935 ] }, "kmem_cache_alloc#4": { "amount": [ - 110150, 110150, 110150 + 110150, + 110150, + 110150 ] }, "__strlen_avx2#1": { "amount": [ - 972738, 972738, 972738 + 972738, + 972738, + 972738 ] }, "entry_SYSCALL_64#1": { "amount": [ - 405507, 405507, 405507 + 405507, + 405507, + 405507 ] }, "fsnotify_destroy_marks#0": { "amount": [ - 268089, 268089, 268089 + 268089, + 268089, + 268089 ] }, "hook_inode_free_security#0": { "amount": [ - 366998, 366998, 366998 + 366998, + 366998, + 366998 ] }, "__inode_wait_for_writeback#1": { "amount": [ - 274702, 274702, 274702 + 274702, + 274702, + 274702 ] }, "fscache_invalidate_writes#0": { "amount": [ - 317720, 317720, 317720 + 317720, + 317720, + 317720 ] }, "_raw_spin_unlock_irqrestore#0": { "amount": [ - 424749, 424749, 424749 + 424749, + 424749, + 424749 ] }, "kmem_cache_alloc#5": { "amount": [ - 414115, 414115, 414115 + 414115, + 414115, + 414115 ] }, "__update_load_avg_cfs_rq#0": { "amount": [ - 428196, 428196, 428196 + 428196, + 428196, + 428196 ] }, "__intel_pmu_enable_all.isra.0#6": { "amount": [ - 25, 25, 25 + 25, + 25, + 25 ] }, "path_init#0": { "amount": [ - 680390, 680390, 680390 + 680390, + 680390, + 680390 ] }, "d_alloc_parallel#0": { "amount": [ - 394528, 394528, 394528 + 394528, + 394528, + 394528 ] }, "get_obj_cgroup_from_current#2": { "amount": [ - 671442, 671442, 671442 + 671442, + 671442, + 671442 ] }, "get_obj_cgroup_from_current#3": { "amount": [ - 421121, 421121, 421121 + 421121, + 421121, + 421121 ] }, "__d_add#0": { "amount": [ - 407939, 407939, 407939 + 407939, + 407939, + 407939 ] }, "p9_fd_request#2": { "amount": [ - 240152, 240152, 240152 + 240152, + 240152, + 240152 ] }, "__queue_work#1": { "amount": [ - 258055, 258055, 258055 + 258055, + 258055, + 258055 ] }, "try_to_wake_up#3": { "amount": [ - 369833, 369833, 369833 + 369833, + 369833, + 369833 ] }, "memcpy_erms#4": { "amount": [ - 743942, 743942, 743942 + 743942, + 743942, + 743942 ] }, "__schedule#2": { "amount": [ - 165631, 165631, 165631 + 165631, + 165631, + 165631 ] }, "__intel_pmu_enable_all.isra.0#7": { "amount": [ - 9, 9, 9 + 9, + 9, + 9 ] }, "kmem_cache_alloc#6": { "amount": [ - 260415, 260415, 260415 + 260415, + 260415, + 260415 ] }, "p9_client_rpc#3": { "amount": [ - 430749, 430749, 430749 + 430749, + 430749, + 430749 ] }, "kmem_cache_alloc#7": { "amount": [ - 269417, 269417, 269417 + 269417, + 269417, + 269417 ] }, "p9pdu_prepare#0": { "amount": [ - 355513, 355513, 355513 + 355513, + 355513, + 355513 ] }, "p9_fd_request#3": { "amount": [ - 345879, 345879, 345879 + 345879, + 345879, + 345879 ] }, "kthread_is_per_cpu#1": { "amount": [ - 491649, 491649, 491649 + 491649, + 491649, + 491649 ] }, "cpuacct_charge#0": { "amount": [ - 392002, 392002, 392002 + 392002, + 392002, + 392002 ] }, "dequeue_task_fair#0": { "amount": [ - 304152, 304152, 304152 + 304152, + 304152, + 304152 ] }, "__intel_pmu_enable_all.isra.0#8": { "amount": [ - 9, 9, 9 + 9, + 9, + 9 ] }, "set_next_entity#0": { "amount": [ - 426821, 426821, 426821 + 426821, + 426821, + 426821 ] }, "v9fs_cache_inode_get_cookie#1": { "amount": [ - 721208, 721208, 721208 + 721208, + 721208, + 721208 ] }, "fscache_alloc_cookie#0": { "amount": [ - 387460, 387460, 387460 + 387460, + 387460, + 387460 ] }, "v9fs_vfs_getattr_dotl#0": { "amount": [ - 401636, 401636, 401636 + 401636, + 401636, + 401636 ] }, "p9_client_rpc#4": { "amount": [ - 289951, 289951, 289951 + 289951, + 289951, + 289951 ] }, "enqueue_task_fair#1": { "amount": [ - 564812, 564812, 564812 + 564812, + 564812, + 564812 ] }, "x86_pmu_disable#1": { "amount": [ - 282569, 282569, 282569 + 282569, + 282569, + 282569 ] }, "finish_task_switch.isra.0#3": { "amount": [ - 312217, 312217, 312217 + 312217, + 312217, + 312217 ] }, "__intel_pmu_enable_all.isra.0#9": { "amount": [ - 16, 16, 16 + 16, + 16, + 16 ] }, "pdu_read#0": { "amount": [ - 331233, 331233, 331233 + 331233, + 331233, + 331233 ] }, "__rseq_handle_notify_resume#0": { "amount": [ - 276682, 276682, 276682 + 276682, + 276682, + 276682 ] }, "__intel_pmu_enable_all.isra.0#10": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "send_call_function_single_ipi#0": { "amount": [ - 8462, 8462, 8462 + 8462, + 8462, + 8462 ] } }, "resource_type_map": { "insert_work#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "insert_work" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;insert_work" }, "dequeue_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - } - ], - "uid": "dequeue_entity" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity" }, "__intel_pmu_enable_all.isra.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__perf_event_task_sched_in#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "is_cpu_allowed#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "is_cpu_allowed" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed" }, "__intel_pmu_enable_all.isra.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "[libc.so.6]#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "[libc.so.6]" + "uid": "ls;[unknown];[libc.so.6]" }, "[ls]#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "[ls]" + "uid": "ls;[unknown];[ls]" }, "[ls]#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - } - ], - "uid": "[ls]" + "time": "???", + "uid": "ls;[unknown];[ls];[ls]" }, "path_getxattr#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "path_getxattr" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr" }, "strncpy_from_user#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - } - ], - "uid": "strncpy_from_user" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;strncpy_from_user" }, "get_cached_acl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_get_acl" - } - ], - "uid": "get_cached_acl" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_get_acl;get_cached_acl" }, "dput#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - } - ], - "uid": "dput" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput" }, "rcu_all_qs#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__cond_resched" - } - ], - "uid": "rcu_all_qs" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__cond_resched;rcu_all_qs" }, "__srcu_read_lock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - }, - { - "func": "fsnotify_destroy_marks" - }, - { - "func": "fsnotify_grab_connector" - } - ], - "uid": "__srcu_read_lock" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;fsnotify_destroy_marks;fsnotify_grab_connector;__srcu_read_lock" }, "__inode_wait_for_writeback#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "inode_wait_for_writeback" - } - ], - "uid": "__inode_wait_for_writeback" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;inode_wait_for_writeback;__inode_wait_for_writeback" }, "memcmp#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "memcmp" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;memcmp" }, "p9_client_clunk#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - } - ], - "uid": "p9_client_clunk" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk" }, "p9_client_rpc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc" }, "update_curr#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - } - ], - "uid": "update_curr" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr" }, "prepare_to_wait_event#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "prepare_to_wait_event" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;prepare_to_wait_event" }, "__schedule#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule" }, "intel_pmu_disable_all#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "intel_pmu_disable_all" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;intel_pmu_disable_all" }, "__perf_event_task_sched_in#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "__intel_pmu_enable_all.isra.0#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "radix_tree_delete_item#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "radix_tree_delete_item" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;radix_tree_delete_item" }, "always_delete_dentry#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - } - ], - "uid": "always_delete_dentry" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;always_delete_dentry" }, "get_obj_cgroup_from_current#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "kmem_cache_alloc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - }, - { - "func": "idr_get_free" - }, - { - "func": "radix_tree_node_alloc.constprop.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free;radix_tree_node_alloc.constprop.0;kmem_cache_alloc" }, "___slab_alloc.constprop.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0" }, "p9_fd_request#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request" }, "try_to_wake_up#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "_raw_spin_lock_irqsave#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "prepare_to_wait_event" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;prepare_to_wait_event;_raw_spin_lock_irqsave" }, "dequeue_entity#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - } - ], - "uid": "dequeue_entity" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity" }, "__perf_event_task_sched_in#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "__intel_pmu_enable_all.isra.0#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "_raw_spin_lock_irqsave#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "___slab_alloc.constprop.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc;___slab_alloc.constprop.0" }, "idr_remove#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "idr_remove" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;idr_remove" }, "p9_client_rpc#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "memcpy_erms#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9pdu_vwritef" - }, - { - "func": "pdu_write" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_vwritef;pdu_write;memcpy_erms" }, "p9pdu_vwritef#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9pdu_writef" - } - ], - "uid": "p9pdu_vwritef" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_writef;p9pdu_vwritef" }, "__radix_tree_lookup#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "__radix_tree_lookup" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;__radix_tree_lookup" }, "idr_find#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "idr_find" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;idr_find" }, "insert_work#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "insert_work" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;insert_work" }, "_raw_spin_lock_irqsave#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;_raw_spin_lock_irqsave" }, "is_cpu_allowed#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "is_cpu_allowed" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed" }, "prepare_to_wait_event#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "prepare_to_wait_event" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;prepare_to_wait_event" }, "finish_task_switch.isra.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__intel_pmu_enable_all.isra.0#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "acct_account_cputime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - }, - { - "func": "__hrtimer_run_queues" - }, - { - "func": "tick_sched_timer" - }, - { - "func": "update_process_times" - } - ], - "uid": "acct_account_cputime" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues;tick_sched_timer;update_process_times;acct_account_cputime" }, "kmem_cache_free.part.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - } - ], - "uid": "kmem_cache_free.part.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0" }, "_raw_spin_lock_irqsave#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "__raw_callee_save___pv_queued_spin_unlock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "_raw_spin_unlock_irqrestore" - } - ], - "uid": "__raw_callee_save___pv_queued_spin_unlock" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0;_raw_spin_unlock_irqrestore;__raw_callee_save___pv_queued_spin_unlock" }, "p9pdu_readf#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9pdu_readf;p9pdu_readf" }, "v9fs_cache_inode_get_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_cache_inode_get_cookie" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie" }, "v9fs_init_inode#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_init_inode" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_init_inode" }, "v9fs_stat2inode_dotl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_stat2inode_dotl" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_stat2inode_dotl" }, "__virt_addr_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "getname_flags.part.0" - }, - { - "func": "strncpy_from_user" - }, - { - "func": "__check_object_size" - } - ], - "uid": "__virt_addr_valid" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;getname_flags.part.0;strncpy_from_user;__check_object_size;__virt_addr_valid" }, "syscall_exit_to_user_mode#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "syscall_exit_to_user_mode" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode" }, "localeconv@@GLIBC_2.2.5#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - } - ], - "uid": "localeconv@@GLIBC_2.2.5" + "time": "???", + "uid": "ls;[unknown];[ls];localeconv@@GLIBC_2.2.5" }, "_int_malloc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - } - ], - "uid": "_int_malloc" + "time": "???", + "uid": "ls;[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];_int_malloc" }, "__vdso_clock_gettime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "clock_gettime@@GLIBC_2.17" - } - ], - "uid": "__vdso_clock_gettime" + "time": "???", + "uid": "ls;[unknown];[unknown];[unknown];clock_gettime@@GLIBC_2.17;__vdso_clock_gettime" }, "_IO_old_init#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "_IO_old_init" + "uid": "ls;[unknown];_IO_old_init" }, "kfree#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___getdents64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9dirent_read" - } - ], - "uid": "kfree" + "time": "???", + "uid": "ls;[unknown];__GI___getdents64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9dirent_read;kfree" }, "memcpy_erms#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___getdents64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9dirent_read" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];__GI___getdents64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9dirent_read;p9pdu_readf;p9pdu_readf;pdu_read;memcpy_erms" }, "try_to_wake_up#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___libc_write" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "ksys_write" - }, - { - "func": "vfs_write" - }, - { - "func": "new_sync_write" - }, - { - "func": "file_tty_write.isra.0" - }, - { - "func": "n_tty_write" - }, - { - "func": "tty_insert_flip_string_and_push_buffer" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;new_sync_write;file_tty_write.isra.0;n_tty_write;tty_insert_flip_string_and_push_buffer;queue_work_on;__queue_work;try_to_wake_up" }, "set_next_buddy#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__close_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_wakeup" - }, - { - "func": "check_preempt_curr" - }, - { - "func": "check_preempt_wakeup" - } - ], - "uid": "set_next_buddy" + "time": "???", + "uid": "ls;[unknown];__close_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_wakeup;check_preempt_curr;check_preempt_wakeup;set_next_buddy" }, "__libc_calloc#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "__libc_calloc" + "uid": "ls;[unknown];__libc_calloc" }, "schedule#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__open64_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "schedule" + "time": "???", + "uid": "ls;[unknown];__open64_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule" }, "__hrtimer_run_queues#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__open64_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - } - ], - "uid": "__hrtimer_run_queues" + "time": "???", + "uid": "ls;[unknown];__open64_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues" }, "__strlen_avx2#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "__strlen_avx2" + "uid": "ls;[unknown];__strlen_avx2" }, "cfree@GLIBC_2.2.5#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "cfree@GLIBC_2.2.5" + "uid": "ls;[unknown];cfree@GLIBC_2.2.5" }, "entry_SYSCALL_64#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - } - ], - "uid": "entry_SYSCALL_64" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64" }, "strncpy_from_user#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - } - ], - "uid": "strncpy_from_user" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;strncpy_from_user" }, "p9_client_rpc#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc" }, "_raw_spin_lock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - } - ], - "uid": "_raw_spin_lock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;_raw_spin_lock" }, "try_to_wake_up#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "kmem_cache_free.part.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - } - ], - "uid": "kmem_cache_free.part.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0" }, "__list_add_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "__list_add_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;__list_add_valid" }, "_raw_spin_lock_irqsave#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "p9pdu_readf#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9pdu_readf" }, "memcpy_erms#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9pdu_readf;pdu_read;memcpy_erms" }, "finish_task_switch.isra.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "p9_fid_create#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - } - ], - "uid": "p9_fid_create" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_fid_create" }, "__list_del_entry_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - } - ], - "uid": "__list_del_entry_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;__list_del_entry_valid" }, "wake_up_bit#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "wake_up_bit" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;wake_up_bit" }, "wake_up_bit#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - } - ], - "uid": "wake_up_bit" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;wake_up_bit" }, "_raw_spin_lock_irqsave#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - }, - { - "func": "___slab_alloc.constprop.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0;_raw_spin_lock_irqsave" }, "__queue_work#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - } - ], - "uid": "__queue_work" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work" }, "kthread_is_per_cpu#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "is_cpu_allowed" - } - ], - "uid": "kthread_is_per_cpu" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed;kthread_is_per_cpu" }, "update_min_vruntime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_curr" - } - ], - "uid": "update_min_vruntime" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr;update_min_vruntime" }, "clear_buddies#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - }, - { - "func": "dequeue_entity" - } - ], - "uid": "clear_buddies" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity;clear_buddies" }, "__list_del_entry_valid#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "pick_next_task_fair" - } - ], - "uid": "__list_del_entry_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;pick_next_task_fair;__list_del_entry_valid" }, "memcg_slab_free_hook#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - } - ], - "uid": "memcg_slab_free_hook" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_req_put;kmem_cache_free.part.0;memcg_slab_free_hook" }, "__lookup_slow#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - } - ], - "uid": "__lookup_slow" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow" }, "get_obj_cgroup_from_current#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "__d_rehash#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "d_splice_alias" - }, - { - "func": "__d_add" - } - ], - "uid": "__d_rehash" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;d_splice_alias;__d_add;__d_rehash" }, "idr_get_free#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "idr_get_free" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free" }, "kmem_cache_alloc#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc" }, "___slab_alloc.constprop.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0" }, "enqueue_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - } - ], - "uid": "enqueue_task_fair" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair" }, "ttwu_do_wakeup#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "ttwu_do_wakeup" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_wakeup" }, "update_rq_clock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "update_rq_clock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;update_rq_clock" }, "p9pdu_readf#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_parse_header;p9pdu_readf" }, "__schedule#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule" }, "intel_pmu_disable_all#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "intel_pmu_disable_all" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;intel_pmu_disable_all" }, "x86_pmu_disable#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "x86_pmu_disable" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;x86_pmu_disable" }, "finish_task_switch.isra.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__radix_tree_replace#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_fid_create" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "__radix_tree_replace" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_fid_create;idr_alloc_u32;__radix_tree_replace" }, "idr_get_free#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_fid_create" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "idr_get_free" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_fid_create;idr_alloc_u32;idr_get_free" }, "p9_req_put#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - } - ], - "uid": "p9_req_put" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_req_put" }, "p9pdu_readf#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9pdu_readf;p9pdu_readf;p9pdu_readf" }, "kmem_cache_alloc#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc" }, "p9_fd_request#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request" }, "enqueue_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - } - ], - "uid": "enqueue_entity" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity" }, "memcpy_erms#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_parse_header;p9pdu_readf;pdu_read;memcpy_erms" }, "__raw_callee_save___pv_queued_spin_unlock#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__raw_callee_save___pv_queued_spin_unlock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__raw_callee_save___pv_queued_spin_unlock" }, "pick_next_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "pick_next_task_fair" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;pick_next_task_fair" }, "__fscache_acquire_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - } - ], - "uid": "__fscache_acquire_cookie" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie" }, "kmem_cache_alloc#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - }, - { - "func": "__fscache_acquire_cookie" - }, - { - "func": "fscache_alloc_cookie" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie;fscache_alloc_cookie;kmem_cache_alloc" }, "v9fs_stat2inode_dotl#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_stat2inode_dotl" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_stat2inode_dotl" }, "down_read#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - } - ], - "uid": "down_read" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;down_read" }, "__check_object_size#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "getname_flags.part.0" - }, - { - "func": "strncpy_from_user" - } - ], - "uid": "__check_object_size" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;getname_flags.part.0;strncpy_from_user;__check_object_size" }, "syscall_exit_to_user_mode#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "syscall_exit_to_user_mode" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode" }, "syscall_return_via_sysret#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - } - ], - "uid": "syscall_return_via_sysret" + "time": "???", + "uid": "ls;[unknown];lgetxattr;syscall_return_via_sysret" }, "kmem_cache_alloc#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "__GI___fstatat64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__do_sys_newfstatat" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;__GI___fstatat64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__do_sys_newfstatat;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc" }, "__strlen_avx2#1": { - "command": "ls", "time": "???", - "trace": [], - "uid": "__strlen_avx2" + "uid": "ls;__strlen_avx2" }, "entry_SYSCALL_64#1": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "statx" - } - ], - "uid": "entry_SYSCALL_64" + "uid": "ls;statx;entry_SYSCALL_64" }, "fsnotify_destroy_marks#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - } - ], - "uid": "fsnotify_destroy_marks" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;fsnotify_destroy_marks" }, "hook_inode_free_security#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - }, - { - "func": "security_inode_free" - } - ], - "uid": "hook_inode_free_security" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;security_inode_free;hook_inode_free_security" }, "__inode_wait_for_writeback#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "inode_wait_for_writeback" - } - ], - "uid": "__inode_wait_for_writeback" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;evict;inode_wait_for_writeback;__inode_wait_for_writeback" }, "fscache_invalidate_writes#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "fscache_invalidate_writes" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;fscache_invalidate_writes" }, "_raw_spin_unlock_irqrestore#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "_raw_spin_unlock_irqrestore" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;_raw_spin_unlock_irqrestore" }, "kmem_cache_alloc#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_client_prepare_req.part.0;kmem_cache_alloc" }, "__update_load_avg_cfs_rq#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_load_avg" - } - ], - "uid": "__update_load_avg_cfs_rq" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_load_avg;__update_load_avg_cfs_rq" }, "__intel_pmu_enable_all.isra.0#6": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "path_init#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - } - ], - "uid": "path_init" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;path_init" }, "d_alloc_parallel#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - } - ], - "uid": "d_alloc_parallel" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel" }, "get_obj_cgroup_from_current#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "__kmalloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;__kmalloc;get_obj_cgroup_from_current" }, "get_obj_cgroup_from_current#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "__d_add#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "d_splice_alias" - } - ], - "uid": "__d_add" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;d_splice_alias;__d_add" }, "p9_fd_request#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request" }, "__queue_work#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - } - ], - "uid": "__queue_work" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work" }, "try_to_wake_up#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "memcpy_erms#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_parse_header;p9pdu_readf;pdu_read;memcpy_erms" }, "__schedule#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule" }, "__intel_pmu_enable_all.isra.0#7": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "kmem_cache_alloc#6": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc" }, "p9_client_rpc#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "kmem_cache_alloc#7": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - }, - { - "func": "idr_get_free" - }, - { - "func": "radix_tree_node_alloc.constprop.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free;radix_tree_node_alloc.constprop.0;kmem_cache_alloc" }, "p9pdu_prepare#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - } - ], - "uid": "p9pdu_prepare" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_prepare" }, "p9_fd_request#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request" }, "kthread_is_per_cpu#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "is_cpu_allowed" - } - ], - "uid": "kthread_is_per_cpu" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed;kthread_is_per_cpu" }, "cpuacct_charge#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_curr" - } - ], - "uid": "cpuacct_charge" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr;cpuacct_charge" }, "dequeue_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "dequeue_task_fair" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;dequeue_task_fair" }, "__intel_pmu_enable_all.isra.0#8": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "set_next_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "pick_next_task_fair" - } - ], - "uid": "set_next_entity" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;pick_next_task_fair;set_next_entity" }, "v9fs_cache_inode_get_cookie#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_cache_inode_get_cookie" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie" }, "fscache_alloc_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - }, - { - "func": "__fscache_acquire_cookie" - } - ], - "uid": "fscache_alloc_cookie" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie;fscache_alloc_cookie" }, "v9fs_vfs_getattr_dotl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - } - ], - "uid": "v9fs_vfs_getattr_dotl" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl" }, "p9_client_rpc#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "enqueue_task_fair#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - } - ], - "uid": "enqueue_task_fair" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair" }, "x86_pmu_disable#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "x86_pmu_disable" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;x86_pmu_disable" }, "finish_task_switch.isra.0#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__intel_pmu_enable_all.isra.0#9": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "pdu_read#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "pdu_read" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9pdu_readf;p9pdu_readf;pdu_read" }, "__rseq_handle_notify_resume#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - } - ], - "uid": "__rseq_handle_notify_resume" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;__rseq_handle_notify_resume" }, "__intel_pmu_enable_all.isra.0#10": { - "command": "perf-exec", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;__intel_pmu_enable_all.isra.0" }, "send_call_function_single_ipi#0": { - "command": "perf-exec", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - }, - { - "func": "__hrtimer_run_queues" - }, - { - "func": "tick_sched_timer" - }, - { - "func": "update_process_times" - }, - { - "func": "smp_call_function_single_async" - }, - { - "func": "generic_exec_single" - } - ], - "uid": "send_call_function_single_ipi" + "time": "???", + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues;tick_sched_timer;update_process_times;smp_call_function_single_async;generic_exec_single;send_call_function_single_ipi" } }, "models": [], @@ -9661,7 +2477,9 @@ "aggregate_by": "total", "description": "total number of bogus stats", "value": [ - "val1", "val2", "val3" + "val1", + "val2", + "val3" ] }, { @@ -9671,7 +2489,11 @@ "aggregate_by": "min_count", "description": "min count of bogus stats", "value": [ - "val1", "val2", "val3", "val2", "val3" + "val1", + "val2", + "val3", + "val2", + "val3" ] }, { @@ -9681,7 +2503,10 @@ "aggregate_by": "max_count", "description": "max count of bogus stats", "value": [ - "val1", "val2", "val3", "val2" + "val1", + "val2", + "val3", + "val2" ] }, { @@ -9721,7 +2546,8 @@ "aggregate_by": "min", "description": "", "value": [ - 18511.379883, 17894.875698 + 18511.379883, + 17894.875698 ] }, { @@ -9731,7 +2557,8 @@ "aggregate_by": "max", "description": "", "value": [ - 956.36, 897.01 + 956.36, + 897.01 ] }, { @@ -9750,7 +2577,9 @@ "type": "time", "cmd": "ls", "exitcode": [ - 0, 0, 1 + 0, + 0, + 1 ], "workload": "..", "units": { @@ -9766,4 +2595,4 @@ } }, "postprocessors": [] -} +} \ No newline at end of file diff --git a/tests/profiles/diff_profiles/kperf-baseline.perf b/tests/profiles/diff_profiles/kperf-baseline.perf index d4c936dd3..1e3903fca 100644 --- a/tests/profiles/diff_profiles/kperf-baseline.perf +++ b/tests/profiles/diff_profiles/kperf-baseline.perf @@ -48,399 +48,40 @@ }, "resource_type_map": { "__intel_pmu_enable_all.isra.0#0": { - "command": "ls", "time": 1.610957145690918, - "trace": [], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#1": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9_client_readdir" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9_client_readdir;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#2": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_open" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_open;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#3": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#4": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#5": { - "command": "perf-exec", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;__intel_pmu_enable_all.isra.0" }, "finish_task_switch.isra.0#0": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__perf_event_task_sched_in#0": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "__raw_callee_save___pv_queued_spin_unlock#0": { - "command": "ls", "time": 1.610957145690918, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__raw_callee_save___pv_queued_spin_unlock" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__raw_callee_save___pv_queued_spin_unlock" } }, "models": [], diff --git a/tests/profiles/diff_profiles/kperf-target-stats-metadata.perf b/tests/profiles/diff_profiles/kperf-target-stats-metadata.perf index e6a9f043b..b8eff6de3 100644 --- a/tests/profiles/diff_profiles/kperf-target-stats-metadata.perf +++ b/tests/profiles/diff_profiles/kperf-target-stats-metadata.perf @@ -2,9070 +2,1886 @@ "resources": { "insert_work#0": { "amount": [ - 788525, 788525, 788525 + 788525, + 788525, + 788525 ] }, "dequeue_entity#0": { "amount": [ - 1081404, 1081404, 1081404 + 1081404, + 1081404, + 1081404 ] }, "__intel_pmu_enable_all.isra.0#0": { "amount": [ - 173, 173, 173 + 173, + 173, + 173 ] }, "__perf_event_task_sched_in#0": { "amount": [ - 301, 301, 301 + 301, + 301, + 301 ] }, "is_cpu_allowed#0": { "amount": [ - 198140, 198140, 198140 + 198140, + 198140, + 198140 ] }, "__intel_pmu_enable_all.isra.0#1": { "amount": [ - 49, 49, 49 + 49, + 49, + 49 ] }, "__intel_pmu_enable_all.isra.0#2": { "amount": [ - 90, 90, 90 + 90, + 90, + 90 ] }, "[libc.so.6]#0": { "amount": [ - 647901, 647901, 647901 + 647901, + 647901, + 647901 ] }, "[ls]#0": { "amount": [ - 407860, 407860, 407860 + 407860, + 407860, + 407860 ] }, "[ls]#1": { "amount": [ - 2065754, 2065754, 2065754 + 2065754, + 2065754, + 2065754 ] }, "path_getxattr#0": { "amount": [ - 386145, 386145, 386145 + 386145, + 386145, + 386145 ] }, "strncpy_from_user#0": { "amount": [ - 360662, 360662, 360662 + 360662, + 360662, + 360662 ] }, "get_cached_acl#0": { "amount": [ - 255439, 255439, 255439 + 255439, + 255439, + 255439 ] }, "dput#0": { "amount": [ - 822718, 822718, 822718 + 822718, + 822718, + 822718 ] }, "rcu_all_qs#0": { "amount": [ - 282305, 282305, 282305 + 282305, + 282305, + 282305 ] }, "__srcu_read_lock#0": { "amount": [ - 570839, 570839, 570839 + 570839, + 570839, + 570839 ] }, "__inode_wait_for_writeback#0": { "amount": [ - 252971, 252971, 252971 + 252971, + 252971, + 252971 ] }, "memcmp#0": { "amount": [ - 370723, 370723, 370723 + 370723, + 370723, + 370723 ] }, "p9_client_clunk#0": { "amount": [ - 304183, 304183, 304183 + 304183, + 304183, + 304183 ] }, "p9_client_rpc#0": { "amount": [ - 276682, 276682, 276682 + 276682, + 276682, + 276682 ] }, "update_curr#0": { "amount": [ - 626576, 626576, 626576 + 626576, + 626576, + 626576 ] }, "prepare_to_wait_event#0": { "amount": [ - 6759, 6759, 6759 + 6759, + 6759, + 6759 ] }, "__schedule#0": { "amount": [ - 303056, 303056, 303056 + 303056, + 303056, + 303056 ] }, "intel_pmu_disable_all#0": { "amount": [ - 436514, 436514, 436514 + 436514, + 436514, + 436514 ] }, "__perf_event_task_sched_in#1": { "amount": [ - 481539, 481539, 481539 + 481539, + 481539, + 481539 ] }, "__intel_pmu_enable_all.isra.0#3": { "amount": [ - 296791, 296791, 296791 + 296791, + 296791, + 296791 ] }, "radix_tree_delete_item#0": { "amount": [ - 309327, 309327, 309327 + 309327, + 309327, + 309327 ] }, "always_delete_dentry#0": { "amount": [ - 293584, 293584, 293584 + 293584, + 293584, + 293584 ] }, "get_obj_cgroup_from_current#0": { "amount": [ - 869880, 869880, 869880 + 869880, + 869880, + 869880 ] }, "kmem_cache_alloc#0": { "amount": [ - 410931, 410931, 410931 + 410931, + 410931, + 410931 ] }, "___slab_alloc.constprop.0#0": { "amount": [ - 261659, 261659, 261659 + 261659, + 261659, + 261659 ] }, "p9_fd_request#0": { "amount": [ - 387472, 387472, 387472 + 387472, + 387472, + 387472 ] }, "try_to_wake_up#0": { "amount": [ - 395389, 395389, 395389 + 395389, + 395389, + 395389 ] }, "_raw_spin_lock_irqsave#0": { "amount": [ - 436355, 436355, 436355 + 436355, + 436355, + 436355 ] }, "dequeue_entity#1": { "amount": [ - 322790, 322790, 322790 + 322790, + 322790, + 322790 ] }, "__perf_event_task_sched_in#2": { "amount": [ - 633, 633, 633 + 633, + 633, + 633 ] }, "__intel_pmu_enable_all.isra.0#4": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "_raw_spin_lock_irqsave#1": { "amount": [ - 267141, 267141, 267141 + 267141, + 267141, + 267141 ] }, "___slab_alloc.constprop.0#1": { "amount": [ - 283948, 283948, 283948 + 283948, + 283948, + 283948 ] }, "idr_remove#0": { "amount": [ - 284277, 284277, 284277 + 284277, + 284277, + 284277 ] }, "p9_client_rpc#1": { "amount": [ - 275066, 275066, 275066 + 275066, + 275066, + 275066 ] }, "memcpy_erms#0": { "amount": [ - 386198, 386198, 386198 + 386198, + 386198, + 386198 ] }, "p9pdu_vwritef#0": { "amount": [ - 377213, 377213, 377213 + 377213, + 377213, + 377213 ] }, "__radix_tree_lookup#0": { "amount": [ - 408615, 408615, 408615 + 408615, + 408615, + 408615 ] }, "idr_find#0": { "amount": [ - 302336, 302336, 302336 + 302336, + 302336, + 302336 ] }, "insert_work#1": { "amount": [ - 316781, 316781, 316781 + 316781, + 316781, + 316781 ] }, "_raw_spin_lock_irqsave#2": { "amount": [ - 398270, 398270, 398270 + 398270, + 398270, + 398270 ] }, "is_cpu_allowed#1": { "amount": [ - 373799, 373799, 373799 + 373799, + 373799, + 373799 ] }, "prepare_to_wait_event#1": { "amount": [ - 1176, 1176, 1176 + 1176, + 1176, + 1176 ] }, "finish_task_switch.isra.0#0": { "amount": [ - 353516, 353516, 353516 + 353516, + 353516, + 353516 ] }, "__intel_pmu_enable_all.isra.0#5": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "acct_account_cputime#0": { "amount": [ - 315318, 315318, 315318 + 315318, + 315318, + 315318 ] }, "kmem_cache_free.part.0#0": { "amount": [ - 296132, 296132, 296132 + 296132, + 296132, + 296132 ] }, "_raw_spin_lock_irqsave#3": { "amount": [ - 299381, 299381, 299381 + 299381, + 299381, + 299381 ] }, "__raw_callee_save___pv_queued_spin_unlock#0": { "amount": [ - 387460, 387460, 387460 + 387460, + 387460, + 387460 ] }, "p9pdu_readf#0": { "amount": [ - 287855, 287855, 287855 + 287855, + 287855, + 287855 ] }, "v9fs_cache_inode_get_cookie#0": { "amount": [ - 371528, 371528, 371528 + 371528, + 371528, + 371528 ] }, "v9fs_init_inode#0": { "amount": [ - 404430, 404430, 404430 + 404430, + 404430, + 404430 ] }, "v9fs_stat2inode_dotl#0": { "amount": [ - 394569, 394569, 394569 + 394569, + 394569, + 394569 ] }, "__virt_addr_valid#0": { "amount": [ - 278799, 278799, 278799 + 278799, + 278799, + 278799 ] }, "syscall_exit_to_user_mode#0": { "amount": [ - 793104, 793104, 793104 + 793104, + 793104, + 793104 ] }, "localeconv@@GLIBC_2.2.5#0": { "amount": [ - 989952, 989952, 989952 + 989952, + 989952, + 989952 ] }, "_int_malloc#0": { "amount": [ - 743942, 743942, 743942 + 743942, + 743942, + 743942 ] }, "__vdso_clock_gettime#0": { "amount": [ - 373595, 373595, 373595 + 373595, + 373595, + 373595 ] }, "_IO_old_init#0": { "amount": [ - 746955, 746955, 746955 + 746955, + 746955, + 746955 ] }, "kfree#0": { "amount": [ - 187300, 187300, 187300 + 187300, + 187300, + 187300 ] }, "memcpy_erms#1": { "amount": [ - 193571, 193571, 193571 + 193571, + 193571, + 193571 ] }, "try_to_wake_up#1": { "amount": [ - 837102, 837102, 837102 + 837102, + 837102, + 837102 ] }, "set_next_buddy#0": { "amount": [ - 371293, 371293, 371293 + 371293, + 371293, + 371293 ] }, "__libc_calloc#0": { "amount": [ - 1139742, 1139742, 1139742 + 1139742, + 1139742, + 1139742 ] }, "schedule#0": { "amount": [ - 105467, 105467, 105467 + 105467, + 105467, + 105467 ] }, "__hrtimer_run_queues#0": { "amount": [ - 110150, 110150, 110150 + 110150, + 110150, + 110150 ] }, "__strlen_avx2#0": { "amount": [ - 404165, 404165, 404165 + 404165, + 404165, + 404165 ] }, "cfree@GLIBC_2.2.5#0": { "amount": [ - 845160, 845160, 845160 + 845160, + 845160, + 845160 ] }, "entry_SYSCALL_64#0": { "amount": [ - 277906, 277906, 277906 + 277906, + 277906, + 277906 ] }, "strncpy_from_user#1": { "amount": [ - 744276, 744276, 744276 + 744276, + 744276, + 744276 ] }, "p9_client_rpc#2": { "amount": [ - 483062, 483062, 483062 + 483062, + 483062, + 483062 ] }, "_raw_spin_lock#0": { "amount": [ - 550419, 550419, 550419 + 550419, + 550419, + 550419 ] }, "try_to_wake_up#2": { "amount": [ - 328041, 328041, 328041 + 328041, + 328041, + 328041 ] }, "kmem_cache_free.part.0#1": { "amount": [ - 372972, 372972, 372972 + 372972, + 372972, + 372972 ] }, "__list_add_valid#0": { "amount": [ - 317189, 317189, 317189 + 317189, + 317189, + 317189 ] }, "_raw_spin_lock_irqsave#4": { "amount": [ - 626576, 626576, 626576 + 626576, + 626576, + 626576 ] }, "p9pdu_readf#1": { "amount": [ - 972738, 972738, 972738 + 972738, + 972738, + 972738 ] }, "memcpy_erms#2": { "amount": [ - 376612, 376612, 376612 + 376612, + 376612, + 376612 ] }, "finish_task_switch.isra.0#1": { "amount": [ - 270022, 270022, 270022 + 270022, + 270022, + 270022 ] }, "p9_fid_create#0": { "amount": [ - 457930, 457930, 457930 + 457930, + 457930, + 457930 ] }, "__list_del_entry_valid#0": { "amount": [ - 304152, 304152, 304152 + 304152, + 304152, + 304152 ] }, "wake_up_bit#0": { "amount": [ - 282569, 282569, 282569 + 282569, + 282569, + 282569 ] }, "wake_up_bit#1": { "amount": [ - 303405, 303405, 303405 + 303405, + 303405, + 303405 ] }, "_raw_spin_lock_irqsave#5": { "amount": [ - 396841, 396841, 396841 + 396841, + 396841, + 396841 ] }, "__queue_work#0": { "amount": [ - 443619, 443619, 443619 + 443619, + 443619, + 443619 ] }, "kthread_is_per_cpu#0": { "amount": [ - 289566, 289566, 289566 + 289566, + 289566, + 289566 ] }, "update_min_vruntime#0": { "amount": [ - 326866, 326866, 326866 + 326866, + 326866, + 326866 ] }, "clear_buddies#0": { "amount": [ - 356942, 356942, 356942 + 356942, + 356942, + 356942 ] }, "__list_del_entry_valid#1": { "amount": [ - 422374, 422374, 422374 + 422374, + 422374, + 422374 ] }, "memcg_slab_free_hook#0": { "amount": [ - 564812, 564812, 564812 + 564812, + 564812, + 564812 ] }, "__lookup_slow#0": { "amount": [ - 570839, 570839, 570839 + 570839, + 570839, + 570839 ] }, "get_obj_cgroup_from_current#1": { "amount": [ - 412936, 412936, 412936 + 412936, + 412936, + 412936 ] }, "__d_rehash#0": { "amount": [ - 374824, 374824, 374824 + 374824, + 374824, + 374824 ] }, "idr_get_free#0": { "amount": [ - 377157, 377157, 377157 + 377157, + 377157, + 377157 ] }, "kmem_cache_alloc#1": { "amount": [ - 288831, 288831, 288831 + 288831, + 288831, + 288831 ] }, "___slab_alloc.constprop.0#2": { "amount": [ - 834354, 834354, 834354 + 834354, + 834354, + 834354 ] }, "enqueue_task_fair#0": { "amount": [ - 324380, 324380, 324380 + 324380, + 324380, + 324380 ] }, "ttwu_do_wakeup#0": { "amount": [ - 263927, 263927, 263927 + 263927, + 263927, + 263927 ] }, "update_rq_clock#0": { "amount": [ - 297309, 297309, 297309 + 297309, + 297309, + 297309 ] }, "p9pdu_readf#2": { "amount": [ - 287020, 287020, 287020 + 287020, + 287020, + 287020 ] }, "__schedule#1": { "amount": [ - 402974, 402974, 402974 + 402974, + 402974, + 402974 ] }, "intel_pmu_disable_all#1": { "amount": [ - 270780, 270780, 270780 + 270780, + 270780, + 270780 ] }, "x86_pmu_disable#0": { "amount": [ - 578521, 578521, 578521 + 578521, + 578521, + 578521 ] }, "finish_task_switch.isra.0#2": { "amount": [ - 264024, 264024, 264024 + 264024, + 264024, + 264024 ] }, "__radix_tree_replace#0": { "amount": [ - 671442, 671442, 671442 + 671442, + 671442, + 671442 ] }, "idr_get_free#1": { "amount": [ - 300146, 300146, 300146 + 300146, + 300146, + 300146 ] }, "p9_req_put#0": { "amount": [ - 701944, 701944, 701944 + 701944, + 701944, + 701944 ] }, "p9pdu_readf#3": { "amount": [ - 315014, 315014, 315014 + 315014, + 315014, + 315014 ] }, "kmem_cache_alloc#2": { "amount": [ - 457930, 457930, 457930 + 457930, + 457930, + 457930 ] }, "p9_fd_request#1": { "amount": [ - 394836, 394836, 394836 + 394836, + 394836, + 394836 ] }, "enqueue_entity#0": { "amount": [ - 516319, 516319, 516319 + 516319, + 516319, + 516319 ] }, "memcpy_erms#3": { "amount": [ - 400593, 400593, 400593 + 400593, + 400593, + 400593 ] }, "__raw_callee_save___pv_queued_spin_unlock#1": { "amount": [ - 378607, 378607, 378607 + 378607, + 378607, + 378607 ] }, "pick_next_task_fair#0": { "amount": [ - 285677, 285677, 285677 + 285677, + 285677, + 285677 ] }, "__fscache_acquire_cookie#0": { "amount": [ - 504914, 504914, 504914 + 504914, + 504914, + 504914 ] }, "kmem_cache_alloc#3": { "amount": [ - 439744, 439744, 439744 + 439744, + 439744, + 439744 ] }, "v9fs_stat2inode_dotl#1": { "amount": [ - 339890, 339890, 339890 + 339890, + 339890, + 339890 ] }, "down_read#0": { "amount": [ - 410430, 410430, 410430 + 410430, + 410430, + 410430 ] }, "__check_object_size#0": { "amount": [ - 833353, 833353, 833353 + 833353, + 833353, + 833353 ] }, "syscall_exit_to_user_mode#1": { "amount": [ - 303686, 303686, 303686 + 303686, + 303686, + 303686 ] }, "syscall_return_via_sysret#0": { "amount": [ - 841935, 841935, 841935 + 841935, + 841935, + 841935 ] }, "kmem_cache_alloc#4": { "amount": [ - 110150, 110150, 110150 + 110150, + 110150, + 110150 ] }, "__strlen_avx2#1": { "amount": [ - 972738, 972738, 972738 + 972738, + 972738, + 972738 ] }, "entry_SYSCALL_64#1": { "amount": [ - 405507, 405507, 405507 + 405507, + 405507, + 405507 ] }, "fsnotify_destroy_marks#0": { "amount": [ - 268089, 268089, 268089 + 268089, + 268089, + 268089 ] }, "hook_inode_free_security#0": { "amount": [ - 366998, 366998, 366998 + 366998, + 366998, + 366998 ] }, "__inode_wait_for_writeback#1": { "amount": [ - 274702, 274702, 274702 + 274702, + 274702, + 274702 ] }, "fscache_invalidate_writes#0": { "amount": [ - 317720, 317720, 317720 + 317720, + 317720, + 317720 ] }, "_raw_spin_unlock_irqrestore#0": { "amount": [ - 424749, 424749, 424749 + 424749, + 424749, + 424749 ] }, "kmem_cache_alloc#5": { "amount": [ - 414115, 414115, 414115 + 414115, + 414115, + 414115 ] }, "__update_load_avg_cfs_rq#0": { "amount": [ - 428196, 428196, 428196 + 428196, + 428196, + 428196 ] }, "__intel_pmu_enable_all.isra.0#6": { "amount": [ - 25, 25, 25 + 25, + 25, + 25 ] }, "path_init#0": { "amount": [ - 680390, 680390, 680390 + 680390, + 680390, + 680390 ] }, "d_alloc_parallel#0": { "amount": [ - 394528, 394528, 394528 + 394528, + 394528, + 394528 ] }, "get_obj_cgroup_from_current#2": { "amount": [ - 671442, 671442, 671442 + 671442, + 671442, + 671442 ] }, "get_obj_cgroup_from_current#3": { "amount": [ - 421121, 421121, 421121 + 421121, + 421121, + 421121 ] }, "__d_add#0": { "amount": [ - 407939, 407939, 407939 + 407939, + 407939, + 407939 ] }, "p9_fd_request#2": { "amount": [ - 240152, 240152, 240152 + 240152, + 240152, + 240152 ] }, "__queue_work#1": { "amount": [ - 258055, 258055, 258055 + 258055, + 258055, + 258055 ] }, "try_to_wake_up#3": { "amount": [ - 369833, 369833, 369833 + 369833, + 369833, + 369833 ] }, "memcpy_erms#4": { "amount": [ - 743942, 743942, 743942 + 743942, + 743942, + 743942 ] }, "__schedule#2": { "amount": [ - 165631, 165631, 165631 + 165631, + 165631, + 165631 ] }, "__intel_pmu_enable_all.isra.0#7": { "amount": [ - 9, 9, 9 + 9, + 9, + 9 ] }, "kmem_cache_alloc#6": { "amount": [ - 260415, 260415, 260415 + 260415, + 260415, + 260415 ] }, "p9_client_rpc#3": { "amount": [ - 430749, 430749, 430749 + 430749, + 430749, + 430749 ] }, "kmem_cache_alloc#7": { "amount": [ - 269417, 269417, 269417 + 269417, + 269417, + 269417 ] }, "p9pdu_prepare#0": { "amount": [ - 355513, 355513, 355513 + 355513, + 355513, + 355513 ] }, "p9_fd_request#3": { "amount": [ - 345879, 345879, 345879 + 345879, + 345879, + 345879 ] }, "kthread_is_per_cpu#1": { "amount": [ - 491649, 491649, 491649 + 491649, + 491649, + 491649 ] }, "cpuacct_charge#0": { "amount": [ - 392002, 392002, 392002 + 392002, + 392002, + 392002 ] }, "dequeue_task_fair#0": { "amount": [ - 304152, 304152, 304152 + 304152, + 304152, + 304152 ] }, "__intel_pmu_enable_all.isra.0#8": { "amount": [ - 9, 9, 9 + 9, + 9, + 9 ] }, "set_next_entity#0": { "amount": [ - 426821, 426821, 426821 + 426821, + 426821, + 426821 ] }, "v9fs_cache_inode_get_cookie#1": { "amount": [ - 721208, 721208, 721208 + 721208, + 721208, + 721208 ] }, "fscache_alloc_cookie#0": { "amount": [ - 387460, 387460, 387460 + 387460, + 387460, + 387460 ] }, "v9fs_vfs_getattr_dotl#0": { "amount": [ - 401636, 401636, 401636 + 401636, + 401636, + 401636 ] }, "p9_client_rpc#4": { "amount": [ - 289951, 289951, 289951 + 289951, + 289951, + 289951 ] }, "enqueue_task_fair#1": { "amount": [ - 564812, 564812, 564812 + 564812, + 564812, + 564812 ] }, "x86_pmu_disable#1": { "amount": [ - 282569, 282569, 282569 + 282569, + 282569, + 282569 ] }, "finish_task_switch.isra.0#3": { "amount": [ - 312217, 312217, 312217 + 312217, + 312217, + 312217 ] }, "__intel_pmu_enable_all.isra.0#9": { "amount": [ - 16, 16, 16 + 16, + 16, + 16 ] }, "pdu_read#0": { "amount": [ - 331233, 331233, 331233 + 331233, + 331233, + 331233 ] }, "__rseq_handle_notify_resume#0": { "amount": [ - 276682, 276682, 276682 + 276682, + 276682, + 276682 ] }, "__intel_pmu_enable_all.isra.0#10": { "amount": [ - 1, 1, 1 + 1, + 1, + 1 ] }, "send_call_function_single_ipi#0": { "amount": [ - 8462, 8462, 8462 + 8462, + 8462, + 8462 ] } }, "resource_type_map": { "insert_work#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "insert_work" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;insert_work" }, "dequeue_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - } - ], - "uid": "dequeue_entity" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity" }, "__intel_pmu_enable_all.isra.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__perf_event_task_sched_in#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "is_cpu_allowed#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "is_cpu_allowed" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed" }, "__intel_pmu_enable_all.isra.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[ls]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[ls];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "[libc.so.6]#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "[libc.so.6]" + "uid": "ls;[unknown];[libc.so.6]" }, "[ls]#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "[ls]" + "uid": "ls;[unknown];[ls]" }, "[ls]#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - } - ], - "uid": "[ls]" + "time": "???", + "uid": "ls;[unknown];[ls];[ls]" }, "path_getxattr#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "path_getxattr" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr" }, "strncpy_from_user#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - } - ], - "uid": "strncpy_from_user" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;strncpy_from_user" }, "get_cached_acl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_get_acl" - } - ], - "uid": "get_cached_acl" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_get_acl;get_cached_acl" }, "dput#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - } - ], - "uid": "dput" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput" }, "rcu_all_qs#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__cond_resched" - } - ], - "uid": "rcu_all_qs" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__cond_resched;rcu_all_qs" }, "__srcu_read_lock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - }, - { - "func": "fsnotify_destroy_marks" - }, - { - "func": "fsnotify_grab_connector" - } - ], - "uid": "__srcu_read_lock" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;fsnotify_destroy_marks;fsnotify_grab_connector;__srcu_read_lock" }, "__inode_wait_for_writeback#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "inode_wait_for_writeback" - } - ], - "uid": "__inode_wait_for_writeback" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;inode_wait_for_writeback;__inode_wait_for_writeback" }, "memcmp#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "memcmp" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;memcmp" }, "p9_client_clunk#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - } - ], - "uid": "p9_client_clunk" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk" }, "p9_client_rpc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc" }, "update_curr#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - } - ], - "uid": "update_curr" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr" }, "prepare_to_wait_event#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "prepare_to_wait_event" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;prepare_to_wait_event" }, "__schedule#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule" }, "intel_pmu_disable_all#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "intel_pmu_disable_all" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;intel_pmu_disable_all" }, "__perf_event_task_sched_in#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "__intel_pmu_enable_all.isra.0#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "radix_tree_delete_item#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "radix_tree_delete_item" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;radix_tree_delete_item" }, "always_delete_dentry#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - } - ], - "uid": "always_delete_dentry" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;always_delete_dentry" }, "get_obj_cgroup_from_current#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "kmem_cache_alloc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - }, - { - "func": "idr_get_free" - }, - { - "func": "radix_tree_node_alloc.constprop.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free;radix_tree_node_alloc.constprop.0;kmem_cache_alloc" }, "___slab_alloc.constprop.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0" }, "p9_fd_request#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request" }, "try_to_wake_up#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "_raw_spin_lock_irqsave#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "prepare_to_wait_event" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;prepare_to_wait_event;_raw_spin_lock_irqsave" }, "dequeue_entity#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - } - ], - "uid": "dequeue_entity" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity" }, "__perf_event_task_sched_in#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "__intel_pmu_enable_all.isra.0#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "_raw_spin_lock_irqsave#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "___slab_alloc.constprop.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc;___slab_alloc.constprop.0" }, "idr_remove#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "idr_remove" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;idr_remove" }, "p9_client_rpc#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "memcpy_erms#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9pdu_vwritef" - }, - { - "func": "pdu_write" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_vwritef;pdu_write;memcpy_erms" }, "p9pdu_vwritef#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9pdu_writef" - } - ], - "uid": "p9pdu_vwritef" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_writef;p9pdu_vwritef" }, "__radix_tree_lookup#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "__radix_tree_lookup" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;__radix_tree_lookup" }, "idr_find#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "idr_find" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;idr_find" }, "insert_work#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "insert_work" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;insert_work" }, "_raw_spin_lock_irqsave#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;_raw_spin_lock_irqsave" }, "is_cpu_allowed#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "is_cpu_allowed" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed" }, "prepare_to_wait_event#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "prepare_to_wait_event" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;prepare_to_wait_event" }, "finish_task_switch.isra.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__intel_pmu_enable_all.isra.0#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "acct_account_cputime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - }, - { - "func": "__hrtimer_run_queues" - }, - { - "func": "tick_sched_timer" - }, - { - "func": "update_process_times" - } - ], - "uid": "acct_account_cputime" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues;tick_sched_timer;update_process_times;acct_account_cputime" }, "kmem_cache_free.part.0#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - } - ], - "uid": "kmem_cache_free.part.0" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0" }, "_raw_spin_lock_irqsave#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "__raw_callee_save___pv_queued_spin_unlock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "_raw_spin_unlock_irqrestore" - } - ], - "uid": "__raw_callee_save___pv_queued_spin_unlock" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_req_put;kmem_cache_free.part.0;_raw_spin_unlock_irqrestore;__raw_callee_save___pv_queued_spin_unlock" }, "p9pdu_readf#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9pdu_readf;p9pdu_readf" }, "v9fs_cache_inode_get_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_cache_inode_get_cookie" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie" }, "v9fs_init_inode#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_init_inode" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_init_inode" }, "v9fs_stat2inode_dotl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_stat2inode_dotl" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_stat2inode_dotl" }, "__virt_addr_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "getname_flags.part.0" - }, - { - "func": "strncpy_from_user" - }, - { - "func": "__check_object_size" - } - ], - "uid": "__virt_addr_valid" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;getname_flags.part.0;strncpy_from_user;__check_object_size;__virt_addr_valid" }, "syscall_exit_to_user_mode#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - }, - { - "func": "getxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "syscall_exit_to_user_mode" + "time": "???", + "uid": "ls;[unknown];[ls];getxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode" }, "localeconv@@GLIBC_2.2.5#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[ls]" - } - ], - "uid": "localeconv@@GLIBC_2.2.5" + "time": "???", + "uid": "ls;[unknown];[ls];localeconv@@GLIBC_2.2.5" }, "_int_malloc#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - } - ], - "uid": "_int_malloc" + "time": "???", + "uid": "ls;[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];_int_malloc" }, "__vdso_clock_gettime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "[unknown]" - }, - { - "func": "clock_gettime@@GLIBC_2.17" - } - ], - "uid": "__vdso_clock_gettime" + "time": "???", + "uid": "ls;[unknown];[unknown];[unknown];clock_gettime@@GLIBC_2.17;__vdso_clock_gettime" }, "_IO_old_init#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "_IO_old_init" + "uid": "ls;[unknown];_IO_old_init" }, "kfree#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___getdents64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9dirent_read" - } - ], - "uid": "kfree" + "time": "???", + "uid": "ls;[unknown];__GI___getdents64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9dirent_read;kfree" }, "memcpy_erms#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___getdents64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9dirent_read" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];__GI___getdents64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9dirent_read;p9pdu_readf;p9pdu_readf;pdu_read;memcpy_erms" }, "try_to_wake_up#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__GI___libc_write" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "ksys_write" - }, - { - "func": "vfs_write" - }, - { - "func": "new_sync_write" - }, - { - "func": "file_tty_write.isra.0" - }, - { - "func": "n_tty_write" - }, - { - "func": "tty_insert_flip_string_and_push_buffer" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;new_sync_write;file_tty_write.isra.0;n_tty_write;tty_insert_flip_string_and_push_buffer;queue_work_on;__queue_work;try_to_wake_up" }, "set_next_buddy#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__close_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_wakeup" - }, - { - "func": "check_preempt_curr" - }, - { - "func": "check_preempt_wakeup" - } - ], - "uid": "set_next_buddy" + "time": "???", + "uid": "ls;[unknown];__close_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_wakeup;check_preempt_curr;check_preempt_wakeup;set_next_buddy" }, "__libc_calloc#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "__libc_calloc" + "uid": "ls;[unknown];__libc_calloc" }, "schedule#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__open64_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "schedule" + "time": "???", + "uid": "ls;[unknown];__open64_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule" }, "__hrtimer_run_queues#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "__open64_nocancel" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - } - ], - "uid": "__hrtimer_run_queues" + "time": "???", + "uid": "ls;[unknown];__open64_nocancel;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues" }, "__strlen_avx2#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "__strlen_avx2" + "uid": "ls;[unknown];__strlen_avx2" }, "cfree@GLIBC_2.2.5#0": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "[unknown]" - } - ], - "uid": "cfree@GLIBC_2.2.5" + "uid": "ls;[unknown];cfree@GLIBC_2.2.5" }, "entry_SYSCALL_64#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - } - ], - "uid": "entry_SYSCALL_64" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64" }, "strncpy_from_user#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - } - ], - "uid": "strncpy_from_user" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;strncpy_from_user" }, "p9_client_rpc#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc" }, "_raw_spin_lock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - } - ], - "uid": "_raw_spin_lock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;_raw_spin_lock" }, "try_to_wake_up#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "kmem_cache_free.part.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - } - ], - "uid": "kmem_cache_free.part.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0" }, "__list_add_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "__list_add_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;__list_add_valid" }, "_raw_spin_lock_irqsave#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - }, - { - "func": "__slab_free.isra.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9_req_put;kmem_cache_free.part.0;__slab_free.isra.0;_raw_spin_lock_irqsave" }, "p9pdu_readf#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9pdu_readf" }, "memcpy_erms#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;p9pdu_readf;pdu_read;memcpy_erms" }, "finish_task_switch.isra.0#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "p9_fid_create#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "getxattr" - }, - { - "func": "vfs_getxattr" - }, - { - "func": "v9fs_xattr_handler_get" - }, - { - "func": "v9fs_fid_xattr_get" - }, - { - "func": "p9_client_xattrwalk" - } - ], - "uid": "p9_fid_create" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;getxattr;vfs_getxattr;v9fs_xattr_handler_get;v9fs_fid_xattr_get;p9_client_xattrwalk;p9_fid_create" }, "__list_del_entry_valid#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - } - ], - "uid": "__list_del_entry_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;__list_del_entry_valid" }, "wake_up_bit#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "wake_up_bit" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;wake_up_bit" }, "wake_up_bit#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - } - ], - "uid": "wake_up_bit" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;wake_up_bit" }, "_raw_spin_lock_irqsave#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - }, - { - "func": "___slab_alloc.constprop.0" - } - ], - "uid": "_raw_spin_lock_irqsave" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0;_raw_spin_lock_irqsave" }, "__queue_work#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - } - ], - "uid": "__queue_work" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work" }, "kthread_is_per_cpu#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "is_cpu_allowed" - } - ], - "uid": "kthread_is_per_cpu" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed;kthread_is_per_cpu" }, "update_min_vruntime#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_curr" - } - ], - "uid": "update_min_vruntime" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr;update_min_vruntime" }, "clear_buddies#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "dequeue_task_fair" - }, - { - "func": "dequeue_entity" - } - ], - "uid": "clear_buddies" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;dequeue_task_fair;dequeue_entity;clear_buddies" }, "__list_del_entry_valid#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "pick_next_task_fair" - } - ], - "uid": "__list_del_entry_valid" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;pick_next_task_fair;__list_del_entry_valid" }, "memcg_slab_free_hook#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_req_put" - }, - { - "func": "kmem_cache_free.part.0" - } - ], - "uid": "memcg_slab_free_hook" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_req_put;kmem_cache_free.part.0;memcg_slab_free_hook" }, "__lookup_slow#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - } - ], - "uid": "__lookup_slow" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow" }, "get_obj_cgroup_from_current#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "__d_rehash#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "d_splice_alias" - }, - { - "func": "__d_add" - } - ], - "uid": "__d_rehash" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;d_splice_alias;__d_add;__d_rehash" }, "idr_get_free#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "idr_get_free" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free" }, "kmem_cache_alloc#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc" }, "___slab_alloc.constprop.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "___slab_alloc.constprop.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc;___slab_alloc.constprop.0" }, "enqueue_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - } - ], - "uid": "enqueue_task_fair" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair" }, "ttwu_do_wakeup#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "ttwu_do_wakeup" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_wakeup" }, "update_rq_clock#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - } - ], - "uid": "update_rq_clock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;update_rq_clock" }, "p9pdu_readf#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_parse_header;p9pdu_readf" }, "__schedule#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule" }, "intel_pmu_disable_all#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "intel_pmu_disable_all" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;intel_pmu_disable_all" }, "x86_pmu_disable#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "x86_pmu_disable" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;x86_pmu_disable" }, "finish_task_switch.isra.0#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__radix_tree_replace#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_fid_create" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "__radix_tree_replace" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_fid_create;idr_alloc_u32;__radix_tree_replace" }, "idr_get_free#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_fid_create" - }, - { - "func": "idr_alloc_u32" - } - ], - "uid": "idr_get_free" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_fid_create;idr_alloc_u32;idr_get_free" }, "p9_req_put#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - } - ], - "uid": "p9_req_put" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_req_put" }, "p9pdu_readf#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "p9pdu_readf" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9pdu_readf;p9pdu_readf;p9pdu_readf" }, "kmem_cache_alloc#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc" }, "p9_fd_request#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request" }, "enqueue_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - } - ], - "uid": "enqueue_entity" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity" }, "memcpy_erms#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_parse_header;p9pdu_readf;pdu_read;memcpy_erms" }, "__raw_callee_save___pv_queued_spin_unlock#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__raw_callee_save___pv_queued_spin_unlock" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__raw_callee_save___pv_queued_spin_unlock" }, "pick_next_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "pick_next_task_fair" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;pick_next_task_fair" }, "__fscache_acquire_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - } - ], - "uid": "__fscache_acquire_cookie" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie" }, "kmem_cache_alloc#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - }, - { - "func": "__fscache_acquire_cookie" - }, - { - "func": "fscache_alloc_cookie" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie;fscache_alloc_cookie;kmem_cache_alloc" }, "v9fs_stat2inode_dotl#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_stat2inode_dotl" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_stat2inode_dotl" }, "down_read#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - } - ], - "uid": "down_read" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;filename_lookup;path_lookupat;walk_component;down_read" }, "__check_object_size#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "path_getxattr" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "getname_flags.part.0" - }, - { - "func": "strncpy_from_user" - } - ], - "uid": "__check_object_size" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;path_getxattr;user_path_at_empty;getname_flags.part.0;strncpy_from_user;__check_object_size" }, "syscall_exit_to_user_mode#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - } - ], - "uid": "syscall_exit_to_user_mode" + "time": "???", + "uid": "ls;[unknown];lgetxattr;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode" }, "syscall_return_via_sysret#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "lgetxattr" - } - ], - "uid": "syscall_return_via_sysret" + "time": "???", + "uid": "ls;[unknown];lgetxattr;syscall_return_via_sysret" }, "kmem_cache_alloc#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "__GI___fstatat64" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__do_sys_newfstatat" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "p9_fcall_init" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;__GI___fstatat64;entry_SYSCALL_64_after_hwframe;do_syscall_64;__do_sys_newfstatat;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9_fcall_init;kmem_cache_alloc" }, "__strlen_avx2#1": { - "command": "ls", "time": "???", - "trace": [], - "uid": "__strlen_avx2" + "uid": "ls;__strlen_avx2" }, "entry_SYSCALL_64#1": { - "command": "ls", "time": "???", - "trace": [ - { - "func": "statx" - } - ], - "uid": "entry_SYSCALL_64" + "uid": "ls;statx;entry_SYSCALL_64" }, "fsnotify_destroy_marks#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - } - ], - "uid": "fsnotify_destroy_marks" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;fsnotify_destroy_marks" }, "hook_inode_free_security#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "destroy_inode" - }, - { - "func": "__destroy_inode" - }, - { - "func": "security_inode_free" - } - ], - "uid": "hook_inode_free_security" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;destroy_inode;__destroy_inode;security_inode_free;hook_inode_free_security" }, "__inode_wait_for_writeback#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "inode_wait_for_writeback" - } - ], - "uid": "__inode_wait_for_writeback" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;evict;inode_wait_for_writeback;__inode_wait_for_writeback" }, "fscache_invalidate_writes#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "evict" - }, - { - "func": "v9fs_evict_inode" - }, - { - "func": "v9fs_cache_inode_put_cookie" - }, - { - "func": "__fscache_relinquish_cookie" - }, - { - "func": "__fscache_disable_cookie" - } - ], - "uid": "fscache_invalidate_writes" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;evict;v9fs_evict_inode;v9fs_cache_inode_put_cookie;__fscache_relinquish_cookie;__fscache_disable_cookie;fscache_invalidate_writes" }, "_raw_spin_unlock_irqrestore#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - } - ], - "uid": "_raw_spin_unlock_irqrestore" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;_raw_spin_unlock_irqrestore" }, "kmem_cache_alloc#5": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_client_prepare_req.part.0;kmem_cache_alloc" }, "__update_load_avg_cfs_rq#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_load_avg" - } - ], - "uid": "__update_load_avg_cfs_rq" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_load_avg;__update_load_avg_cfs_rq" }, "__intel_pmu_enable_all.isra.0#6": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "path_put" - }, - { - "func": "dput" - }, - { - "func": "__dentry_kill" - }, - { - "func": "v9fs_dentry_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;path_put;dput;__dentry_kill;v9fs_dentry_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "path_init#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - } - ], - "uid": "path_init" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;path_init" }, "d_alloc_parallel#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - } - ], - "uid": "d_alloc_parallel" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel" }, "get_obj_cgroup_from_current#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "__kmalloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;__kmalloc;get_obj_cgroup_from_current" }, "get_obj_cgroup_from_current#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "d_alloc_parallel" - }, - { - "func": "d_alloc" - }, - { - "func": "__d_alloc" - }, - { - "func": "kmem_cache_alloc" - } - ], - "uid": "get_obj_cgroup_from_current" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;d_alloc_parallel;d_alloc;__d_alloc;kmem_cache_alloc;get_obj_cgroup_from_current" }, "__d_add#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "d_splice_alias" - } - ], - "uid": "__d_add" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;d_splice_alias;__d_add" }, "p9_fd_request#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request" }, "__queue_work#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - } - ], - "uid": "__queue_work" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work" }, "try_to_wake_up#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - } - ], - "uid": "try_to_wake_up" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up" }, "memcpy_erms#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_parse_header" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "pdu_read" - } - ], - "uid": "memcpy_erms" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;p9_parse_header;p9pdu_readf;pdu_read;memcpy_erms" }, "__schedule#2": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - } - ], - "uid": "__schedule" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule" }, "__intel_pmu_enable_all.isra.0#7": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "kmem_cache_alloc#6": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "iget5_locked" - }, - { - "func": "alloc_inode" - }, - { - "func": "v9fs_alloc_inode" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;iget5_locked;alloc_inode;v9fs_alloc_inode;kmem_cache_alloc" }, "p9_client_rpc#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "kmem_cache_alloc#7": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - }, - { - "func": "idr_alloc" - }, - { - "func": "idr_alloc_u32" - }, - { - "func": "idr_get_free" - }, - { - "func": "radix_tree_node_alloc.constprop.0" - } - ], - "uid": "kmem_cache_alloc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;idr_alloc;idr_alloc_u32;idr_get_free;radix_tree_node_alloc.constprop.0;kmem_cache_alloc" }, "p9pdu_prepare#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_client_prepare_req.part.0" - } - ], - "uid": "p9pdu_prepare" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_client_prepare_req.part.0;p9pdu_prepare" }, "p9_fd_request#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "p9_fd_request" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request" }, "kthread_is_per_cpu#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "is_cpu_allowed" - } - ], - "uid": "kthread_is_per_cpu" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;is_cpu_allowed;kthread_is_per_cpu" }, "cpuacct_charge#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - }, - { - "func": "enqueue_task_fair" - }, - { - "func": "enqueue_entity" - }, - { - "func": "update_curr" - } - ], - "uid": "cpuacct_charge" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair;enqueue_entity;update_curr;cpuacct_charge" }, "dequeue_task_fair#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "dequeue_task_fair" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;dequeue_task_fair" }, "__intel_pmu_enable_all.isra.0#8": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "set_next_entity#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "pick_next_task_fair" - } - ], - "uid": "set_next_entity" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;pick_next_task_fair;set_next_entity" }, "v9fs_cache_inode_get_cookie#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - } - ], - "uid": "v9fs_cache_inode_get_cookie" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie" }, "fscache_alloc_cookie#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "walk_component" - }, - { - "func": "__lookup_slow" - }, - { - "func": "v9fs_vfs_lookup" - }, - { - "func": "v9fs_inode_from_fid_dotl" - }, - { - "func": "v9fs_cache_inode_get_cookie" - }, - { - "func": "__fscache_acquire_cookie" - } - ], - "uid": "fscache_alloc_cookie" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;walk_component;__lookup_slow;v9fs_vfs_lookup;v9fs_inode_from_fid_dotl;v9fs_cache_inode_get_cookie;__fscache_acquire_cookie;fscache_alloc_cookie" }, "v9fs_vfs_getattr_dotl#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - } - ], - "uid": "v9fs_vfs_getattr_dotl" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl" }, "p9_client_rpc#4": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - } - ], - "uid": "p9_client_rpc" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc" }, "enqueue_task_fair#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "p9_fd_request" - }, - { - "func": "queue_work_on" - }, - { - "func": "__queue_work" - }, - { - "func": "try_to_wake_up" - }, - { - "func": "ttwu_do_activate" - } - ], - "uid": "enqueue_task_fair" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;p9_fd_request;queue_work_on;__queue_work;try_to_wake_up;ttwu_do_activate;enqueue_task_fair" }, "x86_pmu_disable#1": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "__perf_event_task_sched_out" - } - ], - "uid": "x86_pmu_disable" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;__perf_event_task_sched_out;x86_pmu_disable" }, "finish_task_switch.isra.0#3": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__intel_pmu_enable_all.isra.0#9": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "pdu_read#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9pdu_readf" - }, - { - "func": "p9pdu_readf" - } - ], - "uid": "pdu_read" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9pdu_readf;p9pdu_readf;pdu_read" }, "__rseq_handle_notify_resume#0": { - "command": "ls", - "time": "???", - "trace": [ - { - "func": "statx" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - } - ], - "uid": "__rseq_handle_notify_resume" + "time": "???", + "uid": "ls;statx;entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;__rseq_handle_notify_resume" }, "__intel_pmu_enable_all.isra.0#10": { - "command": "perf-exec", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "time": "???", + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;__intel_pmu_enable_all.isra.0" }, "send_call_function_single_ipi#0": { - "command": "perf-exec", - "time": "???", - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - }, - { - "func": "__hrtimer_run_queues" - }, - { - "func": "tick_sched_timer" - }, - { - "func": "update_process_times" - }, - { - "func": "smp_call_function_single_async" - }, - { - "func": "generic_exec_single" - } - ], - "uid": "send_call_function_single_ipi" + "time": "???", + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues;tick_sched_timer;update_process_times;smp_call_function_single_async;generic_exec_single;send_call_function_single_ipi" } }, "models": [], @@ -9656,7 +2472,9 @@ "aggregate_by": "total", "description": "total number of bogus stats", "value": [ - "val1", "val2", "val3" + "val1", + "val2", + "val3" ] }, { @@ -9666,7 +2484,11 @@ "aggregate_by": "min_count", "description": "min count of bogus stats", "value": [ - "val1", "val2", "val3", "val2", "val3" + "val1", + "val2", + "val3", + "val2", + "val3" ] }, { @@ -9676,7 +2498,10 @@ "aggregate_by": "max_count", "description": "max count of bogus stats", "value": [ - "val1", "val2", "val3", "val2" + "val1", + "val2", + "val3", + "val2" ] }, { @@ -9706,7 +2531,8 @@ "aggregate_by": "min", "description": "", "value": [ - 18311.379883, 16894.875698 + 18311.379883, + 16894.875698 ] }, { @@ -9716,7 +2542,8 @@ "aggregate_by": "max", "description": "", "value": [ - "956.36", "897.01" + "956.36", + "897.01" ] }, { @@ -9743,7 +2570,9 @@ "type": "time", "cmd": "ls", "exitcode": [ - 0, 0, 1 + 0, + 0, + 1 ], "workload": "..", "units": { @@ -9759,4 +2588,4 @@ } }, "postprocessors": [] -} +} \ No newline at end of file diff --git a/tests/profiles/diff_profiles/kperf-target.perf b/tests/profiles/diff_profiles/kperf-target.perf index 803e4988e..750a283a7 100644 --- a/tests/profiles/diff_profiles/kperf-target.perf +++ b/tests/profiles/diff_profiles/kperf-target.perf @@ -83,686 +83,68 @@ }, "resource_type_map": { "__legitimize_mnt#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "user_path_at_empty" - }, - { - "func": "filename_lookup" - }, - { - "func": "path_lookupat" - }, - { - "func": "complete_walk" - }, - { - "func": "try_to_unlazy" - }, - { - "func": "__legitimize_path" - } - ], - "uid": "__legitimize_mnt" + "uid": "ls;[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;user_path_at_empty;filename_lookup;path_lookupat;complete_walk;try_to_unlazy;__legitimize_path;__legitimize_mnt" }, "entry_SYSCALL_64#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "[libc.so.6]" - } - ], - "uid": "entry_SYSCALL_64" + "uid": "ls;[unknown];[libc.so.6];[libc.so.6];[libc.so.6];entry_SYSCALL_64" }, "__d_lookup_rcu#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "lookup_fast" - } - ], - "uid": "__d_lookup_rcu" + "uid": "ls;[unknown];[libc.so.6];[libc.so.6];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;lookup_fast;__d_lookup_rcu" }, "__intel_pmu_enable_all.isra.0#0": { - "command": "perf-exec", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_execve" - }, - { - "func": "do_execveat_common.isra.0" - }, - { - "func": "bprm_execve" - }, - { - "func": "load_elf_binary" - }, - { - "func": "begin_new_exec" - }, - { - "func": "perf_event_exec" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "perf-exec;[unknown];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_execve;do_execveat_common.isra.0;bprm_execve;load_elf_binary;begin_new_exec;perf_event_exec;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#1": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__do_sys_newfstatat" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__do_sys_newfstatat;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#2": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_statx" - }, - { - "func": "do_statx" - }, - { - "func": "vfs_statx" - }, - { - "func": "v9fs_vfs_getattr_dotl" - }, - { - "func": "p9_client_getattr_dotl" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_statx;do_statx;vfs_statx;v9fs_vfs_getattr_dotl;p9_client_getattr_dotl;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "finish_task_switch.isra.0#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9_client_readdir" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9_client_readdir;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "__intel_pmu_enable_all.isra.0#3": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9_client_readdir" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9_client_readdir;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#4": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_open" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_open;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "__intel_pmu_enable_all.isra.0#5": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_openat" - }, - { - "func": "do_sys_openat2" - }, - { - "func": "do_filp_open" - }, - { - "func": "path_openat" - }, - { - "func": "do_dentry_open" - }, - { - "func": "v9fs_file_open" - }, - { - "func": "p9_client_walk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - }, - { - "func": "__perf_event_task_sched_in" - } - ], - "uid": "__intel_pmu_enable_all.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_openat;do_sys_openat2;do_filp_open;path_openat;do_dentry_open;v9fs_file_open;p9_client_walk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in;__intel_pmu_enable_all.isra.0" }, "schedule#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - } - ], - "uid": "schedule" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule" }, "__perf_event_task_sched_in#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_getdents64" - }, - { - "func": "iterate_dir" - }, - { - "func": "v9fs_dir_readdir_dotl" - }, - { - "func": "p9_client_readdir" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - }, - { - "func": "finish_task_switch.isra.0" - } - ], - "uid": "__perf_event_task_sched_in" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_getdents64;iterate_dir;v9fs_dir_readdir_dotl;p9_client_readdir;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0;__perf_event_task_sched_in" }, "finish_task_switch.isra.0#1": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "schedule" - }, - { - "func": "__schedule" - } - ], - "uid": "finish_task_switch.isra.0" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;schedule;__schedule;finish_task_switch.isra.0" }, "task_work_add#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_exit_group" - }, - { - "func": "do_group_exit" - }, - { - "func": "do_exit" - }, - { - "func": "mmput" - }, - { - "func": "exit_mmap" - }, - { - "func": "fput_many" - } - ], - "uid": "task_work_add" + "uid": "ls;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_exit_group;do_group_exit;do_exit;mmput;exit_mmap;fput_many;task_work_add" }, "nohz_balance_exit_idle#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "__x64_sys_exit_group" - }, - { - "func": "do_group_exit" - }, - { - "func": "do_exit" - }, - { - "func": "mmput" - }, - { - "func": "exit_mmap" - }, - { - "func": "unmap_vmas" - }, - { - "func": "unmap_page_range" - }, - { - "func": "asm_sysvec_hyperv_stimer0" - }, - { - "func": "sysvec_hyperv_stimer0" - }, - { - "func": "__sysvec_hyperv_stimer0" - }, - { - "func": "hrtimer_interrupt" - }, - { - "func": "__hrtimer_run_queues" - }, - { - "func": "tick_sched_timer" - }, - { - "func": "update_process_times" - }, - { - "func": "trigger_load_balance" - } - ], - "uid": "nohz_balance_exit_idle" + "uid": "ls;entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_exit_group;do_group_exit;do_exit;mmput;exit_mmap;unmap_vmas;unmap_page_range;asm_sysvec_hyperv_stimer0;sysvec_hyperv_stimer0;__sysvec_hyperv_stimer0;hrtimer_interrupt;__hrtimer_run_queues;tick_sched_timer;update_process_times;trigger_load_balance;nohz_balance_exit_idle" }, "_raw_spin_lock_irqsave#0": { - "command": "ls", "time": 2.059779644012451, - "trace": [ - { - "func": "[unknown]" - }, - { - "func": "[libc.so.6]" - }, - { - "func": "entry_SYSCALL_64_after_hwframe" - }, - { - "func": "do_syscall_64" - }, - { - "func": "syscall_exit_to_user_mode" - }, - { - "func": "exit_to_user_mode_prepare" - }, - { - "func": "task_work_run" - }, - { - "func": "__fput" - }, - { - "func": "v9fs_dir_release" - }, - { - "func": "p9_client_clunk" - }, - { - "func": "p9_client_rpc" - }, - { - "func": "prepare_to_wait_event" - } - ], - "uid": "_raw_spin_lock_irqsave" + "uid": "ls;[unknown];[libc.so.6];entry_SYSCALL_64_after_hwframe;do_syscall_64;syscall_exit_to_user_mode;exit_to_user_mode_prepare;task_work_run;__fput;v9fs_dir_release;p9_client_clunk;p9_client_rpc;prepare_to_wait_event;_raw_spin_lock_irqsave" } }, "models": [], diff --git a/tests/test_diff_views.py b/tests/test_diff_views.py index f7450343c..318ae98c6 100755 --- a/tests/test_diff_views.py +++ b/tests/test_diff_views.py @@ -75,11 +75,11 @@ def test_diff_flamegraphs_basic(pcs_with_root): "-o", "icicle_graph.html", "--flamegraph-inverted", - "--no-squash-unknown", + "--no-squash", ], ) assert result.exit_code == 0 - assert Path.cwd() / "icicle_graph.html" in Path.cwd().iterdir() + assert Path.cwd() / "icicle_graph.html" in list(Path.cwd().iterdir()) def test_diff_flamegraphs_custom(pcs_with_root): @@ -104,7 +104,7 @@ def test_diff_flamegraphs_custom(pcs_with_root): target_profilename, "-o", "flamegraph_custom", - "--minimize", + "--hide-generics", "--flamegraph-width", 1000, "--flamegraph-height", @@ -185,11 +185,12 @@ def test_diff_report_native(pcs_with_root): "diff", "-o", "diff_report.html", - "--filter-by-relative", - 0.05, - "--top-n", + "--function-threshold", + 0.2, + "--traces-threshold", + 0.005, + "--max-function-traces", 5, - "--minimize", "--link", "https://perfexionists.github.io/perun/", "Perun documentation", diff --git a/tests/test_flame_graph.py b/tests/test_flame_graph.py index a8b8fb6e5..9eeda04e9 100644 --- a/tests/test_flame_graph.py +++ b/tests/test_flame_graph.py @@ -4,15 +4,18 @@ # Standard Imports import os +import pathlib # Third-Party Imports from click.testing import CliRunner # Perun Imports from perun import cli +from perun.profiles.conversions import native_folded +from perun.profiles.folded import postprocess from perun.testing import asserts -from perun.view.flamegraph import run as flamegraph_run import perun.testing.utils as test_utils +from perun.view.flamegraph import core def test_flame_graph(pcs_with_root, valid_profile_pool): @@ -24,11 +27,17 @@ def test_flame_graph(pcs_with_root, valid_profile_pool): valid_profile = test_utils.load_profilename("to_add_profiles", "new-prof-2-memory-basic.perf") memory_profile = test_utils.load_profile("to_add_profiles", "new-prof-2-memory-basic.perf") - # First try to create the graph using the convential matters - flamegraph_run.save_flamegraph(memory_profile, "flame2.svg") + # First, try to create the graph using the module API. + core.generate_flamegraph( + native_folded.native_to_folded(memory_profile), + pathlib.Path("flame2.svg"), + "Test flame graph", + core.FlameGraphSettings(), + postprocess.PostprocessParameters(hide_generics=True, squash=True), + ) assert "flame2.svg" in os.listdir(os.getcwd()) - # Next try to create it using the click + # Next, try to create it using CLI. result = runner.invoke(cli.show, [valid_profile, "flamegraph"]) asserts.predicate_from_cli(result, result.exit_code == 0) diff --git a/tests/test_pcs.py b/tests/test_pcs.py index 053508aa8..7aa407759 100755 --- a/tests/test_pcs.py +++ b/tests/test_pcs.py @@ -9,8 +9,8 @@ # Perun Imports from perun.utils.exceptions import UnsupportedModuleException -from perun.select.whole_repository_selection import WholeRepositorySelection -import perun.select.factory as select +import perun.selection.factory as select +from perun.selection.whole_repository_selection import WholeRepositorySelection def test_selection(pcs_with_root): diff --git a/tests/test_profile_converters.py b/tests/test_profile_converters.py index 669412534..7451afd18 100644 --- a/tests/test_profile_converters.py +++ b/tests/test_profile_converters.py @@ -1,4 +1,4 @@ -"""Basic tests for profile convert module. +"""Basic tests for profile conversion modules. Tests basic functionality of creating other representations of profiles, like e.g for heap and heat map visualizations, etc. @@ -12,7 +12,8 @@ import pytest # Perun Imports -from perun.profiles.native import convert +from perun.postprocess.regression_analysis import transform +from perun.profiles.conversions import native_folded, native_pandas from perun.utils import exceptions import perun.testing.utils as test_utils @@ -23,7 +24,7 @@ def test_convert_models_to_dataframe(): models_profile = test_utils.load_profile("postprocess_profiles", "complexity-models.perf") assert models_profile is not None - df = convert.models_to_pandas_dataframe(models_profile) + df = native_pandas.models_to_pandas_dataframe(models_profile) assert sorted(list(df)) == sorted( [ "coeffs", @@ -47,15 +48,12 @@ def test_flame_graph(memory_profiles): Expecting no errors and returned list of lines representing the format by greg. """ for memory_profile in memory_profiles: - flame_graph = convert.to_flame_graph_format(memory_profile) + flame_graph = list(native_folded.native_to_folded(memory_profile)) line_no = 0 for _, snap in memory_profile.all_snapshots(): line_no += len(list(filter(lambda item: item["subtype"] != "free", snap))) - for line in flame_graph: - print(line) - assert line_no == len(flame_graph) @@ -72,9 +70,9 @@ def test_coefficients_to_points_correct(): # TODO: add more advanced checks models = list(models_profile.all_models()) for model in models: - data = convert.plot_data_from_coefficients_of(model[1]) - assert "plot_x" in data - assert "plot_y" in data + model[1].update(transform.coefficients_to_points(**model[1])) + assert "plot_x" in model[1] + assert "plot_y" in model[1] def test_coefficients_to_points_corrupted_model(): @@ -92,5 +90,5 @@ def test_coefficients_to_points_corrupted_model(): models = list(models_profile.all_models()) with pytest.raises(exceptions.InvalidModelException) as exc: for model in models: - convert.plot_data_from_coefficients_of(model[1]) + model[1].update(transform.coefficients_to_points(**model[1])) assert "Invalid or unsupported regression model: invalid_model." in str(exc.value) diff --git a/tests/test_select.py b/tests/test_select.py index 685dee954..55de40e67 100755 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -1,4 +1,4 @@ -"""Collections of test for perun.select package""" +"""Collections of test for perun.selection package""" from __future__ import annotations @@ -11,8 +11,8 @@ # Perun Imports from perun.logic import pcs from perun.profiles.native import helpers as profile_helpers -from perun.select.abstract_base_selection import AbstractBaseSelection -from perun.select.whole_repository_selection import WholeRepositorySelection +from perun.selection.abstract_base_selection import AbstractBaseSelection +from perun.selection.whole_repository_selection import WholeRepositorySelection def test_base_select(): diff --git a/tests/test_utils.py b/tests/test_utils.py index 73859d2ad..b35281b33 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -5,8 +5,8 @@ # Standard Imports import glob import io -import pkgutil import os +import pkgutil import re import subprocess import signal @@ -20,10 +20,13 @@ from perun.collect.trace.optimizations.structs import Complexity from perun.fuzz import filetype from perun.logic import commands, config, locks -from perun.profiles.native import convert +from perun.profiles import utils +from perun.profiles.conversions import native_folded +from perun.profiles.native import helpers from perun.testing import asserts from perun.utils import log, mapping from perun.utils.common import common_kit, cli_kit, traces_kit, view_kit +from perun.utils.common.common_kit import Aggregations from perun.utils.exceptions import ( SystemTapScriptCompilationException, SystemTapStartupException, @@ -297,8 +300,8 @@ def simple_generator(): assert common_kit.compact_convert_num_to_str(10.0) == "10" assert common_kit.compact_convert_num_to_str(10.123) == "10.12" - assert convert.to_string_line("uid") == "uid" - assert convert.flatten(["hello", "world"]) == "hello,world" + assert native_folded._to_string_line("uid") == "uid" + assert helpers.flatten(["hello", "world"]) == "hello,world" assert common_kit.strtobool("true") == True assert common_kit.strtobool("false") == False @@ -316,19 +319,37 @@ def simple_generator(): assert mapping.get_readable_key("benchmarking.time") == "Benchmarking Time [ms]" assert mapping.get_unit("unsupported") == "?" - assert common_kit.hide_generics("std::vector>") == "std::vector<>" + assert utils.hide_uid_generics("std::vector>") == "std::vector<>" assert external_commands.is_executable("nonexisting") == False p = {"type": "mixed", "units": {"mixed(time delta)": "s"}} assert view_kit.add_y_units(p, "min", "y") == "y [s]" - assert common_kit.aggregate_list([1, 2, 3], "min") == 1 - assert common_kit.aggregate_list([1, 2, 3], "max") == 3 - assert common_kit.aggregate_list([1, 2, 3], "med") == 2 - assert common_kit.aggregate_list([2, 2, 5], "avg") == 3 - assert common_kit.aggregate_list([2, 2, 5], "sum") == 9 - with pytest.raises(AssertionError): - common_kit.aggregate_list([1, 2, 3], "sumvage") + # Test aggregation functions. + sum_func = common_kit.get_aggregation_callable(Aggregations.from_string("sum")) + min_func = common_kit.get_aggregation_callable(Aggregations.from_string("min")) + max_func = common_kit.get_aggregation_callable(Aggregations.from_string("max")) + count_func = common_kit.get_aggregation_callable(Aggregations.from_string("count")) + nunique_func = common_kit.get_aggregation_callable(Aggregations.from_string("nunique")) + mean_func = common_kit.get_aggregation_callable(Aggregations.from_string("mean")) + assert mean_func is common_kit.get_aggregation_callable(Aggregations.from_string("avg")) + assert mean_func is common_kit.get_aggregation_callable(Aggregations.from_string("average")) + median_func = common_kit.get_aggregation_callable(Aggregations.from_string("median")) + assert median_func is common_kit.get_aggregation_callable(Aggregations.from_string("med")) + with pytest.raises(ValueError): + common_kit.get_aggregation_callable(Aggregations.from_string("sumvage")) + + assert sum_func([1, 2, 3]) == 6 + assert min_func([1, 2, 3]) == 1 + assert max_func([1, 2, 3]) == 3 + assert count_func([1, 2, 3, 1]) == 4 + assert nunique_func([1, 2, 3]) == 3 + assert nunique_func([1, 2, 3, 1]) == 3 + assert mean_func([2, 2, 5]) == 3 + assert mean_func([]) == 0 + assert median_func([2, 2, 5]) == 2 + assert median_func([]) == 0 + assert int(median_func([2, 6])) == 4 def test_predicates(capsys): From 1aeef77ecc38529a6204d319e8f3df66b738af52 Mon Sep 17 00:00:00 2001 From: Jiri Pavela Date: Tue, 8 Sep 2026 21:20:34 +0200 Subject: [PATCH 3/4] Update the documentation --- docs/conf.py | 14 +++++- docs/config.rst | 2 +- docs/profile.rst | 53 ++++++++++++++++++--- perun/profiles/conversions/native_pandas.py | 5 +- perun/profiles/conversions/polars_folded.py | 8 ++-- perun/templates/diff_sections/meson.build | 1 - perun/templates/scripts/meson.build | 1 - perun/templates/style/meson.build | 1 - perun/utils/type_hints.py | 12 ++--- perun/view/flamegraph/run.py | 4 +- pyproject.toml | 1 + tests/test_flame_graph.py | 4 +- 12 files changed, 78 insertions(+), 28 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e5a66422a..2d9bdadfa 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,8 +41,15 @@ "sphinx.ext.todo", "sphinx.ext.intersphinx", "sphinx_click.ext", + "sphinx_autodoc_typehints", ] +# Add intersphinx mappings for libraries. +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "pandas": ("https://pandas.pydata.org/docs", None), +} + # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -185,7 +192,12 @@ ), ] -nitpick_ignore = [("py:class", "ModelRecord"), ("py:class", "pandas.DataFrame")] +nitpick_ignore = [ + ("py:class", "perun.utils.structs.common_structs.ModelRecord"), + ("py:class", "perun.utils.common.common_kit.Aggregations"), + ("py:class", "TempTextIO"), + ("py:class", "polars.lazyframe.frame.LazyFrame"), +] def doctree_read_handler(_, doctree): diff --git a/docs/config.rst b/docs/config.rst index 9438acc47..fa605e49c 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -207,7 +207,7 @@ List of Supported Options multi-key sort semantics by providing a list of the following attributes specified by the class attribute ``ProfileInfo.valid_attributes``: -.. currentmodule:: perun.profile.helpers +.. currentmodule:: perun.profiles.native.helpers .. autoattribute:: ProfileInfo.valid_attributes .. confkey:: format.sort_profiles_order diff --git a/docs/profile.rst b/docs/profile.rst index 1d2cd277a..c1af5f152 100644 --- a/docs/profile.rst +++ b/docs/profile.rst @@ -355,9 +355,9 @@ to the look-up table should greatly reduce the size of profiles. Profile API ----------- -.. automodule:: perun.profile.helpers +.. automodule:: perun.profiles.native.helpers -.. automodule:: perun.profile.factory +.. automodule:: perun.profiles.native.factory .. autoclass:: Profile :members: all_resources, all_snapshots, all_models, all_filtered_models @@ -367,20 +367,46 @@ Profile API Profile Conversions API ----------------------- -.. automodule:: perun.profile.convert +.. automodule:: perun.profiles.conversions.elk_native + +.. autofunction:: import_elk_from_json + +.. automodule:: perun.profiles.conversions.folded_polars + +.. autofunction:: folded_profiles_to_polars_lf + +.. automodule:: perun.profiles.conversions.native_folded + +.. autofunction:: native_to_folded + +.. automodule:: perun.profiles.conversions.native_pandas .. autofunction:: resources_to_pandas_dataframe -.. autofunction:: to_flame_graph_format +.. autofunction:: models_to_pandas_dataframe + +.. automodule:: perun.profiles.conversions.native_polars + +.. autofunction:: native_to_polars_lf + +.. automodule:: perun.profiles.conversions.perf_native + +.. autofunction:: import_perf_from_record + +.. autofunction:: import_perf_from_script -.. autofunction:: plot_data_from_coefficients_of +.. autofunction:: import_perf_from_stack + +.. automodule:: perun.profiles.conversions.polars_folded + +.. autofunction:: store_polars_as_folded_profile .. _profile-query-api: Profile Query API ----------------- -.. automodule:: perun.profile.query +.. automodule:: perun.profiles.native.query .. autofunction:: all_items_of @@ -391,3 +417,18 @@ Profile Query API .. autofunction:: all_key_values_of .. autofunction:: unique_model_values_of + +Helper Structures +----------------- + +.. automodule:: perun.profiles.structs + +.. autoclass:: PostprocessParameters + +.. autoclass:: ProfileFeatures + +.. automodule:: perun.profiles.polars.structs + +.. autoclass:: FunctionMaps + +.. autoclass:: PolarsTraceProfile diff --git a/perun/profiles/conversions/native_pandas.py b/perun/profiles/conversions/native_pandas.py index 7c5a0a470..bfa13f061 100644 --- a/perun/profiles/conversions/native_pandas.py +++ b/perun/profiles/conversions/native_pandas.py @@ -1,4 +1,7 @@ -"""Functions for converting native profiles to Pandas representations.""" +"""Functions for converting native profiles to Pandas representations. + +.. _pandas: https://pandas.pydata.org/ +""" from __future__ import annotations diff --git a/perun/profiles/conversions/polars_folded.py b/perun/profiles/conversions/polars_folded.py index 42a974102..690ce775f 100644 --- a/perun/profiles/conversions/polars_folded.py +++ b/perun/profiles/conversions/polars_folded.py @@ -3,15 +3,13 @@ from __future__ import annotations # Standard Imports -from typing import TextIO, TYPE_CHECKING +from typing import TextIO # Third-Party Imports # Perun Imports - -if TYPE_CHECKING: - from perun.profiles.polars import structs - from perun.utils.type_hints import TempTextIO +from perun.profiles.polars import structs +from perun.utils.type_hints import TempTextIO def store_polars_as_folded_profile( diff --git a/perun/templates/diff_sections/meson.build b/perun/templates/diff_sections/meson.build index 087c8b7bd..4f3d1069f 100644 --- a/perun/templates/diff_sections/meson.build +++ b/perun/templates/diff_sections/meson.build @@ -8,7 +8,6 @@ perun_templates_diff_section_files = files( 'section_page_footer.html.jinja2', 'section_page_title.html.jinja2', 'section_profile_stats.html.jinja2', - 'section_sankey_graph.html.jinja2', 'section_tree_maps.html.jinja2', 'section_vulnerabilities.html.jinja2', ) diff --git a/perun/templates/scripts/meson.build b/perun/templates/scripts/meson.build index 038f838b4..8bed90edc 100644 --- a/perun/templates/scripts/meson.build +++ b/perun/templates/scripts/meson.build @@ -9,7 +9,6 @@ perun_templates_script_files = files( 'chatbot.html.jinja2', 'navigation_sidepanel.js', 'notes.js', - 'sankey_graph_js.html.jinja2', 'overview.js', 'themes_js.html.jinja2', 'tooltip.js', diff --git a/perun/templates/style/meson.build b/perun/templates/style/meson.build index 807ffed69..3dcf4b942 100644 --- a/perun/templates/style/meson.build +++ b/perun/templates/style/meson.build @@ -12,7 +12,6 @@ perun_templates_style_files = files( 'navigation_sidepanel.css', 'notes.css', 'pins.css', - 'sankey_graph.css', 'overview.css', 'tag.css', 'themes_sidepanel.css', diff --git a/perun/utils/type_hints.py b/perun/utils/type_hints.py index 74d44d5d2..0d77a5aea 100644 --- a/perun/utils/type_hints.py +++ b/perun/utils/type_hints.py @@ -3,15 +3,13 @@ from __future__ import annotations # Standard Imports -from typing import TYPE_CHECKING +import tempfile +from typing import TypeAlias # Third-Party Imports # Perun Imports -if TYPE_CHECKING: - import tempfile - - # The tempfile.NamedTemporaryFile context manager does not have a public type hint. - # Hence, we use the private tempfile object and alias it to avoid redundant warnings. - TempTextIO = tempfile._TemporaryFileWrapper[str] +# The tempfile.NamedTemporaryFile context manager does not have a public type hint. +# Hence, we use the private tempfile object and alias it to avoid redundant warnings. +TempTextIO: TypeAlias = "tempfile._TemporaryFileWrapper[str]" diff --git a/perun/view/flamegraph/run.py b/perun/view/flamegraph/run.py index a09c639ec..ff8e0e396 100644 --- a/perun/view/flamegraph/run.py +++ b/perun/view/flamegraph/run.py @@ -35,7 +35,7 @@ def flamegraph(profile: profile_factory.Profile, output_path: pathlib.Path, **kw \b * **Limitations**: `memory` or `time` profiles generated by - :ref:`collectors-memory` or :ref:`collectors-kperf`. + :ref:`collectors-memory` or kperf. * **Interpretation style**: graphical * **Visualization backend**: HTML @@ -64,7 +64,7 @@ def flamegraph(profile: profile_factory.Profile, output_path: pathlib.Path, **kw Refer to :ref:`views-flame-graph` for more thorough description and examples of the interpretation technique. Refer to - :func:`perun.profile.convert.to_flame_graph_format` for more details how + :func:`perun.profiles.conversions.native_folded.native_to_folded` for more details how the profiles are converted to the flame graph format. """ params = structs.PostprocessParameters(**kwargs) diff --git a/pyproject.toml b/pyproject.toml index 050dbea5a..98d949fcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ dynamic = ["version"] docs = [ "Sphinx>=8.1.3", # Latest release still supporting Python3.10 "sphinx-click>=6.2.0", + "sphinx-autodoc-typehints>=3.0.1", # Latest release still supporting Python3.10 ] lint = [ "pre-commit>=4.6.0", diff --git a/tests/test_flame_graph.py b/tests/test_flame_graph.py index 9eeda04e9..bd5dc0b7c 100644 --- a/tests/test_flame_graph.py +++ b/tests/test_flame_graph.py @@ -11,8 +11,8 @@ # Perun Imports from perun import cli +from perun.profiles import structs from perun.profiles.conversions import native_folded -from perun.profiles.folded import postprocess from perun.testing import asserts import perun.testing.utils as test_utils from perun.view.flamegraph import core @@ -33,7 +33,7 @@ def test_flame_graph(pcs_with_root, valid_profile_pool): pathlib.Path("flame2.svg"), "Test flame graph", core.FlameGraphSettings(), - postprocess.PostprocessParameters(hide_generics=True, squash=True), + structs.PostprocessParameters(hide_generics=True, squash=True), ) assert "flame2.svg" in os.listdir(os.getcwd()) From ce27f401871a6927aa1b6098dcf8fa28eb30ab54 Mon Sep 17 00:00:00 2001 From: Jiri Pavela Date: Tue, 8 Sep 2026 21:36:36 +0200 Subject: [PATCH 4/4] Fix minor issues + typing + linting --- docs/conf.py | 1 + perun/profiles/conversions/perf_native.py | 6 +++--- perun/selection/whole_repository_selection.py | 7 ++----- perun/templates/diff_components/traces_table.html.jinja2 | 6 ------ .../templates/diff_sections/section_tree_maps.html.jinja2 | 2 -- perun/templates/scripts/tree_map_js.html.jinja2 | 8 +------- perun/view_diff/report/core.py | 2 +- perun/view_diff/report/folded.py | 1 - perun/view_diff/report/native.py | 2 +- 9 files changed, 9 insertions(+), 26 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2d9bdadfa..35e6743a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -197,6 +197,7 @@ ("py:class", "perun.utils.common.common_kit.Aggregations"), ("py:class", "TempTextIO"), ("py:class", "polars.lazyframe.frame.LazyFrame"), + ("py:class", "pandas.core.frame.DataFrame"), # Needed only for Python 3.10 ] diff --git a/perun/profiles/conversions/perf_native.py b/perun/profiles/conversions/perf_native.py index cbea5b105..8b7fa83a1 100644 --- a/perun/profiles/conversions/perf_native.py +++ b/perun/profiles/conversions/perf_native.py @@ -6,7 +6,7 @@ from dataclasses import asdict import os from pathlib import Path -import subprocess +from subprocess import CalledProcessError import sys from typing import Any @@ -61,7 +61,7 @@ def import_perf_from_record( log.minor_success( f"Raw data from {log.path_style(str(imported_file.path))}", "collected" ) - except subprocess.CalledProcessError as err: + except CalledProcessError as err: log.minor_fail( f"Raw data from {log.path_style(str(imported_file.path))}", "not collected" ) @@ -171,7 +171,7 @@ def _import_perf_profile( "exitcode": [p.exit_code for p in profiles], "workload": kwargs.get("workload", ""), "label": kwargs.get("profile_label", ""), - "units": {"time": "sample"}, + "units": {"time": "samples"}, }, "collector_info": { "name": "kperf", diff --git a/perun/selection/whole_repository_selection.py b/perun/selection/whole_repository_selection.py index 387ed17a5..f6215e8a4 100755 --- a/perun/selection/whole_repository_selection.py +++ b/perun/selection/whole_repository_selection.py @@ -7,19 +7,16 @@ """ # Standard Imports -from typing import Iterator, TYPE_CHECKING +from typing import Iterator # Third-Party Imports # Perun Imports from perun.logic import pcs -from perun.profiles.native import helpers as profile_helpers +from perun.profiles.native import helpers as profile_helpers, Profile, ProfileInfo from perun.selection.abstract_base_selection import AbstractBaseSelection from perun.utils.structs.common_structs import MinorVersion -if TYPE_CHECKING: - from perun.profiles.native import Profile, ProfileInfo - class WholeRepositorySelection(AbstractBaseSelection): """Implementation of Whole Repository Selection""" diff --git a/perun/templates/diff_components/traces_table.html.jinja2 b/perun/templates/diff_components/traces_table.html.jinja2 index 2516df167..efcb2eecf 100644 --- a/perun/templates/diff_components/traces_table.html.jinja2 +++ b/perun/templates/diff_components/traces_table.html.jinja2 @@ -6,10 +6,8 @@ {{ m_title.display_subtitle("Inclusive (Cumulative) Consumption", "traces-inclusive", "", notes_enabled) }}
-{% if is_folded %} {{ m_title.display_subtitle("Exclusive (Self) Consumption", "traces-exclusive", "", notes_enabled) }}
-{% endif %} \ No newline at end of file diff --git a/perun/templates/diff_sections/section_tree_maps.html.jinja2 b/perun/templates/diff_sections/section_tree_maps.html.jinja2 index 7ad09b28e..112fb815d 100644 --- a/perun/templates/diff_sections/section_tree_maps.html.jinja2 +++ b/perun/templates/diff_sections/section_tree_maps.html.jinja2 @@ -40,9 +40,7 @@ - {% if is_folded %} - {% endif %} diff --git a/perun/templates/scripts/tree_map_js.html.jinja2 b/perun/templates/scripts/tree_map_js.html.jinja2 index d50d707fa..80fdbe129 100644 --- a/perun/templates/scripts/tree_map_js.html.jinja2 +++ b/perun/templates/scripts/tree_map_js.html.jinja2 @@ -856,14 +856,12 @@ .attr("y", FONT_SIZE * 2) .text(HIGHLIGHT_STATES[panel.highlight].btnLabel); - {% if is_folded %} svg.append("text") .attr("id", `${panelId}-excToggle`) .attr("class", "svg-btn") .attr("x", VIEW_WIDTH - XPAD1 - 200) .attr("y", FONT_SIZE * 2) .text(exclusiveToggleLabel(panel.metric)); - {% endif %} svg.append("text") .attr("id", `${panelId}-search`) @@ -985,12 +983,10 @@ if (icBtn) { icBtn.classList.toggle("show", panel.search.ignorecase); } - {% if is_folded %} const excBtn = document.getElementById(`${panelId}-excToggle`); if (excBtn) { excBtn.textContent = exclusiveToggleLabel(panel.metric); } - {% endif %} } function renderPanel(panelId) { @@ -1177,7 +1173,7 @@ { suffix: "-search", handler: panelSearchPrompt }, { suffix: "-ignorecase", handler: togglePanelIgnorecase }, { suffix: "-highlight", handler: togglePanelHighlight }, - {% if is_folded %} { suffix: "-excToggle", handler: togglePanelExclusive }, {% endif %} + { suffix: "-excToggle", handler: togglePanelExclusive }, ]; function handlePanelControlClick(targetId) { @@ -1201,9 +1197,7 @@ initTreemapHoverDelegation(chartGrid); document.getElementById("treemap_search_reset_all").addEventListener("click", globalSearchReset); - {% if is_folded %} document.getElementById("treemap_toggle_ie_all").addEventListener("click", globalMetricToggle); - {% endif %} document.getElementById("treemap_search_all_input").addEventListener("input", globalSearchApply); document.getElementById("treemap_search_all_input").addEventListener("keypress", function (e) { globalSearchApply(); diff --git a/perun/view_diff/report/core.py b/perun/view_diff/report/core.py index 4335c397e..4ea9478c6 100644 --- a/perun/view_diff/report/core.py +++ b/perun/view_diff/report/core.py @@ -150,6 +150,7 @@ def generate_report_view( ) minwidth_threshold = fg_settings.compute_minwidth_threshold() + log.minor_info(f"Units: {fg_settings.countname}") # Dump the profiles back into a folded format for flamegraph scripts. grid = fg_grid.FlameGraphGrid() log.minor_info("Saving post-processed profiles in temporary files.") @@ -289,7 +290,6 @@ def generate_report_view( top_diffs_func_exclusive=diffs.iterate_top_diffs(func_top_diffs[1]), total_baseline=pair_profile.baseline.features.total_resources, total_target=pair_profile.target.features.total_resources, - is_folded=True, ) log.minor_success("HTML report", "rendered") return content, utc_time_now diff --git a/perun/view_diff/report/folded.py b/perun/view_diff/report/folded.py index 6d7d35c84..43f679880 100644 --- a/perun/view_diff/report/folded.py +++ b/perun/view_diff/report/folded.py @@ -3,7 +3,6 @@ from __future__ import annotations # Standard Imports -import dataclasses from pathlib import Path from typing import Any, Optional diff --git a/perun/view_diff/report/native.py b/perun/view_diff/report/native.py index f58680003..075b0f894 100644 --- a/perun/view_diff/report/native.py +++ b/perun/view_diff/report/native.py @@ -135,7 +135,7 @@ def generate_report_from_native_profiles( ) log.minor_success("Parsing Input Perun Profiles") - diff_html, creation_time = core.generate_report_view( + diff_html, _ = core.generate_report_view( pair_profile, baseline, target,