Skip to content

Commit

Permalink
Add support for import from CSV and import dir
Browse files Browse the repository at this point in the history
Perun import now supports specification of imported profiles
through CSV files and allows to specify the import directory.

CLI and CSV interface were extended with the ability to specify
custom profile stats.
  • Loading branch information
JiriPavela committed Aug 7, 2024
1 parent c2c10cc commit 72c9237
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 104 deletions.
36 changes: 22 additions & 14 deletions perun/cli_groups/import_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@
# Perun Imports
from perun.logic import commands
from perun.profile import imports
from perun.utils.common import cli_kit


@click.group("import")
@click.option(
"--machine-info",
"-i",
type=click.Path(resolve_path=True, readable=True),
help="Imports machine info from file in JSON format (by default, machine info is loaded from the current host)."
"You can use `utils/generate_machine_info.sh` script to generate the machine info file.",
help="Imports machine info from file in JSON format (by default, machine info is loaded from "
"the current host). You can use `utils/generate_machine_info.sh` script to generate the "
"machine info file.",
)
@click.option(
"--import-dir",
"-d",
type=click.Path(resolve_path=True, readable=True),
help="Specifies the directory to import profiles from.",
)
@click.option(
"--minor-version",
Expand All @@ -31,18 +37,18 @@
help="Specifies the head minor version, for which the profiles will be imported.",
)
@click.option(
"--exitcode",
"-e",
"--stats-info",
"-t",
nargs=1,
required=False,
default="?",
help=("Exit code of the command."),
default=None,
metavar="<stat1-description,...>",
help="Describes the stats associated with the imported profiles. Please see the import "
"documentation for details regarding the stat description format.",
)
@click.option(
"--cmd",
"-c",
nargs=1,
required=False,
default="",
help=(
"Command that was being profiled. Either corresponds to some"
Expand All @@ -53,7 +59,6 @@
"--workload",
"-w",
nargs=1,
required=False,
default="",
help="Inputs for <cmd>. E.g. ``./subdir`` is possible workload for ``ls`` command.",
)
Expand Down Expand Up @@ -85,7 +90,8 @@ def perf_group(ctx: click.Context, **kwargs: Any) -> None:
This supports either profiles collected in:
1. Binary format: e.g., `collected.data` files, that are results of `perf record`
2. Text format: result of `perf script` that parses the binary into user-friendly and parsing-friendly text format
2. Text format: result of `perf script` that parses the binary into user-friendly and
parsing-friendly text format
"""
ctx.obj.update(kwargs)

Expand All @@ -107,7 +113,7 @@ def from_binary(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:


@perf_group.command("script")
@click.argument("imported", type=click.Path(resolve_path=True), nargs=-1, required=True)
@click.argument("imported", type=str, nargs=-1, required=True)
@click.pass_context
def from_text(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:
"""Import Perun profiles from output generated by `perf script` command"""
Expand All @@ -116,9 +122,11 @@ def from_text(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:


@perf_group.command("stack")
@click.argument("imported", type=click.Path(resolve_path=True), nargs=-1, required=True)
@click.argument("imported", type=str, nargs=-1, required=True)
@click.pass_context
def from_stacks(ctx: click.Context, imported: list[str], **kwargs: Any) -> None:
"""Import Perun profiles from output generated by `perf script | stackcollapse-perf.pl` command"""
"""Import Perun profiles from output generated by `perf script | stackcollapse-perf.pl`
command
"""
kwargs.update(ctx.obj)
imports.import_perf_from_stack(imported, **kwargs)
51 changes: 50 additions & 1 deletion perun/profile/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
from __future__ import annotations

# Standard Imports
from typing import Any, TYPE_CHECKING
from typing import Any, TYPE_CHECKING, ClassVar
import json
import operator
import os
import re
import time
from dataclasses import dataclass

# Third-Party Imports

Expand Down Expand Up @@ -609,3 +610,51 @@ def is_compatible_with_profile(self, profile: profiles.Profile) -> bool:
"checksum",
"source",
]


@dataclass
class ProfileStat:
ALLOWED_ORDERING: ClassVar[dict[str, bool]] = {
"higher_is_better": True,
"lower_is_better": False,
}

name: str
unit: str = "#"
ordering: bool = True
tooltip: str = ""
value: int | float = 0.0

@classmethod
def from_string(
cls,
name: str = "empty",
unit: str = "#",
ordering: str = "higher_is_better",
tooltip: str = "",
*_: Any,
) -> ProfileStat:
if name == "empty":
# Invalid stat specification, warn
perun_log.warn("Empty profile stat specification. Creating a dummy 'empty' stat.")
if ordering not in cls.ALLOWED_ORDERING:
# Invalid stat ordering, warn
perun_log.warn(
f"Unknown stat ordering: {ordering}. Please choose one of "
f"({', '.join(cls.ALLOWED_ORDERING.keys())}). "
f"Using the default stat ordering value."
)
ordering_bool = ProfileStat.ordering
else:
ordering_bool = cls.ALLOWED_ORDERING[ordering]
return cls(name, unit, ordering_bool, tooltip)

def get_normalized_tooltip(self) -> str:
# Find the string representation of the ordering to use in the tooltip
ordering: str = ""
for str_desc, bool_repr in self.ALLOWED_ORDERING.items():
if bool_repr == self.ordering:
ordering = str_desc.replace("_", " ")
if self.tooltip:
return f"{self.tooltip} ({ordering})"
return ordering
Loading

0 comments on commit 72c9237

Please sign in to comment.