Files
rocm-systems/projects/rocprofiler-sdk/source/lib/python/rocpd/output_config.py
T
systems-assistant[bot] 061948a5ec [rocpd] Adding merge and package submodules for rocpd (#164)
* adding ROCpd database merge

* adding ROCpd database merge concatenating all tables

* update merge script

  - copy all tables from files

* fix merge format

* Add package submodule, initial POC.  Need to refine

* Minor fixes and clean up duplicated code in package.py

* Revamp metadata layout, add wildcard and .rpdb parsing

* Add auto merge & package when > 5 DBs, add examples, don't use auto_merge when using sub-commands merge & package

* - Extend package/yaml inputs to all rocpd modules
- Improve handling more corner cases for bad input files when parsing input parameters (bad yaml files, bad .rpdb folder, folders as input)
- Changed to use UUID in merged filename instead of the time, in auto-merge algorithm

* Minor text fixes for consistancy between modules

* Add more wildcard support and add package, merge tests

* Make changes based on review suggestions

* Move parsing packages into importer.py, simplified adding required params to a function

* fix package test by flattening input list before processing

* Integrate merge.py changes from Jonathan to add name-collision checks, recreating indexes, foreign key check (disabled for now, due to processing time)

* Rework rocpd.<submodule>.{add_args,process_args}

- add_args function returns a functor which accepts input and args
- time_window functor returned from add_args automatically applies time windowing of input

* change merge&package limit to 1, merge should create data views

* Move files by default instead of making copies

- copying can be enabled by passing "copy=True" or --copy cmdline argument

* refactor package to make the logic cleaner, set merge limit back to 5

* Allow automerge-limit param to override limit, change default back to 1.  Tests updated to use query, much quicker

* Update --help instructions for package

---------

Co-authored-by: acanadas <acanadas@amd.com>
Co-authored-by: a-canadasruiz <Araceli.CanadasRuiz@amd.com>
Co-authored-by: Young Hui <young.hui@amd.com>
Co-authored-by: Jonathan R. Madsen <jonathanrmadsen@gmail.com>
2025-11-12 17:07:12 -05:00

203 lines
6.7 KiB
Python

###############################################################################
# MIT License
#
# Copyright (c) 2023 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
###############################################################################
import sys
import os
import argparse
try:
import ctypes
sqlite3lib = ctypes.CDLL("libsqlite3.so")
except Exception:
pass
from . import libpyrocpd
__all__ = ["format_path", "output_config", "add_args", "add_generic_args"]
def _generate_attribute_docs(data):
properties = []
for key, itr in data.items():
if not isinstance(key, str):
pass
if key.startswith("__") and key.endswith("__"):
pass
else:
properties += [key]
return "\n\t- ".join(properties)
class output_config(libpyrocpd.output_config):
__doc__ = f"""Output configuration
Read/Write properties:\n\t- {_generate_attribute_docs(libpyrocpd.output_config.__dict__)}
Example:
# folder for output data
output_dir = os.path.join(os.getcwd(), "rocpd-output")
# create output config instance
cfg = output_config(output_path=output_dir, output_file="out")
# using read/write properties
if cfg.output_path != output_dir:
cfg.output_path = output_dir
"""
def __init__(self, **kwargs):
super(output_config, self).__init__()
self.update(**kwargs)
def update(self, **kwargs):
_strict = kwargs.get("strict", False)
for key, itr in kwargs.items():
if hasattr(self, key):
if key == "agent_index_value":
if itr == "absolute":
setattr(self, key, libpyrocpd.agent_indexing.node)
elif itr == "type-relative":
setattr(self, key, libpyrocpd.agent_indexing.logical_node_type)
else:
setattr(self, key, libpyrocpd.agent_indexing.logical_node)
else:
setattr(self, key, itr)
elif _strict:
raise KeyError(f"output_config does not have {key} attribute")
return self
def format_path(path, tag=os.path.basename(sys.executable)):
return libpyrocpd.format_path(path, tag)
def sanitize_input_list(input: list):
sanitized_list = []
for items in input:
if isinstance(items, list):
sanitized_list.extend(sanitize_input_list(items))
else:
sanitized_list.append(items)
return sanitized_list
def check_file_exists(filename):
import glob
# Check for wildcards passed in
if any(char in filename for char in ["*", "?", "["]):
expanded_files = glob.glob(filename)
if not expanded_files:
raise argparse.ArgumentTypeError(f"File '{filename}' does not exist.")
return sanitize_input_list(expanded_files)
else:
if not os.path.exists(filename):
raise argparse.ArgumentTypeError(f"File '{filename}' does not exist.")
return filename
def add_args(parser):
"""Add output arguments to an existing parser."""
io_options = parser.add_argument_group("I/O options")
io_options.add_argument(
"-o",
"--output-file",
help="Sets the base output file name (default base filename: `out`)",
default=os.environ.get("ROCPD_OUTPUT_NAME", "out"),
type=str,
required=False,
)
io_options.add_argument(
"-d",
"--output-path",
help="Sets the output path where the output files will be saved (default path: `./rocpd-output-data`)",
default=os.environ.get("ROCPD_OUTPUT_PATH", "./rocpd-output-data"),
type=str,
required=False,
)
io_options.add_argument(
"--automerge-limit",
help="Change database auto-merge limit (default: 1, auto-merge to 1 DB. max: 8)",
type=int,
required=False,
)
def process_args(input, args):
valid_args = ["output_file", "output_path"]
ret = {}
for itr in valid_args:
if hasattr(args, itr):
val = getattr(args, itr)
if itr == "output_format":
ret[itr] = val
elif itr == "output_path" and val is not None:
ret[itr] = format_path(val)
elif val is not None:
ret[itr] = val
return ret
return process_args
def add_generic_args(parser):
"""Add generic arguments that apply to multiple output formats."""
kernel_naming_options = parser.add_argument_group("Kernel naming options")
kernel_naming_options.add_argument(
"--kernel-rename",
help="Use ROCTx marker names instead of kernel names",
action="store_true",
default=False,
)
generic_options = parser.add_argument_group("Generic options")
generic_options.add_argument(
"--agent-index-value",
choices=("absolute", "relative", "type-relative"),
help="""Device identification format in CSV/Perfetto/OTF2 output (default: relative):
absolute: uses node_id (Agent-0, Agent-2, Agent-4) ignoring cgroups restrictions.
relative: uses logical_node_id (Agent-0, Agent-1, Agent-2) considering cgroups restrictions.
type-relative: uses logical_node_type_id (CPU-0, GPU-0, GPU-1) with numbering that resets for each device type.""",
default="relative",
)
def process_args(input, args):
valid_args = ["agent_index_value", "kernel_rename"]
ret = {}
for itr in valid_args:
if hasattr(args, itr):
val = getattr(args, itr)
if val is not None:
ret[itr] = val
return ret
return process_args