061948a5ec
* 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>
203 lines
6.7 KiB
Python
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
|