rocprofv3: summary reports + more JSON metadata (#1029)

* Move include/rocprofiler-sdk/cxx/details/delimit.hpp to tokenize.hpp

* Update docs/how-to/using-rocprofv3.rst

- fix code block indents
- reorder rocprofv3 options, limit them to important options
- add docs for `--runtime-trace`

* Update rocprofv3.py

- parser argument groups
- new `--runtime-trace` option
- new `--summary` option
- new `--summary-per-domain` option
- new `--summary-groups` option
- new `--summary-output-file` option
- new `--summary-units` option

* Update lib/rocprofiler-sdk/hsa/async_copy.cpp

- fix async copy operation names: add "MEMORY_COPY_" prefix

* lib/rocprofiler-sdk-tool: update statistics.{hpp,cpp}

- statistics<>::get_percent function
- stats_entry_t struct
- stats_formatter struct
- percentage struct
- std::to_string(::rocprofiler::tool::percentage)

* lib/rocprofiler-sdk-tool: update domain_type.{hpp,cpp}

- reorder domain_type enum values

* lib/rocprofiler-sdk-tool: update generateCSV.{hpp,cpp}

- separate writing CSV from accumulating statistics
- a lot of functionality was moved to statistics.{hpp,cpp}

* lib/rocprofiler-sdk-tool: update output_file.{hpp,cpp}

- output_stream_t struct
- get_output_stream(...) returns output_stream_t instance

* lib/rocprofiler-sdk-tool: update generateJSON.cpp

- update get_output_stream usage to output_stream_t

* lib/rocprofiler-sdk-tool: update generateOTF2.cpp

- header include order tweak

* lib/rocprofiler-sdk-tool: update buffered_output.hpp

- stats_data_t was renamed to stats_entry_t

* lib/rocprofiler-sdk-tool: update generatePerfetto.cpp

- header include tweak

* lib/rocprofiler-sdk-tool: update tmp_file_buffer.hpp

- emit warning message if write_ring_buffer fails after offloading instead of aborting
- prefer placement new instead of assignment in write_ring_buffer

* lib/rocprofiler-sdk-tool: add generateStats.{hpp,cpp}

- functions for accumulating statistics

* Update tests/rocprofv3/tracing-hip-in-libraries/CMakeLists.txt

- accommodate tweak to CSV output file name for HIP and HSA traces

* lib/rocprofiler-sdk-tool: update config.{hpp,cpp}

- new config variables
  - stats_summary
  - stats_summary_per_domain
  - summary_output
  - stats_summary_unit_value
  - stats_summary_unit
  - stats_summary_file
  - stats_summary_groups
- support output keys for hostname: %hostname% / %h

* lib/rocprofiler-sdk-tool: update tool.cpp

- support summary output

* Documentation fixes

* Test for summary output

* Update tests/bin/transpose to use more ROCTx

- also support building with the roctracer ROCTx

* Remove roctxMark from OTF2 + fix kernel-rename tests

- following more ROCTx calls in transpose, kernel-rename validation had to be updated

* JSON metadata + JSON summary

- add serialization support for config
- add serialization support for statistics
- additions to json spec
  - rocprofiler-sdk-tool/metadata/config
  - rocprofiler-sdk-tool/metadata/command
  - rocprofiler-sdk-tool/summary
- config output_keys support for NVIDIA %q{<ENV-VAR>} syntax
- config output_keys support keys within keys

* rocprofv3 --summary-groups warning if no domain matches

- emit warning if a regex in for summary groups did not match any domain names

* Compile fix for lib/rocprofiler-sdk-tool/tool.cpp

- get_config().scratch_memory_trace
- pass contributions to write_json

* Update rocprofv3.py to preload rocprofiler-sdk-roctx

- appended to LD_PRELOAD when args.marker_trace is enabled

* Fix ReST link errors about subtitle underline being too short

* Patch tokenization of config::stats_summary_groups

- guard against array values of empty strings

* Tweak rocprofv3 summary test

- input-summary.yaml (used by rocprofv3-test-summary-inp-yaml-execute) only provides one summary group regex

* Disable LD_PRELOAD of librocprofiler-sdk-roctx.so

- this causes problems in the sanitizers, will be addressed in another PR

[ROCm/rocprofiler-sdk commit: 395f01b689]
This commit is contained in:
Jonathan R. Madsen
2024-09-09 11:20:55 -05:00
committed by GitHub
parent 63f9bdc75e
commit 6743608258
40 changed files with 2293 additions and 580 deletions
+256 -111
View File
@@ -83,8 +83,8 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
formatter_class=argparse.RawTextHelpFormatter,
)
def add_parser_bool_argument(*args, **kwargs):
parser.add_argument(
def add_parser_bool_argument(gparser, *args, **kwargs):
gparser.add_argument(
*args,
**kwargs,
action=booleanArgAction,
@@ -95,90 +95,15 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
metavar="BOOL",
)
# Add the arguments
add_parser_bool_argument(
"--hip-trace",
help="For Collecting HIP Traces (runtime + compiler)",
)
add_parser_bool_argument(
"--hip-runtime-trace",
help="For Collecting HIP Runtime API Traces",
)
add_parser_bool_argument(
"--hip-compiler-trace",
help="For Collecting HIP Compiler generated code Traces",
)
add_parser_bool_argument(
"--marker-trace",
help="For Collecting Marker (ROCTx) Traces",
)
add_parser_bool_argument(
"--kernel-trace",
help="For Collecting Kernel Dispatch Traces",
)
add_parser_bool_argument(
"--memory-copy-trace",
help="For Collecting Memory Copy Traces",
)
add_parser_bool_argument(
"--scratch-memory-trace",
help="For Collecting Scratch Memory operations Traces",
)
add_parser_bool_argument(
"--stats",
help="For Collecting statistics of enabled tracing types",
)
add_parser_bool_argument(
"--hsa-trace",
help="For Collecting HSA Traces (core + amd + image + finalizer)",
)
add_parser_bool_argument(
"--hsa-core-trace",
help="For Collecting HSA API Traces (core API)",
)
add_parser_bool_argument(
"--hsa-amd-trace",
help="For Collecting HSA API Traces (AMD-extension API)",
)
add_parser_bool_argument(
"--hsa-image-trace",
help="For Collecting HSA API Traces (Image-extenson API)",
)
add_parser_bool_argument(
"--hsa-finalizer-trace",
help="For Collecting HSA API Traces (Finalizer-extension API)",
)
add_parser_bool_argument(
"-s",
"--sys-trace",
help="For Collecting HIP, HSA, Marker (ROCTx), Memory copy, Scratch memory, and Kernel dispatch traces",
)
add_parser_bool_argument(
"-M",
"--mangled-kernels",
help="Do not demangle the kernel names",
)
add_parser_bool_argument(
"-T",
"--truncate-kernels",
help="Truncate the demangled kernel names",
)
add_parser_bool_argument(
"-L",
"--list-metrics",
help="List metrics for counter collection",
)
add_parser_bool_argument(
"--kernel-rename",
help="Use region names defined by roctxRangePush/roctxRangePop regions to rename the kernels",
)
parser.add_argument(
io_options = parser.add_argument_group("I/O options")
io_options.add_argument(
"-i",
"--input",
help="Input file for counter collection",
required=False,
)
parser.add_argument(
io_options.add_argument(
"-o",
"--output-file",
help="For the output file name",
@@ -186,7 +111,7 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
type=str,
required=False,
)
parser.add_argument(
io_options.add_argument(
"-d",
"--output-directory",
help="For adding output path where the output files will be saved",
@@ -194,7 +119,7 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
type=str,
required=False,
)
parser.add_argument(
io_options.add_argument(
"--output-format",
help="For adding output format (supported formats: csv, json, pftrace, otf2)",
nargs="+",
@@ -202,41 +127,188 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
choices=("csv", "json", "pftrace", "otf2"),
type=str.lower,
)
parser.add_argument(
io_options.add_argument(
"--log-level",
help="Set the log level",
default=None,
choices=("fatal", "error", "warning", "info", "trace", "env"),
type=str.lower,
)
parser.add_argument(
aggregate_tracing_options = parser.add_argument_group("Aggregate tracing options")
add_parser_bool_argument(
aggregate_tracing_options,
"-r",
"--runtime-trace",
help="Collect tracing data for HIP runtime API, Marker (ROCTx) API, Memory operations (copies and scratch), and Kernel dispatches. Similar to --sys-trace but without tracing HIP compiler API and the underlying HSA API.",
)
add_parser_bool_argument(
aggregate_tracing_options,
"-s",
"--sys-trace",
help="Collect tracing data for HIP API, HSA API, Marker (ROCTx) API, Memory operations (copies and scratch), and Kernel dispatches.",
)
basic_tracing_options = parser.add_argument_group("Basic tracing options")
# Add the arguments
add_parser_bool_argument(
basic_tracing_options,
"--hip-trace",
help="For collecting HIP Traces (runtime + compiler)",
)
add_parser_bool_argument(
basic_tracing_options,
"--marker-trace",
help="For collecting Marker (ROCTx) Traces",
)
add_parser_bool_argument(
basic_tracing_options,
"--kernel-trace",
help="For collecting Kernel Dispatch Traces",
)
add_parser_bool_argument(
basic_tracing_options,
"--memory-copy-trace",
help="For collecting Memory Copy Traces",
)
add_parser_bool_argument(
basic_tracing_options,
"--scratch-memory-trace",
help="For collecting Scratch Memory operations Traces",
)
add_parser_bool_argument(
basic_tracing_options,
"--hsa-trace",
help="For collecting HSA Traces (core + amd + image + finalizer)",
)
extended_tracing_options = parser.add_argument_group("Granular tracing options")
add_parser_bool_argument(
extended_tracing_options,
"--hip-runtime-trace",
help="For collecting HIP Runtime API Traces, e.g. public HIP API functions starting with 'hip' (i.e. hipSetDevice).",
)
add_parser_bool_argument(
extended_tracing_options,
"--hip-compiler-trace",
help="For collecting HIP Compiler generated code Traces, e.g. HIP API functions starting with '__hip' (i.e. __hipRegisterFatBinary).",
)
add_parser_bool_argument(
extended_tracing_options,
"--hsa-core-trace",
help="For collecting HSA API Traces (core API), e.g. HSA functions prefixed with only 'hsa_' (i.e. hsa_init).",
)
add_parser_bool_argument(
extended_tracing_options,
"--hsa-amd-trace",
help="For collecting HSA API Traces (AMD-extension API), e.g. HSA function prefixed with 'hsa_amd_' (i.e. hsa_amd_coherency_get_type).",
)
add_parser_bool_argument(
extended_tracing_options,
"--hsa-image-trace",
help="For collecting HSA API Traces (Image-extenson API), e.g. HSA functions prefixed with only 'hsa_ext_image_' (i.e. hsa_ext_image_get_capability).",
)
add_parser_bool_argument(
extended_tracing_options,
"--hsa-finalizer-trace",
help="For collecting HSA API Traces (Finalizer-extension API), e.g. HSA functions prefixed with only 'hsa_ext_program_' (i.e. hsa_ext_program_create).",
)
post_processing_options = parser.add_argument_group("Post-processing tracing options")
add_parser_bool_argument(
post_processing_options,
"--stats",
help="For collecting statistics of enabled tracing types",
)
add_parser_bool_argument(
post_processing_options,
"-S",
"--summary",
help="Output single summary of tracing data at the conclusion of the profiling session",
)
add_parser_bool_argument(
post_processing_options,
"-D",
"--summary-per-domain",
help="Output summary for each tracing domain at the conclusion of the profiling session",
)
post_processing_options.add_argument(
"--summary-groups",
help="Output a summary for each set of domains matching the regular expression, e.g. 'KERNEL_DISPATCH|MEMORY_COPY' will generate a summary from all the tracing data in the KERNEL_DISPATCH and MEMORY_COPY domains; '*._API' will generate a summary from all the tracing data in the HIP_API, HSA_API, and MARKER_API domains",
nargs="+",
default=None,
type=str,
metavar="REGULAR_EXPRESSION",
)
summary_options = parser.add_argument_group("Summary options")
summary_options.add_argument(
"--summary-output-file",
help="Output summary to a file, stdout, or stderr (default: stderr)",
default=None,
type=str,
)
summary_options.add_argument(
"-u",
"--summary-units",
help="Timing units for output summary",
default=None,
type=str,
choices=("sec", "msec", "usec", "nsec"),
)
kernel_naming_options = parser.add_argument_group("Kernel naming options")
add_parser_bool_argument(
kernel_naming_options,
"-M",
"--mangled-kernels",
help="Do not demangle the kernel names",
)
add_parser_bool_argument(
kernel_naming_options,
"-T",
"--truncate-kernels",
help="Truncate the demangled kernel names",
)
add_parser_bool_argument(
kernel_naming_options,
"--kernel-rename",
help="Use region names defined by roctxRangePush/roctxRangePop regions to rename the kernels",
)
filter_options = parser.add_argument_group("Filtering options")
filter_options.add_argument(
"--kernel-include-regex",
help="Include the kernels matching this filter",
help="Include the kernels matching this filter from counter-collection and thread-trace data (non-matching kernels will be excluded)",
default=None,
type=str,
metavar="REGULAR_EXPRESSION",
)
parser.add_argument(
filter_options.add_argument(
"--kernel-exclude-regex",
help="Exclude the kernels matching this filter",
help="Exclude the kernels matching this filter from counter-collection and thread-trace data (applied after --kernel-include-regex option)",
default=None,
type=str,
metavar="REGULAR_EXPRESSION",
)
parser.add_argument(
filter_options.add_argument(
"--kernel-iteration-range",
help="Iteration range",
nargs="+",
default=None,
type=str,
)
parser.add_argument(
"--preload",
help="Libraries to prepend to LD_PRELOAD (usually for sanitizers)",
default=os.environ.get("ROCPROF_PRELOAD", "").split(":"),
nargs="*",
)
parser.add_argument(
perfetto_options = parser.add_argument_group("Perfetto-specific options")
perfetto_options.add_argument(
"--perfetto-backend",
help="Perfetto data collection backend. 'system' mode requires starting traced and perfetto daemons",
default=None,
@@ -244,21 +316,21 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
nargs=1,
choices=("inprocess", "system"),
)
parser.add_argument(
perfetto_options.add_argument(
"--perfetto-buffer-size",
help="Size of buffer for perfetto output in KB. default: 1 GB",
default=None,
type=int,
metavar="KB",
)
parser.add_argument(
perfetto_options.add_argument(
"--perfetto-buffer-fill-policy",
help="Policy for handling new records when perfetto has reached the buffer limit",
default=None,
type=str,
choices=("discard", "ring_buffer"),
)
parser.add_argument(
perfetto_options.add_argument(
"--perfetto-shmem-size-hint",
help="Perfetto shared memory size hint in KB. default: 64 KB",
default=None,
@@ -266,6 +338,24 @@ For MPI applications (or other job launchers such as SLURM), place rocprofv3 ins
metavar="KB",
)
display_options = parser.add_argument_group("Display options")
add_parser_bool_argument(
display_options,
"-L",
"--list-metrics",
help="List metrics for counter collection",
)
advanced_options = parser.add_argument_group("Advanced options")
advanced_options.add_argument(
"--preload",
help="Libraries to prepend to LD_PRELOAD (useful for sanitizer libraries)",
default=os.environ.get("ROCPROF_PRELOAD", "").split(":"),
nargs="*",
)
if args is None:
args = sys.argv[1:]
@@ -488,9 +578,13 @@ def run(app_args, args, **kwargs):
if not _overwrite:
pass
elif _prepend:
app_env[env_var] = "{}{}{}".format(_val, _join_char, _curr_val)
app_env[env_var] = (
"{}{}{}".format(_val, _join_char, _curr_val) if _val else _curr_val
)
elif _append:
app_env[env_var] = "{}{}{}".format(_curr_val, _join_char, _val)
app_env[env_var] = (
"{}{}{}".format(_curr_val, _join_char, _val) if _val else _curr_val
)
elif _overwrite:
_write_env_value()
else:
@@ -504,12 +598,13 @@ def run(app_args, args, **kwargs):
ROCM_DIR = os.path.dirname(ROCPROFV3_DIR)
ROCPROF_TOOL_LIBRARY = f"{ROCM_DIR}/lib/rocprofiler-sdk/librocprofiler-sdk-tool.so"
ROCPROF_SDK_LIBRARY = f"{ROCM_DIR}/lib/librocprofiler-sdk.so"
ROCPROF_ROCTX_LIBRARY = f"{ROCM_DIR}/lib/librocprofiler-sdk-roctx.so"
args.preload = [itr for itr in args.preload if itr]
if args.preload:
update_env("LD_PRELOAD", ":".join(args.preload), prepend=True)
prepend_preload = [itr for itr in args.preload if itr]
append_preload = [ROCPROF_TOOL_LIBRARY, ROCPROF_SDK_LIBRARY]
update_env("LD_PRELOAD", f"{ROCPROF_TOOL_LIBRARY}:{ROCPROF_SDK_LIBRARY}", append=True)
update_env("LD_PRELOAD", ":".join(prepend_preload), prepend=True)
update_env("LD_PRELOAD", ":".join(append_preload), append=True)
update_env(
"ROCP_TOOL_LIBRARIES",
@@ -555,6 +650,16 @@ def run(app_args, args, **kwargs):
):
setattr(args, itr, True)
if args.runtime_trace:
for itr in (
"hip_runtime_trace",
"marker_trace",
"kernel_trace",
"memory_copy_trace",
"scratch_memory_trace",
):
setattr(args, itr, True)
if args.hip_trace:
for itr in ("compiler", "runtime"):
setattr(args, f"hip_{itr}_trace", True)
@@ -584,14 +689,54 @@ def run(app_args, args, **kwargs):
trace_count += 1 if val else 0
trace_opts += ["--{}".format(opt.replace("_", "-"))]
if trace_count == 0 and args.stats:
fatal_error(
"No tracing options were enabled for --stats option. Tracing options:\n\t{}".format(
"\n\t".join(trace_opts)
)
)
# if marker tracing was requested, LD_PRELOAD the rocprofiler-sdk-roctx library
# to override the roctx symbols of an app linked to the old roctracer roctx
# if args.marker_trace:
# update_env("LD_PRELOAD", ROCPROF_ROCTX_LIBRARY, append=True)
if trace_count == 0:
# if no tracing was enabled but the options below were enabled, raise an error
for oitr in [
"stats",
"summary",
"summary-per-domain",
"summary-groups",
"summary-output-file",
"summary-units",
]:
_attr = oitr.replace("-", "_")
if not hasattr(args, _attr):
fatal_error(
f"Internal error. parser does not support --{oitr} argument (i.e. args.{_attr})"
)
elif getattr(args, _attr):
_len = max([len(f"{key}") for key in args.keys()])
_args = "\n\t".join(
sorted([f"{key:<{_len}} = {val}" for key, val in args.items()])
)
fatal_error(
"No tracing options were enabled for --{} option.\nConfiguration argument values:\n\t{}\nTracing options:\n\t{}".format(
oitr, f"{_args}", "\n\t".join(trace_opts)
)
)
_summary_groups = "##@@##".join(args.summary_groups) if args.summary_groups else None
_summary_output_fname = args.summary_output_file
if _summary_output_fname is None:
_summary_output_fname = "stderr"
elif _summary_output_fname.lower() in ("stdout", "stderr"):
_summary_output_fname = _summary_output_fname.lower()
update_env("ROCPROF_STATS", args.stats, overwrite_if_true=True)
update_env("ROCPROF_STATS_SUMMARY", args.summary, overwrite_if_true=True)
update_env("ROCPROF_STATS_SUMMARY_UNITS", args.summary_units, overwrite=True)
update_env("ROCPROF_STATS_SUMMARY_OUTPUT", _summary_output_fname, overwrite=True)
update_env("ROCPROF_STATS_SUMMARY_GROUPS", _summary_groups, overwrite=True)
update_env(
"ROCPROF_STATS_SUMMARY_PER_DOMAIN",
args.summary_per_domain,
overwrite_if_true=True,
)
update_env(
"ROCPROF_DEMANGLE_KERNELS",
not args.mangled_kernels,
@@ -17,19 +17,19 @@ The following sections demonstrate the use of ``rocprofv3`` for application trac
.. code-block:: bash
export PATH=$PATH:/opt/rocm/bin
export PATH=$PATH:/opt/rocm/bin
Before you start tracing or profiling your HIP application using ``rocprofv3``, build the application using:
.. code-block:: bash
cmake -B <build-directory> <source-directory> -DCMAKE_PREFIX_PATH=/opt/rocm
cmake --build <build-directory> --target all --parallel <N>
cmake -B <build-directory> <source-directory> -DCMAKE_PREFIX_PATH=/opt/rocm
cmake --build <build-directory> --target all --parallel <N>
Options
---------
Here is the list of ``rocprofv3`` command-line options. Some options are used for application tracing and some for kernel profiling while the output control options control the presentation and redirection of the generated output.
Here is the sample of commonly used ``rocprofv3`` command-line options. Some options are used for application tracing and some for kernel profiling while the output control options control the presentation and redirection of the generated output.
.. list-table:: rocprofv3 options
:header-rows: 1
@@ -38,44 +38,32 @@ Here is the list of ``rocprofv3`` command-line options. Some options are used fo
- Description
- Use
* - ``-i`` \| ``--input``
- Specifies the input file. JSON and YAML formats support configuration of all command-line options whereas the text format only supports specifying HW counters.
- Run Configuration
* - ``-d`` \| ``--output-directory``
- Specifies the path for the output files. Supports special keys: ``%hostname%``, ``%pid%``, ``%rank%``, etc.
- Output control
* - ``-o`` \| ``--output-file``
- Specifies the name of the output file. Note that this name is appended to the default names (_api_trace or counter_collection.csv) of the generated files'. Supports special keys: ``%hostname%``, ``%pid%``, ``%rank%``, etc.
- Output control
* - ``--output-format``
- For adding output format (supported formats: csv, json, pftrace)
- Output control
* - ``-r`` \| ``--runtime-trace``
- Collects HIP (runtime), memory copy, marker, scratch memory, and kernel dispatch traces.
- Application Tracing
* - ``-s`` \| ``--sys-trace``
- Collects HIP, HSA, memory copy, marker, scratch memory, and kernel dispatch traces.
- Application Tracing
* - ``--hip-trace``
- Collects HIP runtime traces.
- Application tracing
* - ``--hip-runtime-trace``
- Collects HIP runtime API traces.
- Application tracing
* - ``--hip-compiler-trace``
- Collects HIP compiler-generated code traces.
- Application tracing
* - ``--scratch-memory-trace``
- Collects scratch memory operations traces.
- Application tracing
* - ``--hsa-trace``
- Collects HSA API traces.
- Application tracing
* - ``--hsa-core-trace``
- Collects HSA API traces (core API).
- Application tracing
* - ``--hsa-amd-trace``
- Collects HSA API traces (AMD-extension API).
- Application tracing
* - ``--hsa-image-trace``
- Collects HSA API Traces (Image-extension API).
- Application tracing
* - ``--hsa-finalizer-trace``
- Collects HSA API traces (Finalizer-extension API).
- Application tracing
* - ``--stats``
- For Collecting statistics of enabled tracing types
- Collects HIP runtime and compiler traces.
- Application tracing
* - ``--kernel-trace``
@@ -90,37 +78,49 @@ Here is the list of ``rocprofv3`` command-line options. Some options are used fo
- Collects memory copy traces.
- Application tracing
* - ``--sys-trace``
- Collects HIP, HSA, memory copy, marker, and kernel dispatch traces.
- Application Tracing
* - ``--scratch-memory-trace``
- Collects scratch memory operations traces.
- Application tracing
* - ``-i``
- Specifies the input file.
- Kernel profiling with text file. Tracing and profiling with JSON and YAML.
* - ``--hsa-trace``
- Collects HSA API traces.
- Application tracing
* - ``--hip-runtime-trace``
- Collects HIP runtime API traces.
- Application tracing
* - ``--hsa-core-trace``
- Collects HSA API traces (core API).
- Application tracing
* - ``--hsa-amd-trace``
- Collects HSA API traces (AMD-extension API).
- Application tracing
* - ``--stats``
- For Collecting statistics of enabled tracing types
- Application tracing
* - ``-p`` \| ``--summary``
- Display summary of collected data
- Application tracing
* - ``--kernel-include-regex``
- Include the kernels matching this filter.
- Kernel profiling
- Kernel Dispatch Counter Collection
* - ``--kernel-exclude-regex``
- Exclude the kernels matching this filter.
- Kernel profiling
- Kernel Dispatch Counter Collection
* - ``--kernel-iteration-range``
- Iteration range for each kernel that match the filter [start-stop].
- Kernel profiling
- Kernel Dispatch Counter Collection
* - ``-L`` \| ``--list-metrics``
- List metrics for counter collection.
- Kernel profiling
* - ``-d`` \| ``--output-directory``
- Specifies the path for the output files.
- Output control
* - ``-o`` \| ``--output-file``
- Specifies the name of the output file. Note that this name is appended to the default names (_api_trace or counter_collection.csv) of the generated files'.
- Output control
- Kernel Dispatch Counter Collection
* - ``-M`` \| ``--mangled-kernels``
- Overrides the default demangling of kernel names.
@@ -130,19 +130,11 @@ Here is the list of ``rocprofv3`` command-line options. Some options are used fo
- Truncates the demangled kernel names for improved readability.
- Output control
* - ``--output-format``
- For adding output format (supported formats: csv, json, pftrace)
- Output control
* - ``--preload``
- Libraries to prepend to LD_PRELOAD (usually for sanitizers)
- Extension
You can also see all the ``rocprofv3`` options using:
You can see the full list of all the ``rocprofv3`` options using:
.. code-block:: bash
rocprofv3 --help
rocprofv3 --help
Application tracing
---------------------
@@ -153,7 +145,7 @@ To use ``rocprofv3`` for application tracing, run:
.. code-block:: bash
rocprofv3 <tracing_option> -- <app_relative_path>
rocprofv3 <tracing_option> -- <app_relative_path>
HIP trace
+++++++++++
@@ -164,15 +156,15 @@ To trace HIP runtime APIs, use:
.. code-block:: bash
rocprofv3 --hip-trace -- < app_relative_path >
rocprofv3 --hip-trace -- < app_relative_path >
The above command generates a `hip_api_trace.csv` file prefixed with the process ID.
The above command generates a ``hip_api_trace.csv`` file prefixed with the process ID.
.. code-block:: shell
$ cat 238_hip_api_trace.csv
$ cat 238_hip_api_trace.csv
Here are the contents of `hip_api_trace.csv` file:
Here are the contents of ``hip_api_trace.csv`` file:
.. csv-table:: HIP runtime api trace
:file: /data/hip_compile_trace.csv
@@ -183,15 +175,15 @@ To trace HIP compile time APIs, use:
.. code-block:: shell
rocprofv3 --hip-compiler-trace -- < app_relative_path >
rocprofv3 --hip-compiler-trace -- < app_relative_path >
The above command generates a `hip_api_trace.csv` file prefixed with the process ID.
The above command generates a ``hip_api_trace.csv`` file prefixed with the process ID.
.. code-block:: shell
$ cat 208_hip_api_trace.csv
$ cat 208_hip_api_trace.csv
Here are the contents of `hip_api_trace.csv` file:
Here are the contents of ``hip_api_trace.csv`` file:
.. csv-table:: HIP compile time api trace
:file: /data/hip_compile_trace.csv
@@ -209,18 +201,18 @@ HSA trace contains the start and end time of HSA runtime API calls and their asy
.. code-block:: bash
rocprofv3 --hsa-trace -- < app_relative_path >
rocprofv3 --hsa-trace -- < app_relative_path >
The above command generates a `hsa_api_trace.csv` file prefixed with process ID. Note that the contents of this file have been truncated for demonstration purposes.
The above command generates a ``hsa_api_trace.csv`` file prefixed with process ID. Note that the contents of this file have been truncated for demonstration purposes.
.. code-block:: shell
$ cat 197_hsa_api_trace.csv
$ cat 197_hsa_api_trace.csv
Here are the contents of `hsa_api_trace.csv` file:
Here are the contents of ``hsa_api_trace.csv`` file:
.. csv-table:: HSA api trace
:file: /data/hsa_trace.csv
:file: /data/hsa_api_trace.csv
:widths: 10,10,10,10,10,20,20
:header-rows: 1
@@ -231,7 +223,7 @@ Marker trace
In certain situations, such as debugging performance issues in large-scale GPU programs, API-level tracing might be too fine-grained to provide a big picture of the program execution. In such cases, it is helpful to define specific tasks to be traced.
To specify the tasks for tracing, enclose the respective source code with the API calls provided by the `ROCTX` library. This process is also known as instrumentation. As the scope of code for instrumentation is defined using the enclosing API calls, it is called a range. A range is a programmer-defined task that has a well-defined start and end code scope. You can also refine the scope specified within a range using further nested ranges. ``rocprofv3`` also reports the timelines for these nested ranges.
To specify the tasks for tracing, enclose the respective source code with the API calls provided by the ``ROCTX`` library. This process is also known as instrumentation. As the scope of code for instrumentation is defined using the enclosing API calls, it is called a range. A range is a programmer-defined task that has a well-defined start and end code scope. You can also refine the scope specified within a range using further nested ranges. ``rocprofv3`` also reports the timelines for these nested ranges.
Here is a list of useful APIs for code instrumentation.
@@ -241,41 +233,41 @@ Here is a list of useful APIs for code instrumentation.
- ``roctxRangePop``: Stops the current nested range.
- ``roctxRangeStop``: Stops the given range.
See how to use `rocTX` APIs in the MatrixTranspose application below:
See how to use ``rocTX`` APIs in the MatrixTranspose application below:
.. code-block:: bash
roctxMark("before hipLaunchKernel");
int rangeId = roctxRangeStart("hipLaunchKernel range");
roctxRangePush("hipLaunchKernel");
roctxMark("before hipLaunchKernel");
int rangeId = roctxRangeStart("hipLaunchKernel range");
roctxRangePush("hipLaunchKernel");
// Launching kernel from host
hipLaunchKernelGGL(matrixTranspose, dim3(WIDTH/THREADS_PER_BLOCK_X, WIDTH/THREADS_PER_BLOCK_Y), dim3(THREADS_PER_BLOCK_X, THREADS_PER_BLOCK_Y), 0,0,gpuTransposeMatrix,gpuMatrix, WIDTH);
// Launching kernel from host
hipLaunchKernelGGL(matrixTranspose, dim3(WIDTH/THREADS_PER_BLOCK_X, WIDTH/THREADS_PER_BLOCK_Y), dim3(THREADS_PER_BLOCK_X, THREADS_PER_BLOCK_Y), 0,0,gpuTransposeMatrix,gpuMatrix, WIDTH);
roctxMark("after hipLaunchKernel");
roctxMark("after hipLaunchKernel");
// Memory transfer from device to host
roctxRangePush("hipMemcpy");
// Memory transfer from device to host
roctxRangePush("hipMemcpy");
hipMemcpy(TransposeMatrix, gpuTransposeMatrix, NUM * sizeof(float), hipMemcpyDeviceToHost);
hipMemcpy(TransposeMatrix, gpuTransposeMatrix, NUM * sizeof(float), hipMemcpyDeviceToHost);
roctxRangePop(); // for "hipMemcpy"
roctxRangePop(); // for "hipLaunchKernel"
roctxRangeStop(rangeId);
roctxRangePop(); // for "hipMemcpy"
roctxRangePop(); // for "hipLaunchKernel"
roctxRangeStop(rangeId);
To trace the API calls enclosed within the range, use:
.. code-block:: bash
rocprofv3 --marker-trace -- < app_relative_path >
rocprofv3 --marker-trace -- < app_relative_path >
Running the preceding command generates a `marker_api_trace.csv` file prefixed with the process ID.
Running the preceding command generates a ``marker_api_trace.csv`` file prefixed with the process ID.
.. code-block:: shell
$ cat 210_marker_api_trace.csv
$ cat 210_marker_api_trace.csv
Here are the contents of `marker_api_trace.csv` file:
Here are the contents of ``marker_api_trace.csv`` file:
.. csv-table:: Marker api trace
:file: /data/marker_api_trace.csv
@@ -291,15 +283,15 @@ To trace kernel dispatch traces, use:
.. code-block:: shell
rocprofv3 --kernel-trace -- < app_relative_path >
rocprofv3 --kernel-trace -- < app_relative_path >
The above command generates a `kernel_trace.csv` file prefixed with the process ID.
The above command generates a ``kernel_trace.csv`` file prefixed with the process ID.
.. code-block:: shell
$ cat 199_kernel_trace.csv
$ cat 199_kernel_trace.csv
Here are the contents of `kernel_trace.csv` file:
Here are the contents of ``kernel_trace.csv`` file:
.. csv-table:: Kernel trace
:file: /data/kernel_trace.csv
@@ -315,15 +307,15 @@ To trace memory moves across the application, use:
.. code-block:: shell
rocprofv3 -memory-copy-trace -- < app_relative_path >
rocprofv3 -memory-copy-trace -- < app_relative_path >
The above command generates a `memory_copy_trace.csv` file prefixed with the process ID.
The above command generates a ``memory_copy_trace.csv`` file prefixed with the process ID.
.. code-block:: shell
$ cat 197_memory_copy_trace.csv
$ cat 197_memory_copy_trace.csv
Here are the contents of `memory_copy_trace.csv` file:
Here are the contents of ``memory_copy_trace.csv`` file:
.. csv-table:: Memory copy trace
:file: /data/memory_copy_trace.csv
@@ -332,16 +324,36 @@ Here are the contents of `memory_copy_trace.csv` file:
For the description of the fields in the output file, see :ref:`output-file-fields`.
Sys trace
+++++++++++
Runtime trace
+++++++++++++++
This is a short-hand option which attempts to target the most relevant tracing options for a standard user by
excluding tracing the HSA runtime API and HIP compiler API.
The HSA runtime API is excluded because it is a lower-level API upon which HIP and OpenMP target are built and
thus, tends to be an implementation detail not relevant to most users. The HIP compiler API is excluded
because these are functions which are automatically inserted during HIP compilation and thus, also tend to be
implementation details which are not relevant to most users.
At present, `--runtime-trace` enables tracing the HIP runtime API, the marker API, kernel dispatches, and
memory operations (copies and scratch).
.. code-block:: shell
rocprofv3 -runtime-trace -- < app_relative_path >
Running the above command generates ``hip_api_trace.csv``, ``kernel_trace.csv``, ``memory_copy_trace.csv``, ``scratch_memory_trace.csv``,and ``marker_api_trace.csv`` (if ``rocTX`` APIs are specified in the application) files prefixed with the process ID.
System trace
++++++++++++++
This is an all-inclusive option to collect all the above-mentioned traces.
.. code-block:: shell
rocprofv3 -sys-trace -- < app_relative_path >
rocprofv3 -sys-trace -- < app_relative_path >
Running the above command generates `hip_api_trace.csv`, `hsa_api_trace.csv`, `kernel_trace.csv`, `memory_copy_trace.csv`, and `marker_api_trace.csv` (if `rocTX` APIs are specified in the application) files prefixed with the process ID.
Running the above command generates ``hip_api_trace.csv``, ``hsa_api_trace.csv``, ``kernel_trace.csv``, ``memory_copy_trace.csv``, and ``marker_api_trace.csv`` (if ``rocTX`` APIs are specified in the application) files prefixed with the process ID.
Scratch memory trace
++++++++++++++++++++++
@@ -350,7 +362,7 @@ This option collects scratch memory operation's traces. Scratch is an address sp
.. code-block:: shell
rocprofv3 --scratch-memory-trace -- < app_relative_path >
rocprofv3 --scratch-memory-trace -- < app_relative_path >
Stats
++++++++
@@ -360,18 +372,18 @@ A higher percentage in statistics can help user focus on the API/function that h
.. code-block:: shell
rocprofv3 --stats --hip-trace -- < app_relative_path >
rocprofv3 --stats --hip-trace -- < app_relative_path >
The above command generates a `hip_stats.csv` and `hip_api_trace` file prefixed with the process ID.
The above command generates a ``hip_api_stats.csv`` and ``hip_api_trace`` file prefixed with the process ID.
.. code-block:: shell
$ cat hip_stats.csv
$ cat hip_api_stats.csv
Here are the contents of `hip_stats.csv` file:
Here are the contents of ``hip_api_stats.csv`` file:
.. csv-table:: HIP stats
:file: /data/hip_stats.csv
:file: /data/hip_api_stats.csv
:widths: 10,10,20,20,10,10,10,10
:header-rows: 1
@@ -454,37 +466,37 @@ Properties
.. code-block:: shell
$ cat input.txt
$ cat input.txt
pmc: GPUBusy SQ_WAVES
pmc: GRBM_GUI_ACTIVE
pmc: GPUBusy SQ_WAVES
pmc: GRBM_GUI_ACTIVE
.. code-block:: shell
$ cat input.json
$ cat input.json
{
"jobs": [
{
"pmc": ["SQ_WAVES", "GRBM_COUNT", "GUI_ACTIVE"]
},
{
"pmc": ["FETCH_SIZE", "WRITE_SIZE"],
"kernel_include_regex": ".*_kernel",
"kernel_exclude_regex": "multiply",
"kernel_iteration_range": "[1-2]","[3-4]"
"output_file": "out",
"output_format": [
"csv",
"json"
],
"truncate_kernels": true
]
}
{
"jobs": [
{
"pmc": ["SQ_WAVES", "GRBM_COUNT", "GUI_ACTIVE"]
},
{
"pmc": ["FETCH_SIZE", "WRITE_SIZE"],
"kernel_include_regex": ".*_kernel",
"kernel_exclude_regex": "multiply",
"kernel_iteration_range": "[1-2]","[3-4]"
"output_file": "out",
"output_format": [
"csv",
"json"
],
"truncate_kernels": true
]
}
.. code-block:: shell
$ cat input.yaml
$ cat input.yaml
jobs:
- pmc:
@@ -505,19 +517,19 @@ To supply the input file for kernel profiling, use:
.. code-block:: shell
rocprofv3 -i input.txt -- <app_relative_path>
rocprofv3 -i input.txt -- <app_relative_path>
Running the above command generates a `./pmc_n/counter_collection.csv` file prefixed with the process ID. For each ``pmc`` row, a directory ``pmc_n`` containing a `counter_collection.csv` file is generated, where n = 1 for the first row and so on.
Running the above command generates a ``./pmc_n/counter_collection.csv`` file prefixed with the process ID. For each ``pmc`` row, a directory ``pmc_n`` containing a ``counter_collection.csv`` file is generated, where n = 1 for the first row and so on.
In case of JSON or YAML input file, for each job, a directory ``pass_n`` containing a `counter_collection.csv` file is generated where n = 1...N jobs.
In case of JSON or YAML input file, for each job, a directory ``pass_n`` containing a ``counter_collection.csv`` file is generated where n = 1...N jobs.
Each row of the CSV file is an instance of kernel execution. Here is a truncated version of the output file from ``pmc_1``:
.. code-block:: shell
$ cat pmc_1/218_counter_collection.csv
$ cat pmc_1/218_counter_collection.csv
Here are the contents of `counter_collection.csv` file:
Here are the contents of ``counter_collection.csv`` file:
.. csv-table:: Counter collection
:file: /data/counter_collection.csv
@@ -534,28 +546,28 @@ and an iteration range (set of iterations of the included kernels). If the itera
.. code-block:: shell
$ cat input.yml
jobs:
- pmc: [SQ_WAVES]
kernel_include_regex: "divide"
kernel_exclude_regex: ""
kernel_iteration_range: "[1, 2, [5-8]]"
$ cat input.yml
jobs:
- pmc: [SQ_WAVES]
kernel_include_regex: "divide"
kernel_exclude_regex: ""
kernel_iteration_range: "[1, 2, [5-8]]"
Agent info
++++++++++++
.. note::
All tracing and counter collection options generate an additional `agent_info.csv` file prefixed with the process ID.
All tracing and counter collection options generate an additional ``agent_info.csv`` file prefixed with the process ID.
The `agent_info.csv` file contains information about the CPU or GPU the kernel runs on.
The ``agent_info.csv`` file contains information about the CPU or GPU the kernel runs on.
.. code-block:: shell
$ cat 238_agent_info.csv
$ cat 238_agent_info.csv
"Node_Id","Logical_Node_Id","Agent_Type","Cpu_Cores_Count","Simd_Count","Cpu_Core_Id_Base","Simd_Id_Base","Max_Waves_Per_Simd","Lds_Size_In_Kb","Gds_Size_In_Kb","Num_Gws","Wave_Front_Size","Num_Xcc","Cu_Count","Array_Count","Num_Shader_Banks","Simd_Arrays_Per_Engine","Cu_Per_Simd_Array","Simd_Per_Cu","Max_Slots_Scratch_Cu","Gfx_Target_Version","Vendor_Id","Device_Id","Location_Id","Domain","Drm_Render_Minor","Num_Sdma_Engines","Num_Sdma_Xgmi_Engines","Num_Sdma_Queues_Per_Engine","Num_Cp_Queues","Max_Engine_Clk_Ccompute","Max_Engine_Clk_Fcompute","Sdma_Fw_Version","Fw_Version","Capability","Cu_Per_Engine","Max_Waves_Per_Cu","Family_Id","Workgroup_Max_Size","Grid_Max_Size","Local_Mem_Size","Hive_Id","Gpu_Id","Workgroup_Max_Dim_X","Workgroup_Max_Dim_Y","Workgroup_Max_Dim_Z","Grid_Max_Dim_X","Grid_Max_Dim_Y","Grid_Max_Dim_Z","Name","Vendor_Name","Product_Name","Model_Name"
0,0,"CPU",24,0,0,0,0,0,0,0,0,1,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3800,0,0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,0,"AMD Ryzen 9 3900X 12-Core Processor","CPU","AMD Ryzen 9 3900X 12-Core Processor",""
1,1,"GPU",0,256,0,2147487744,10,64,0,64,64,1,64,4,4,1,16,4,32,90000,4098,26751,12032,0,128,2,0,2,24,3800,1630,432,440,138420864,16,40,141,1024,4294967295,0,0,64700,1024,1024,1024,4294967295,4294967295,4294967295,"gfx900","AMD","Radeon RX Vega","vega10"
"Node_Id","Logical_Node_Id","Agent_Type","Cpu_Cores_Count","Simd_Count","Cpu_Core_Id_Base","Simd_Id_Base","Max_Waves_Per_Simd","Lds_Size_In_Kb","Gds_Size_In_Kb","Num_Gws","Wave_Front_Size","Num_Xcc","Cu_Count","Array_Count","Num_Shader_Banks","Simd_Arrays_Per_Engine","Cu_Per_Simd_Array","Simd_Per_Cu","Max_Slots_Scratch_Cu","Gfx_Target_Version","Vendor_Id","Device_Id","Location_Id","Domain","Drm_Render_Minor","Num_Sdma_Engines","Num_Sdma_Xgmi_Engines","Num_Sdma_Queues_Per_Engine","Num_Cp_Queues","Max_Engine_Clk_Ccompute","Max_Engine_Clk_Fcompute","Sdma_Fw_Version","Fw_Version","Capability","Cu_Per_Engine","Max_Waves_Per_Cu","Family_Id","Workgroup_Max_Size","Grid_Max_Size","Local_Mem_Size","Hive_Id","Gpu_Id","Workgroup_Max_Dim_X","Workgroup_Max_Dim_Y","Workgroup_Max_Dim_Z","Grid_Max_Dim_X","Grid_Max_Dim_Y","Grid_Max_Dim_Z","Name","Vendor_Name","Product_Name","Model_Name"
0,0,"CPU",24,0,0,0,0,0,0,0,0,1,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3800,0,0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,0,"AMD Ryzen 9 3900X 12-Core Processor","CPU","AMD Ryzen 9 3900X 12-Core Processor",""
1,1,"GPU",0,256,0,2147487744,10,64,0,64,64,1,64,4,4,1,16,4,32,90000,4098,26751,12032,0,128,2,0,2,24,3800,1630,432,440,138420864,16,40,141,1024,4294967295,0,0,64700,1024,1024,1024,4294967295,4294967295,4294967295,"gfx900","AMD","Radeon RX Vega","vega10"
Kernel filtering
+++++++++++++++++
@@ -567,24 +579,24 @@ Here is an input file with kernel filters:
.. code-block:: shell
$ cat input.yml
jobs:
- pmc: [SQ_WAVES]
kernel_include_regex: "divide"
kernel_exclude_regex: ""
$ cat input.yml
jobs:
- pmc: [SQ_WAVES]
kernel_include_regex: "divide"
kernel_exclude_regex: ""
To collect counters for the kernels matching the filters specified in the preceding input file, run:
.. code-block:: shell
rocprofv3 -i input.yml -- <app_relative_path>
rocprofv3 -i input.yml -- <app_relative_path>
$ cat pass_1/312_counter_collection.csv
"Correlation_Id","Dispatch_Id","Agent_Id","Queue_Id","Process_Id","Thread_Id","Grid_Size","Kernel_Name","Workgroup_Size","LDS_Block_Size","Scratch_Size","VGPR_Count","SGPR_Count","Counter_Name","Counter_Value"
4,4,1,1,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
8,8,1,2,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
12,12,1,3,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
16,16,1,4,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
$ cat pass_1/312_counter_collection.csv
"Correlation_Id","Dispatch_Id","Agent_Id","Queue_Id","Process_Id","Thread_Id","Grid_Size","Kernel_Name","Workgroup_Size","LDS_Block_Size","Scratch_Size","VGPR_Count","SGPR_Count","Counter_Name","Counter_Value"
4,4,1,1,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
8,8,1,2,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
12,12,1,3,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
16,16,1,4,36499,36499,1048576,"divide_kernel(float*, float const*, float const*, int, int)",64,0,0,12,16,"SQ_WAVES",16384
.. _output-file-fields:
@@ -3,7 +3,7 @@
# Installation of public C++ headers (implementations)
#
#
set(ROCPROFILER_CXX_DETAILS_HEADER_FILES delimit.hpp join.hpp mpl.hpp name_info.hpp)
set(ROCPROFILER_CXX_DETAILS_HEADER_FILES tokenize.hpp join.hpp mpl.hpp name_info.hpp)
install(
FILES ${ROCPROFILER_CXX_DETAILS_HEADER_FILES}
@@ -105,6 +105,64 @@ tokenize(
return _result;
}
/// \brief tokenize a string into a set
///
template <typename ContainerT = std::vector<std::string>,
typename DelimT = std::string_view,
typename ValueT = typename ContainerT::value_type,
typename PredicateT = ValueT (*)(DelimT&&)>
inline ContainerT
tokenize(
std::string_view line,
const std::vector<DelimT>& delimiters,
PredicateT&& predicate = [](DelimT&& s) -> ValueT { return ValueT{s}; })
{
ContainerT _result = {};
size_t _start = 0;
size_t _end = std::string::npos;
while(_start != std::string::npos)
{
_end = std::string::npos;
// Find the earliest occurrence of any delimiter
for(const auto& itr : delimiters)
{
size_t pos = line.find(itr, _start);
if(pos != std::string::npos && (_end == std::string::npos || pos < _end))
{
_end = pos;
}
}
// Extract token and update start position
if(_end != std::string::npos)
{
mpl::emplace(_result,
std::forward<PredicateT>(predicate)(line.substr(_start, _end - _start)));
_start = _end;
// Move start past the delimiter
for(const auto& delimiter : delimiters)
{
if(line.compare(_start, delimiter.size(), delimiter) == 0)
{
_start += delimiter.size();
break;
}
}
}
else
{
// Last token after the final delimiter
mpl::emplace(_result, std::forward<PredicateT>(predicate)(line.substr(_start)));
break;
}
}
return _result;
}
/// \brief apply a string transformation to substring in between a common delimiter.
///
template <typename PredicateT = std::function<std::string(const std::string&)>>
@@ -13,6 +13,7 @@ set(TOOL_HEADERS
generateJSON.hpp
generateOTF2.hpp
generatePerfetto.hpp
generateStats.hpp
helper.hpp
output_file.hpp
statistics.hpp
@@ -26,9 +27,11 @@ set(TOOL_SOURCES
generateJSON.cpp
generateOTF2.cpp
generatePerfetto.cpp
generateStats.cpp
helper.cpp
main.c
output_file.cpp
statistics.cpp
tmp_file_buffer.cpp
tmp_file.cpp
tool.cpp)
@@ -59,7 +59,7 @@ struct buffered_output
operator bool() const { return enabled; }
std::deque<Tp> element_data = {};
stats_data_t stats = {};
stats_entry_t stats = {};
private:
bool enabled = false;
@@ -28,12 +28,14 @@
#include "lib/common/environment.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/units.hpp"
#include "lib/common/utility.hpp"
#include <rocprofiler-sdk/cxx/details/delimit.hpp>
#include <rocprofiler-sdk/cxx/details/tokenize.hpp>
#include <fmt/core.h>
#include <linux/limits.h>
#include <unistd.h>
#include <algorithm>
#include <chrono>
@@ -44,6 +46,7 @@
#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
namespace rocprofiler
@@ -67,8 +70,14 @@ const auto* launch_clock = as_pointer(std::chrono::system_clock::now());
const auto* launch_datetime =
get_local_datetime(get_env("ROCP_TIME_FORMAT", "%F_%H.%M"), launch_time);
const auto env_regexes =
new std::array<std::regex, 2>{std::regex{"(.*)%(env|ENV)\\{([A-Z0-9_]+)\\}%(.*)"},
std::regex{"(.*)\\$(env|ENV)\\{([A-Z0-9_]+)\\}(.*)"}};
new std::array<std::regex, 3>{std::regex{"(.*)%(env|ENV)\\{([A-Z0-9_]+)\\}%(.*)"},
std::regex{"(.*)\\$(env|ENV)\\{([A-Z0-9_]+)\\}(.*)"},
std::regex{"(.*)%q\\{([A-Z0-9_]+)\\}(.*)"}};
// env regex examples:
// - %env{USER}% Consistent with other output key formats (start+end with %)
// - $ENV{USER} Similar to CMake
// - %q{USER} Compatibility with NVIDIA
//
std::string*
get_local_datetime(const std::string& dt_format, std::time_t*& _dt_curr)
@@ -86,6 +95,22 @@ get_local_datetime(const std::string& dt_format, std::time_t*& _dt_curr)
return nullptr;
}
std::string
get_hostname()
{
auto _hostname_buff = std::array<char, PATH_MAX>{};
_hostname_buff.fill('\0');
if(gethostname(_hostname_buff.data(), _hostname_buff.size() - 1) != 0)
{
auto _err = errno;
ROCP_WARNING << "Hostname unknown. gethostname failed with error code " << _err << ": "
<< strerror(_err);
return std::string{"UNKNOWN_HOSTNAME"};
}
return std::string{_hostname_buff.data()};
}
inline bool
not_is_space(int ch)
{
@@ -279,6 +304,34 @@ config::config()
const auto supported_perfetto_backends = std::set<std::string_view>{"inprocess", "system"};
LOG_IF(FATAL, supported_perfetto_backends.count(perfetto_backend) == 0)
<< "Unsupported perfetto backend type: " << perfetto_backend;
if(stats_summary_unit == "sec")
stats_summary_unit_value = common::units::sec;
else if(stats_summary_unit == "msec")
stats_summary_unit_value = common::units::msec;
else if(stats_summary_unit == "usec")
stats_summary_unit_value = common::units::usec;
else if(stats_summary_unit == "nsec")
stats_summary_unit_value = common::units::nsec;
else
{
ROCP_FATAL << "Unsupported summary units value: " << stats_summary_unit;
}
if(auto _summary_grps = get_env("ROCPROF_STATS_SUMMARY_GROUPS", ""); !_summary_grps.empty())
{
stats_summary_groups =
sdk::parse::tokenize(_summary_grps, std::vector<std::string_view>{"##@@##"});
// remove any empty strings (just in case these slipped through)
stats_summary_groups.erase(std::remove_if(stats_summary_groups.begin(),
stats_summary_groups.end(),
[](const auto& itr) { return itr.empty(); }),
stats_summary_groups.end());
}
// enable summary output if any of these are enabled
summary_output = (stats_summary || stats_summary_per_domain || !stats_summary_groups.empty());
}
std::vector<output_key>
@@ -374,8 +427,10 @@ output_keys(std::string _tag)
}
auto _launch_time = (launch_datetime) ? *launch_datetime : std::string{".UNKNOWN_LAUNCH_TIME."};
auto _hostname = get_hostname();
for(auto&& itr : std::initializer_list<output_key>{
{"%hostname%", _hostname, "Network hostname"},
{"%pid%", _proc_id, "Process identifier"},
{"%ppid%", _parent_id, "Parent process identifier"},
{"%pgid%", _pgroup_id, "Process group identifier"},
@@ -392,6 +447,7 @@ output_keys(std::string _tag)
}
for(auto&& itr : std::initializer_list<output_key>{
{"%h", _hostname, "Shorthand for %hostname%"},
{"%p", _proc_id, "Shorthand for %pid%"},
{"%j", _slurm_job_id, "Shorthand for %job%"},
{"%r", _slurm_proc_id, "Shorthand for %rank%"},
@@ -404,8 +460,10 @@ output_keys(std::string _tag)
return _options;
}
namespace
{
std::string
format(std::string _fpath, const std::string& _tag)
format_impl(std::string _fpath, const std::vector<output_key>& _keys)
{
if(_fpath.find('%') == std::string::npos && _fpath.find('$') == std::string::npos)
return _fpath;
@@ -416,20 +474,36 @@ format(std::string _fpath, const std::string& _tag)
_v.replace(pos, pitr.key.length(), pitr.value);
};
for(auto&& itr : output_keys(_tag))
for(auto&& itr : _keys)
_replace(_fpath, itr);
// environment and configuration variables
try
{
auto strip_leading_and_replace =
[](std::string_view inp_v, std::initializer_list<char> keys, const char* val) {
auto inp = std::string{inp_v};
for(auto key : keys)
{
auto pos = std::string::npos;
while((pos = inp.find(key)) == 0)
inp = inp.substr(pos + 1);
while((pos = inp.find(key)) != std::string::npos)
inp = inp.replace(pos, 1, val);
}
return inp;
};
for(const auto& _re : *env_regexes)
{
while(std::regex_search(_fpath, _re))
{
auto _var = std::regex_replace(_fpath, _re, "$3");
std::string _val = get_env<std::string>(_var, "");
auto _beg = std::regex_replace(_fpath, _re, "$1");
auto _end = std::regex_replace(_fpath, _re, "$4");
_val = strip_leading_and_replace(_val, {'\t', ' ', '/'}, "_");
auto _beg = std::regex_replace(_fpath, _re, "$1");
auto _end = std::regex_replace(_fpath, _re, "$4");
_fpath = fmt::format("{}{}{}", _beg, _val, _end);
}
}
@@ -454,6 +528,25 @@ format(std::string _fpath, const std::string& _tag)
return _fpath;
}
std::string
format(std::string _fpath, const std::vector<output_key>& _keys)
{
if(_fpath.find('%') == std::string::npos && _fpath.find('$') == std::string::npos)
return _fpath;
auto _ref = _fpath;
_fpath = format_impl(std::move(_fpath), _keys);
return (_fpath == _ref) ? _fpath : format(std::move(_fpath), _keys);
}
} // namespace
std::string
format(std::string _fpath, const std::string& _tag)
{
return format(std::move(_fpath), output_keys(_tag));
}
std::string
format_name(std::string_view _name, const config& _cfg)
{
@@ -25,8 +25,14 @@
#include "lib/common/environment.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/common/mpl.hpp"
#include <rocprofiler-sdk/cxx/serialization.hpp>
#include <fmt/format.h>
#include <set>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>
@@ -38,6 +44,8 @@ namespace tool
namespace fs = common::filesystem;
using common::get_env;
struct config;
enum class config_context
{
global = 0,
@@ -48,6 +56,19 @@ enum class config_context
perfetto_plugin,
};
void
initialize();
template <config_context ContextT = config_context::global>
config&
get_config();
std::string
format_name(std::string_view _name, const config& = get_config<>());
std::string
format(std::string _fpath, const std::string& _tag = {});
int
get_mpi_size();
@@ -67,25 +88,32 @@ struct config
bool hsa_finalizer_ext_api_trace = get_env("ROCPROF_HSA_FINALIZER_EXT_API_TRACE", false);
bool marker_api_trace = get_env("ROCPROF_MARKER_API_TRACE", false);
bool memory_copy_trace = get_env("ROCPROF_MEMORY_COPY_TRACE", false);
bool scratch_memory = get_env("ROCPROF_SCRATCH_MEMORY_TRACE", false);
bool scratch_memory_trace = get_env("ROCPROF_SCRATCH_MEMORY_TRACE", false);
bool counter_collection = get_env("ROCPROF_COUNTER_COLLECTION", false);
bool hip_runtime_api_trace = get_env("ROCPROF_HIP_RUNTIME_API_TRACE", false);
bool hip_compiler_api_trace = get_env("ROCPROF_HIP_COMPILER_API_TRACE", false);
bool list_metrics = get_env("ROCPROF_LIST_METRICS", false);
bool list_metrics_output_file = get_env("ROCPROF_OUTPUT_LIST_METRICS_FILE", false);
bool stats = get_env("ROCPROF_STATS", false);
bool stats_summary = get_env("ROCPROF_STATS_SUMMARY", false);
bool stats_summary_per_domain = get_env("ROCPROF_STATS_SUMMARY_PER_DOMAIN", false);
bool csv_output = false;
bool json_output = false;
bool pftrace_output = false;
bool otf2_output = false;
bool summary_output = false;
bool kernel_rename = get_env("ROCPROF_KERNEL_RENAME", false);
int mpi_size = get_mpi_size();
int mpi_rank = get_mpi_rank();
size_t perfetto_shmem_size_hint = get_env("ROCPROF_PERFETTO_SHMEM_SIZE_HINT_KB", 64);
size_t perfetto_buffer_size = get_env("ROCPROF_PERFETTO_BUFFER_SIZE_KB", 1024000);
std::string output_path = get_env("ROCPROF_OUTPUT_PATH", fs::current_path().string());
std::string output_file = get_env("ROCPROF_OUTPUT_FILE_NAME", std::to_string(getpid()));
std::string tmp_directory = get_env("ROCPROF_TMPDIR", output_path);
uint64_t stats_summary_unit_value = 1;
std::string stats_summary_unit = get_env("ROCPROF_STATS_SUMMARY_UNITS", "nsec");
std::string output_path = get_env("ROCPROF_OUTPUT_PATH", fs::current_path().string());
std::string output_file =
get_env("ROCPROF_OUTPUT_FILE_NAME", fmt::format("%hostname%/{}", getpid()));
std::string tmp_directory = get_env("ROCPROF_TMPDIR", output_path);
std::string stats_summary_file = get_env("ROCPROF_STATS_SUMMARY_OUTPUT", "stderr");
std::string kernel_filter_include =
get_env("ROCPROF_KERNEL_FILTER_INCLUDE_REGEX", std::string{".*"});
@@ -94,11 +122,68 @@ struct config
std::string perfetto_buffer_fill_policy =
get_env("ROCPROF_PERFETTO_BUFFER_FILL_POLICY", std::string{"discard"});
std::string perfetto_backend = get_env("ROCPROF_PERFETTO_BACKEND", std::string{"inprocess"});
std::unordered_set<uint32_t> kernel_filter_range = {};
std::set<std::string> counters = {};
std::unordered_set<uint32_t> kernel_filter_range = {};
std::set<std::string> counters = {};
std::vector<std::string> stats_summary_groups = {};
template <typename ArchiveT>
void save(ArchiveT&) const;
template <typename ArchiveT>
void load(ArchiveT&)
{}
};
template <config_context ContextT = config_context::global>
template <typename ArchiveT>
void
config::save(ArchiveT& ar) const
{
#define CFG_SERIALIZE_MEMBER(VAR) ar(cereal::make_nvp(#VAR, VAR))
#define CFG_SERIALIZE_NAMED_MEMBER(NAME, VAR) ar(cereal::make_nvp(NAME, VAR))
CFG_SERIALIZE_MEMBER(demangle);
CFG_SERIALIZE_MEMBER(truncate);
CFG_SERIALIZE_MEMBER(kernel_trace);
CFG_SERIALIZE_MEMBER(hsa_core_api_trace);
CFG_SERIALIZE_MEMBER(hsa_amd_ext_api_trace);
CFG_SERIALIZE_MEMBER(hsa_image_ext_api_trace);
CFG_SERIALIZE_MEMBER(hsa_finalizer_ext_api_trace);
CFG_SERIALIZE_MEMBER(marker_api_trace);
CFG_SERIALIZE_MEMBER(memory_copy_trace);
CFG_SERIALIZE_MEMBER(scratch_memory_trace);
CFG_SERIALIZE_MEMBER(counter_collection);
CFG_SERIALIZE_MEMBER(hip_runtime_api_trace);
CFG_SERIALIZE_MEMBER(hip_compiler_api_trace);
CFG_SERIALIZE_MEMBER(kernel_rename);
CFG_SERIALIZE_NAMED_MEMBER("summary", stats_summary);
CFG_SERIALIZE_NAMED_MEMBER("summary_per_domain", stats_summary_per_domain);
CFG_SERIALIZE_NAMED_MEMBER("summary_groups", stats_summary_groups);
CFG_SERIALIZE_NAMED_MEMBER("summary_unit", stats_summary_unit);
CFG_SERIALIZE_NAMED_MEMBER("summary_file", stats_summary_file);
CFG_SERIALIZE_MEMBER(perfetto_shmem_size_hint);
CFG_SERIALIZE_MEMBER(perfetto_buffer_size);
CFG_SERIALIZE_MEMBER(perfetto_buffer_fill_policy);
CFG_SERIALIZE_MEMBER(perfetto_backend);
CFG_SERIALIZE_NAMED_MEMBER("raw_tmp_directory", tmp_directory);
CFG_SERIALIZE_NAMED_MEMBER("raw_output_path", output_path);
CFG_SERIALIZE_NAMED_MEMBER("raw_output_file", output_file);
CFG_SERIALIZE_NAMED_MEMBER("tmp_directory", format(tmp_directory));
CFG_SERIALIZE_NAMED_MEMBER("output_path", format(output_path));
CFG_SERIALIZE_NAMED_MEMBER("output_file", format(output_file));
CFG_SERIALIZE_MEMBER(counters);
CFG_SERIALIZE_MEMBER(kernel_filter_include);
CFG_SERIALIZE_MEMBER(kernel_filter_exclude);
CFG_SERIALIZE_MEMBER(kernel_filter_range);
#undef CFG_SERIALIZE_MEMBER
#undef CFG_SERIALIZE_NAMED_MEMBER
}
template <config_context ContextT>
config&
get_config()
{
@@ -119,6 +204,11 @@ struct output_key
{
output_key(std::string _key, std::string _val, std::string _desc = {});
template <typename Tp,
typename Up = Tp,
std::enable_if_t<!common::mpl::is_string_type<Up>::value, int> = 0>
output_key(std::string _key, Tp&& _val, std::string _desc = {});
operator std::pair<std::string, std::string>() const;
std::string key = {};
@@ -126,16 +216,14 @@ struct output_key
std::string description = {};
};
template <typename Tp, typename Up, std::enable_if_t<!common::mpl::is_string_type<Up>::value, int>>
output_key::output_key(std::string _key, Tp&& _val, std::string _desc)
: key{std::move(_key)}
, value{fmt::format("{}", std::forward<Tp>(_val))}
, description{std::move(_desc)}
{}
std::vector<output_key>
output_keys(std::string _tag = {});
std::string
format(std::string _fpath, const std::string& _tag = {});
std::string
format_name(std::string_view _name, const config& = get_config<>());
void
initialize();
} // namespace tool
} // namespace rocprofiler
@@ -37,13 +37,13 @@ struct domain_type_name;
static constexpr auto filename = FILENAME; \
};
DEFINE_BUFFER_TYPE_NAME(HSA, "HSA_API", "hsa_trace")
DEFINE_BUFFER_TYPE_NAME(HIP, "HIP_API", "hip_trace")
DEFINE_BUFFER_TYPE_NAME(MEMORY_COPY, "MEMORY_COPY", "memory_copy")
DEFINE_BUFFER_TYPE_NAME(COUNTER_COLLECTION, "COUNTER_COLLECTION", "counter_collection")
DEFINE_BUFFER_TYPE_NAME(HSA, "HSA_API", "hsa_api")
DEFINE_BUFFER_TYPE_NAME(HIP, "HIP_API", "hip_api")
DEFINE_BUFFER_TYPE_NAME(MARKER, "MARKER_API", "marker_api")
DEFINE_BUFFER_TYPE_NAME(KERNEL_DISPATCH, "KERNEL_DISPATCH", "kernel_dispatch")
DEFINE_BUFFER_TYPE_NAME(MARKER, "MARKER", "marker_trace")
DEFINE_BUFFER_TYPE_NAME(MEMORY_COPY, "MEMORY_COPY", "memory_copy")
DEFINE_BUFFER_TYPE_NAME(SCRATCH_MEMORY, "SCRATCH_MEMORY", "scratch_memory")
DEFINE_BUFFER_TYPE_NAME(COUNTER_COLLECTION, "COUNTER_COLLECTION", "counter_collection")
#undef DEFINE_BUFFER_TYPE_NAME
@@ -28,11 +28,11 @@ enum class domain_type
{
HSA = 0,
HIP,
MEMORY_COPY,
COUNTER_COLLECTION,
KERNEL_DISPATCH,
MARKER,
KERNEL_DISPATCH,
MEMORY_COPY,
SCRATCH_MEMORY,
COUNTER_COLLECTION,
LAST,
};
@@ -21,9 +21,10 @@
// SOFTWARE.
#include "generateCSV.hpp"
#include "config.hpp"
#include "csv.hpp"
#include "generateStats.hpp"
#include "helper.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include "statistics.hpp"
#include <rocprofiler-sdk/fwd.h>
@@ -41,47 +42,6 @@ namespace tool
{
namespace
{
using stats_data_t = statistics<uint64_t, float_type>;
using stats_map_t = std::map<std::string_view, stats_data_t>;
struct percentage
{
float_type value = {};
friend std::ostream& operator<<(std::ostream& os, percentage val) { return (os << val.value); }
};
struct stats_formatter
{
template <typename Tp>
std::ostream& operator()(std::ostream& ofs, const Tp& _val) const
{
using value_type = common::mpl::unqualified_type_t<Tp>;
if constexpr(std::is_floating_point<value_type>::value)
{
constexpr value_type one_hundredth = 1.0e-2;
if(_val > one_hundredth)
ofs << std::setprecision(6) << std::fixed;
else
ofs << std::setprecision(8) << std::scientific;
}
else if constexpr(std::is_same<Tp, percentage>::value)
{
constexpr float_type one = 1.0;
constexpr float_type one_hundredth = 1.0e-2;
if(_val.value >= one)
ofs << std::setprecision(2) << std::fixed;
else if(_val.value > one_hundredth)
ofs << std::setprecision(4) << std::fixed;
else
ofs << std::setprecision(3) << std::scientific;
}
return ofs;
}
};
tool::output_file
get_stats_output_file(std::string name)
{
@@ -99,10 +59,10 @@ get_stats_output_file(std::string name)
}};
}
stats_data_t
write_stats(output_file&& ofs, const stats_map_t& data_v)
void
write_stats(output_file&& ofs, const stats_entry_vec_t& data_v)
{
auto data = std::vector<std::pair<std::string_view, stats_data_t>>{};
auto data = stats_entry_vec_t{};
auto _duration = stats_data_t{};
for(const auto& [id, value] : data_v)
{
@@ -136,8 +96,6 @@ write_stats(output_file&& ofs, const stats_map_t& data_v)
value.get_stddev());
ofs << _row.str() << std::flush;
}
return _duration;
}
} // namespace
@@ -275,14 +233,17 @@ generate_csv(tool_table* /*tool_functions*/, std::vector<rocprofiler_agent_v0_t>
}
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
auto kernel_stats = stats_map_t{};
auto ofs = tool::output_file{"kernel_trace",
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("kernel_stats"), stats.entries);
auto ofs = tool::output_file{"kernel_trace",
tool::csv::kernel_trace_csv_encoder{},
{"Kind",
"Agent_Id",
@@ -329,27 +290,21 @@ generate_csv(tool_table*
record.dispatch_info.grid_size.y,
record.dispatch_info.grid_size.z);
if(tool::get_config().stats)
kernel_stats[kernel_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
_duration = write_stats(get_stats_output_file("kernel_stats"), kernel_stats);
return _duration;
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
auto hip_stats = stats_map_t{};
auto ofs = tool::output_file{"hip_api_trace",
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("hip_api_stats"), stats.entries);
auto ofs = tool::output_file{"hip_api_trace",
tool::csv::api_csv_encoder{},
{"Domain",
"Function",
@@ -372,28 +327,21 @@ generate_csv(tool_table* tool
record.start_timestamp,
record.end_timestamp);
if(tool::get_config().stats)
hip_stats[api_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
{
_duration = write_stats(get_stats_output_file("hip_stats"), hip_stats);
}
return _duration;
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
auto hsa_stats = stats_map_t{};
auto ofs = tool::output_file{"hsa_api_trace",
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("hsa_api_stats"), stats.entries);
auto ofs = tool::output_file{"hsa_api_trace",
tool::csv::api_csv_encoder{},
{"Domain",
"Function",
@@ -417,28 +365,21 @@ generate_csv(tool_table* tool
record.start_timestamp,
record.end_timestamp);
if(tool::get_config().stats)
hsa_stats[api_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
{
_duration = write_stats(get_stats_output_file("hsa_stats"), hsa_stats);
}
return _duration;
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
auto memory_copy_stats = stats_map_t{};
auto ofs = tool::output_file{"memory_copy_trace",
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("memory_copy_stats"), stats.entries);
auto ofs = tool::output_file{"memory_copy_trace",
tool::csv::memory_copy_csv_encoder{},
{"Kind",
"Direction",
@@ -461,28 +402,21 @@ generate_csv(tool_table*
record.start_timestamp,
record.end_timestamp);
if(tool::get_config().stats)
memory_copy_stats[api_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
{
_duration = write_stats(get_stats_output_file("memory_copy_stats"), memory_copy_stats);
}
return _duration;
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
auto marker_stats = stats_map_t{};
auto ofs = tool::output_file{"marker_api_trace",
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("marker_api_stats"), stats.entries);
auto ofs = tool::output_file{"marker_api_trace",
tool::csv::marker_csv_encoder{},
{"Domain",
"Function",
@@ -518,25 +452,19 @@ generate_csv(tool_table* t
record.start_timestamp,
record.end_timestamp);
if(tool::get_config().stats)
marker_stats[_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
{
_duration = write_stats(get_stats_output_file("marker_stats"), marker_stats);
}
return _duration;
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_tool_counter_collection_record_t>& data)
const std::deque<rocprofiler_tool_counter_collection_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("counter_collection_stats"), stats.entries);
auto ofs = tool::output_file{"counter_collection",
tool::csv::counter_collection_csv_encoder{},
@@ -601,14 +529,17 @@ generate_csv(tool_table* too
}
ofs << row_ss.str();
}
return stats_data_t{};
}
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data)
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data,
const stats_entry_t& stats)
{
if(data.empty()) return stats_data_t{};
if(data.empty()) return;
if(tool::get_config().stats && stats)
write_stats(get_stats_output_file("scratch_memory_stats"), stats.entries);
auto ofs = tool::output_file{"scratch_memory_trace",
tool::csv::scratch_memory_encoder{},
@@ -623,7 +554,6 @@ generate_csv(tool_table*
"End_Timestamp",
}};
auto scratch_memory_stats = stats_map_t{};
for(const auto& record : data)
{
auto row_ss = std::stringstream{};
@@ -641,45 +571,38 @@ generate_csv(tool_table*
record.start_timestamp,
record.end_timestamp);
if(tool::get_config().stats)
scratch_memory_stats[op_name] += (record.end_timestamp - record.start_timestamp);
ofs << row_ss.str();
}
auto _duration = stats_data_t{};
if(tool::get_config().stats)
{
_duration =
write_stats(get_stats_output_file("scratch_memory_stats"), scratch_memory_stats);
}
return _duration;
}
void
generate_csv(tool_table* /*tool_functions*/, std::unordered_map<domain_type, stats_data_t>& data)
generate_csv(tool_table* /*tool_functions*/, const domain_stats_vec_t& data_v)
{
using csv_encoder_t = rocprofiler::tool::csv::stats_csv_encoder;
if(!tool::get_config().stats) return;
auto _data = data_v;
auto _total_stats = stats_data_t{};
for(const auto& itr : data)
_total_stats += itr.second;
for(const auto& itr : _data)
_total_stats += itr.second.total;
if(_total_stats.get_count() == 0) return;
std::sort(_data.begin(), _data.end(), [](const auto& lhs, const auto& rhs) {
return (lhs.second.total.get_sum() > rhs.second.total.get_sum());
});
auto ofs = get_stats_output_file("domain_stats");
constexpr float_type one_hundred = 100.0;
const float_type _total_duration = _total_stats.get_sum();
for(const auto& [type, value] : data)
const float_type _total_duration = _total_stats.get_sum();
for(const auto& [type, value] : _data)
{
auto name = get_domain_column_name(type);
auto duration_ns = value.get_sum();
auto calls = value.get_count();
float_type avg_ns = value.get_mean();
float_type percent_v = (duration_ns / _total_duration) * one_hundred;
auto name = get_domain_column_name(type);
auto duration_ns = value.total.get_sum();
auto calls = value.total.get_count();
auto avg_ns = value.total.get_mean();
auto percent_v = value.total.get_percent(_total_duration);
auto _row = std::stringstream{};
csv_encoder_t::write_row<stats_formatter>(_row,
@@ -688,9 +611,9 @@ generate_csv(tool_table* /*tool_functions*/, std::unordered_map<domain_type, sta
duration_ns,
avg_ns,
percentage{percent_v},
value.get_min(),
value.get_max(),
value.get_stddev());
value.total.get_min(),
value.total.get_max(),
value.total.get_stddev());
ofs << _row.str() << std::flush;
}
}
@@ -22,6 +22,7 @@
#pragma once
#include "generateStats.hpp"
#include "helper.hpp"
#include "statistics.hpp"
@@ -31,41 +32,45 @@ namespace rocprofiler
{
namespace tool
{
using float_type = double;
using stats_data_t = statistics<uint64_t, float_type>;
void
generate_csv(tool_table* tool_functions, std::vector<rocprofiler_agent_v0_t>& data);
stats_data_t
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_tool_counter_collection_record_t>& data);
stats_data_t
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data);
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions, std::unordered_map<domain_type, stats_data_t>& data);
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_tool_counter_collection_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data,
const stats_entry_t& stats);
void
generate_csv(tool_table* tool_functions, const domain_stats_vec_t& data);
} // namespace tool
} // namespace rocprofiler
@@ -24,8 +24,10 @@
#include "config.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "statistics.hpp"
#include "lib/common/string_entry.hpp"
#include "lib/common/utility.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
@@ -39,6 +41,7 @@ namespace tool
void
write_json(tool_table* tool_functions,
uint64_t pid,
const domain_stats_vec_t& domain_stats,
std::vector<rocprofiler_agent_v0_t> agent_data,
std::vector<rocprofiler_tool_counter_info_t> counter_data,
std::deque<rocprofiler_buffer_tracing_hip_api_record_t>* hip_api_deque,
@@ -52,14 +55,14 @@ write_json(tool_table* tool
{
using JSONOutputArchive = cereal::MinimalJSONOutputArchive;
constexpr auto json_prec = 32;
constexpr auto json_indent = JSONOutputArchive::Options::IndentChar::space;
auto json_opts = JSONOutputArchive::Options{json_prec, json_indent, 1};
auto filename = std::string_view{"results"};
auto [output_stream, cleanup] = get_output_stream(filename, ".json");
constexpr auto json_prec = 32;
constexpr auto json_indent = JSONOutputArchive::Options::IndentChar::space;
auto json_opts = JSONOutputArchive::Options{json_prec, json_indent, 1};
auto filename = std::string_view{"results"};
auto ofs = get_output_stream(filename, ".json");
{
auto json_ar = JSONOutputArchive{*output_stream, json_opts};
auto json_ar = JSONOutputArchive{*ofs.stream, json_opts};
json_ar.setNextName("rocprofiler-sdk-tool");
json_ar.startNode();
@@ -74,6 +77,29 @@ write_json(tool_table* tool
json_ar(cereal::make_nvp("pid", pid));
json_ar(cereal::make_nvp("init_time", timestamps->app_start_time));
json_ar(cereal::make_nvp("fini_time", timestamps->app_end_time));
json_ar(cereal::make_nvp("config", get_config()));
json_ar(cereal::make_nvp("command", common::read_command_line(getpid())));
json_ar.finishNode();
}
// summary
{
json_ar.setNextName("summary");
json_ar.startNode();
json_ar.makeArray();
for(const auto& itr : domain_stats)
{
auto _name = get_domain_column_name(itr.first);
json_ar.startNode();
json_ar(cereal::make_nvp("domain", std::string{_name}));
json_ar(cereal::make_nvp("stats", itr.second));
// itr.second.serialize(json_ar, 0);
json_ar.finishNode();
}
json_ar.finishNode();
}
@@ -154,8 +180,7 @@ write_json(tool_table* tool
json_ar.finishNode();
}
*output_stream << std::flush;
if(cleanup) cleanup(output_stream);
ofs.close();
}
} // namespace tool
@@ -23,6 +23,7 @@
#pragma once
#include "helper.hpp"
#include "statistics.hpp"
namespace rocprofiler
{
@@ -31,6 +32,7 @@ namespace tool
void
write_json(tool_table* tool_functions,
uint64_t pid,
const domain_stats_vec_t& domain_stats,
std::vector<rocprofiler_agent_v0_t> agent_data,
std::vector<rocprofiler_tool_counter_info_t> counter_data,
std::deque<rocprofiler_buffer_tracing_hip_api_record_t>* hip_api_deque,
@@ -21,13 +21,14 @@
// SOFTWARE.
#include "generateOTF2.hpp"
#include "config.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/common/mpl.hpp"
#include "lib/common/units.hpp"
#include "lib/common/utility.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include "output_file.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
@@ -510,6 +511,10 @@ write_otf2(tool_table* tool
if(!_inp) return;
for(auto itr : *_inp)
{
if(itr.kind == ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API &&
itr.operation == ROCPROFILER_MARKER_CORE_API_ID_roctxMarkA)
continue;
using value_type = common::mpl::unqualified_type_t<decltype(itr)>;
auto name = buffer_names.at(itr.kind, itr.operation);
auto paradigm = OTF2_PARADIGM_HIP;
@@ -21,11 +21,11 @@
// SOFTWARE.
#include "generatePerfetto.hpp"
#include "config.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "lib/common/utility.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
@@ -503,23 +503,20 @@ write_perfetto(
tracing_session->FlushBlocking();
tracing_session->StopBlocking();
auto filename = std::string{"results"};
auto cleanup = std::function<void(std::ostream*&)>{};
std::ostream* ofs = nullptr;
std::tie(ofs, cleanup) = get_output_stream(filename, ".pftrace");
auto filename = std::string{"results"};
auto ofs = get_output_stream(filename, ".pftrace");
auto amount_read = std::atomic<size_t>{0};
auto is_done = std::promise<void>{};
auto _mtx = std::mutex{};
auto _reader = [ofs, &_mtx, &is_done, &amount_read](
auto _reader = [&ofs, &_mtx, &is_done, &amount_read](
::perfetto::TracingSession::ReadTraceCallbackArgs _args) {
auto _lk = std::unique_lock<std::mutex>{_mtx};
if(_args.data && _args.size > 0)
{
ROCP_TRACE << "Writing " << _args.size << " B to trace...";
// Write the trace data into file
ofs->write(_args.data, _args.size);
ofs.stream->write(_args.data, _args.size);
amount_read += _args.size;
}
ROCP_INFO_IF(!_args.has_more && amount_read > 0)
@@ -540,10 +537,10 @@ write_perfetto(
tracing_session.reset();
ROCP_TRACE << "Flushing trace output stream...";
(*ofs) << std::flush;
(*ofs.stream) << std::flush;
ROCP_TRACE << "Destroying trace output stream...";
if(cleanup) cleanup(ofs);
ofs.close();
}
} // namespace tool
@@ -0,0 +1,350 @@
// MIT License
//
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// 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.
#include "generateStats.hpp"
#include "config.hpp"
#include "helper.hpp"
#include "lib/common/logging.hpp"
#include "lib/rocprofiler-sdk-tool/domain_type.hpp"
#include "output_file.hpp"
#include "statistics.hpp"
#include <fmt/format.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
#include <unistd.h>
#include <cstdint>
#include <iomanip>
#include <regex>
#include <sstream>
#include <string_view>
#include <utility>
namespace rocprofiler
{
namespace tool
{
namespace
{
stats_entry_t
get_stats(const stats_map_t& data_v)
{
auto _stats = stats_entry_t{};
for(const auto& [id, value] : data_v)
{
_stats.entries.emplace_back(id, value);
_stats.total += value;
}
return _stats.sort();
}
} // namespace
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data)
{
auto kernel_stats = stats_map_t{};
for(const auto& record : data)
{
auto kernel_name = tool_functions->tool_get_kernel_name_fn(
record.dispatch_info.kernel_id, record.correlation_id.external.value);
kernel_stats[kernel_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(kernel_stats);
}
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data)
{
auto hip_stats = stats_map_t{};
for(const auto& record : data)
{
auto api_name = tool_functions->tool_get_operation_name_fn(record.kind, record.operation);
hip_stats[api_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(hip_stats);
}
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data)
{
auto hsa_stats = stats_map_t{};
for(const auto& record : data)
{
auto api_name = tool_functions->tool_get_operation_name_fn(record.kind, record.operation);
hsa_stats[api_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(hsa_stats);
}
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data)
{
auto memory_copy_stats = stats_map_t{};
for(const auto& record : data)
{
auto api_name = tool_functions->tool_get_operation_name_fn(record.kind, record.operation);
memory_copy_stats[api_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(memory_copy_stats);
}
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data)
{
auto marker_stats = stats_map_t{};
for(const auto& record : data)
{
auto _name = std::string_view{};
if(record.kind == ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API &&
(record.operation == ROCPROFILER_MARKER_CORE_API_ID_roctxMarkA ||
record.operation == ROCPROFILER_MARKER_CORE_API_ID_roctxRangePushA ||
record.operation == ROCPROFILER_MARKER_CORE_API_ID_roctxRangeStartA))
{
_name = tool_functions->tool_get_roctx_msg_fn(record.correlation_id.internal);
}
else
{
_name = tool_functions->tool_get_operation_name_fn(record.kind, record.operation);
}
marker_stats[_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(marker_stats);
}
stats_entry_t
generate_stats(tool_table* /*tool_functions*/,
const std::deque<rocprofiler_tool_counter_collection_record_t>& /*data*/)
{
return stats_entry_t{};
}
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data)
{
auto scratch_memory_stats = stats_map_t{};
for(const auto& record : data)
{
auto op_name = tool_functions->tool_get_operation_name_fn(record.kind, record.operation);
scratch_memory_stats[op_name] += (record.end_timestamp - record.start_timestamp);
}
return get_stats(scratch_memory_stats);
}
namespace
{
void
generate_stats(output_stream_t& os,
std::string_view label,
const domain_stats_vec_t& data_v,
std::string_view indent_v)
{
auto _data = stats_entry_t{};
auto _cols = std::unordered_map<std::string_view, domain_type>{};
auto _get_entry = [&_data, &_cols](domain_type _domain,
std::string_view _key) -> stats_data_t* {
for(auto& itr : _data.entries)
{
if(itr.first == _key) return &itr.second;
}
_cols.emplace(_key, _domain);
auto& itr = _data.entries.emplace_back(_key, stats_data_t{});
return &itr.second;
};
uint64_t name_width = 40;
uint64_t domain_width = 12;
for(const auto& itr : data_v)
{
for(const auto& eitr : itr.second.entries)
{
_data.total += eitr.second;
auto* _entry = _get_entry(itr.first, eitr.first);
*CHECK_NOTNULL(_entry) += eitr.second;
name_width = std::max(name_width, eitr.first.length());
}
domain_width = std::max(domain_width, get_domain_column_name(itr.first).length());
}
if(!_data) return;
std::sort(_data.entries.begin(), _data.entries.end(), [](const auto& lhs, const auto& rhs) {
return (lhs.second.get_sum() > rhs.second.get_sum());
});
const float_type _total_duration = _data.total.get_sum();
os << fmt::format("\n{}ROCPROFV3 {}:\n\n", indent_v, label) << std::flush;
{
auto _header = fmt::format(
"| {:^{}} | {:^{}} | {:^15} | {:^15} | {:^15} | {:^13} | {:^15} | {:^15} | {:^15} |",
"NAME",
name_width,
"DOMAIN",
domain_width,
"CALLS",
fmt::format("DURATION ({})", tool::get_config().stats_summary_unit),
fmt::format("AVERAGE ({})", tool::get_config().stats_summary_unit),
"PERCENT (INC)",
fmt::format("MIN ({})", tool::get_config().stats_summary_unit),
fmt::format("MAX ({})", tool::get_config().stats_summary_unit),
"STDDEV");
(*os.stream) << indent_v << _header << "\n" << std::flush;
auto _div =
fmt::format("|-{0:-^{1}}-|-{0:-^{2}}-|-{0:-^15}-|-{0:-^15}-|-{0:-^15}-|-{0:-^13}"
"-|-{0:-^15}-|-{0:-^15}-|-{0:-^15}-|",
"",
name_width,
domain_width);
(*os.stream) << indent_v << _div << "\n" << std::flush;
}
for(const auto& [type, value] : _data.entries)
{
auto name = type;
auto duration_ns = value.get_sum();
auto calls = value.get_count();
auto avg_ns = value.get_mean();
auto percent_v = value.get_percent(_total_duration);
auto percent = std::to_string(percent_v);
auto _row = std::string{};
if(tool::get_config().stats_summary_unit_value > 1)
{
auto _unit_div = static_cast<double>(tool::get_config().stats_summary_unit_value);
_row = fmt::format("{}| {:<{}} | {:<{}} | {:15} | {:15} | {:15.3e} | {:>13} | {:15} | "
"{:15} | {:15.3e} |",
indent_v,
name,
name_width,
get_domain_column_name(_cols.at(name)),
domain_width,
calls,
duration_ns / _unit_div,
avg_ns / _unit_div,
percent,
value.get_min() / _unit_div,
value.get_max() / _unit_div,
value.get_stddev() / _unit_div);
}
else
{
_row = fmt::format("{}| {:<{}} | {:<{}} | {:15} | {:15} | {:15.3e} | {:>13} | {:15} | "
"{:15} | {:15.3e} |",
indent_v,
name,
name_width,
get_domain_column_name(_cols.at(name)),
domain_width,
calls,
duration_ns,
avg_ns,
percent,
value.get_min(),
value.get_max(),
value.get_stddev());
}
(*os.stream) << _row << "\n" << std::flush;
}
(*os.stream) << "\n" << std::flush;
}
} // namespace
void
generate_stats(tool_table* /*tool_functions*/, const domain_stats_vec_t& inp_data)
{
auto data_v = inp_data;
std::sort(data_v.begin(), data_v.end(), [](const auto& lhs, const auto& rhs) {
return lhs.first < rhs.first;
});
output_stream_t _os = get_output_stream(tool::get_config().stats_summary_file, ".txt");
auto _indent = (_os.writes_to_file()) ? std::string_view{} : std::string_view{" "};
if(tool::get_config().stats_summary_per_domain)
{
for(const auto& itr : data_v)
{
if(!itr.second) continue;
auto _name = fmt::format("{} SUMMARY", get_domain_column_name(itr.first));
auto _tmp = domain_stats_vec_t{};
_tmp.emplace_back(itr.first, itr.second);
generate_stats(_os, _name, _tmp, _indent);
}
}
if(!tool::get_config().stats_summary_groups.empty())
{
auto domain_groups = std::vector<domain_stats_vec_t>{};
for(const auto& itr : tool::get_config().stats_summary_groups)
{
auto _names = std::vector<std::string>{};
auto _tmp = domain_stats_vec_t{};
for(const auto& ditr : data_v)
{
auto _col_name = get_domain_column_name(ditr.first);
if(std::regex_match(_col_name.data(), std::regex{itr}))
{
if(!ditr.second) continue;
_names.emplace_back(_col_name);
_tmp.emplace_back(ditr.first, ditr.second);
}
}
ROCP_CI_LOG_IF(WARNING, _names.empty())
<< "summary group regex '" << itr << "' matched with zero domain groups";
auto _name = fmt::format("{} SUMMARY", fmt::join(_names.begin(), _names.end(), " + "));
generate_stats(_os, _name, _tmp, _indent);
}
}
if(tool::get_config().stats_summary) generate_stats(_os, "SUMMARY", data_v, _indent);
}
} // namespace tool
} // namespace rocprofiler
@@ -0,0 +1,63 @@
// MIT License
//
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// 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.
#pragma once
#include "helper.hpp"
#include "statistics.hpp"
namespace rocprofiler
{
namespace tool
{
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hip_api_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_marker_api_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_tool_counter_collection_record_t>& data);
stats_entry_t
generate_stats(tool_table* tool_functions,
const std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>& data);
void
generate_stats(tool_table* tool_functions, const domain_stats_vec_t& data);
} // namespace tool
} // namespace rocprofiler
@@ -33,6 +33,12 @@ namespace tool
{
namespace fs = common::filesystem;
namespace
{
const auto stdout_names = std::unordered_set<std::string_view>{"stdout", "STDOUT"};
const auto stderr_names = std::unordered_set<std::string_view>{"stderr", "STDERR"};
} // namespace
std::string
get_output_filename(std::string_view fname, std::string_view ext)
{
@@ -55,16 +61,17 @@ get_output_filename(std::string_view fname, std::string_view ext)
return tool::format(output_path / fmt::format("{}_{}{}", output_prefix, fname, _ext));
}
std::pair<std::ostream*, output_stream_dtor_t>
output_stream_t
get_output_stream(std::string_view fname, std::string_view ext)
{
auto cfg_output_path = tool::format(tool::get_config().output_path);
if(cfg_output_path == "stdout" || cfg_output_path == "STDOUT")
if(stdout_names.count(cfg_output_path) > 0 || stdout_names.count(fname) > 0)
return {&std::cout, [](auto*&) {}};
else if(cfg_output_path == "stderr" || cfg_output_path == "STDERR")
else if(stderr_names.count(cfg_output_path) > 0 || stderr_names.count(fname) > 0)
return {&std::cout, [](auto*&) {}};
else if(cfg_output_path.empty())
else if(cfg_output_path.empty() || fname.empty())
return {&std::clog, [](auto*&) {}};
auto output_file = get_output_filename(fname, ext);
@@ -82,12 +89,12 @@ get_output_stream(std::string_view fname, std::string_view ext)
output_file::~output_file()
{
if(m_stream)
if(m_os.stream)
ROCP_INFO << "Closing result file: " << m_name;
else
ROCP_WARNING << "output_file::~output_file does not have a output stream instance!";
m_dtor(m_stream);
m_os.close();
}
} // namespace tool
} // namespace rocprofiler
@@ -26,7 +26,6 @@
#include "csv.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/rocprofiler-sdk-tool/csv.hpp"
#include <array>
#include <iostream>
@@ -41,12 +40,48 @@ namespace rocprofiler
{
namespace tool
{
using output_stream_dtor_t = void (*)(std::ostream*&);
using ostream_dtor_t = void (*)(std::ostream*&);
using output_stream_pair_t = std::pair<std::ostream*, ostream_dtor_t>;
struct output_stream_t
{
output_stream_t() = default;
output_stream_t(std::ostream* _os, ostream_dtor_t _dtor)
: stream{_os}
, dtor{_dtor}
{}
~output_stream_t() { close(); }
output_stream_t(const output_stream_t&) = delete;
output_stream_t(output_stream_t&&) noexcept = default;
output_stream_t& operator=(const output_stream_t&) = delete;
output_stream_t& operator=(output_stream_t&&) noexcept = default;
explicit operator bool() const { return stream != nullptr; }
template <typename Tp>
std::ostream& operator<<(Tp&& value)
{
return ((stream) ? *stream : std::cerr) << std::forward<Tp>(value) << std::flush;
}
void close()
{
if(stream) (*stream) << std::flush;
if(dtor) dtor(stream);
}
bool writes_to_file() const { return (dynamic_cast<std::ofstream*>(stream) != nullptr); }
std::ostream* stream = nullptr;
ostream_dtor_t dtor = nullptr;
};
std::string
get_output_filename(std::string_view fname, std::string_view ext);
std::pair<std::ostream*, output_stream_dtor_t>
output_stream_t
get_output_stream(std::string_view fname, std::string_view ext);
struct output_file
@@ -65,16 +100,15 @@ struct output_file
std::ostream& operator<<(T&& value)
{
auto _lk = std::unique_lock<std::mutex>{m_mutex};
return ((m_stream) ? *m_stream : std::cerr) << std::forward<T>(value) << std::flush;
return ((m_os.stream) ? *m_os.stream : std::cerr) << std::forward<T>(value) << std::flush;
}
operator bool() const { return m_stream != nullptr; }
operator bool() const { return m_os.stream != nullptr; }
private:
const std::string m_name = {};
std::mutex m_mutex = {};
std::ostream* m_stream = nullptr;
output_stream_dtor_t m_dtor = [](std::ostream*&) {};
const std::string m_name = {};
std::mutex m_mutex = {};
output_stream_t m_os = {};
};
template <size_t N>
@@ -82,9 +116,8 @@ output_file::output_file(std::string name,
csv::csv_encoder<N> encoder,
std::array<std::string_view, N>&& header)
: m_name{std::move(name)}
, m_os{get_output_stream(m_name, ".csv")}
{
std::tie(m_stream, m_dtor) = get_output_stream(m_name, ".csv");
for(auto& itr : header)
{
ROCP_FATAL_IF(itr.empty())
@@ -92,7 +125,7 @@ output_file::output_file(std::string name,
}
// write the csv header
if(m_stream) encoder.write_row(*m_stream, header);
if(m_os.stream) encoder.write_row(*m_os.stream, header);
}
} // namespace tool
} // namespace rocprofiler
@@ -0,0 +1,75 @@
// MIT License
//
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// 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.
#include "statistics.hpp"
namespace rocprofiler
{
namespace tool
{
template <typename Tp>
std::ostream&
stats_formatter::operator()(std::ostream& ofs, const Tp& _val) const
{
using value_type = common::mpl::unqualified_type_t<Tp>;
if constexpr(std::is_floating_point<value_type>::value)
{
constexpr value_type one_hundredth = 1.0e-2;
if(_val > one_hundredth)
ofs << std::setprecision(6) << std::fixed;
else
ofs << std::setprecision(8) << std::scientific;
}
else if constexpr(std::is_same<Tp, percentage>::value)
{
constexpr float_type one = 1.0;
constexpr float_type one_hundredth = 1.0e-2;
if(_val.value >= one)
ofs << std::setprecision(2) << std::fixed;
else if(_val.value > one_hundredth)
ofs << std::setprecision(4) << std::fixed;
else
ofs << std::setprecision(3) << std::scientific;
}
return ofs;
}
#define STATS_FORMATTER_INSTANTIATE_TEMPLATE(TYPE) \
template std::ostream& stats_formatter::operator()(std::ostream&, const TYPE&) const;
STATS_FORMATTER_INSTANTIATE_TEMPLATE(std::string)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(std::string_view)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(percentage)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(float)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(double)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(uint8_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(uint16_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(uint32_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(uint64_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(int8_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(int16_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(int32_t)
STATS_FORMATTER_INSTANTIATE_TEMPLATE(int64_t)
} // namespace tool
} // namespace rocprofiler
@@ -22,6 +22,13 @@
#pragma once
#include "domain_type.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/mpl.hpp"
#include <rocprofiler-sdk/cxx/serialization.hpp>
#include <cmath>
#include <cstdint>
#include <fstream>
@@ -29,7 +36,9 @@
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <type_traits>
#include <vector>
namespace rocprofiler
{
@@ -93,6 +102,8 @@ public:
}
float_type get_stddev() const { return ::std::sqrt(::std::abs(get_variance())); }
float_type get_percent(float_type _total) const;
float_type get_percent(const this_type&) const;
// Modifications
void reset()
@@ -209,6 +220,107 @@ public:
{
return statistics(lhs) -= rhs;
}
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned int) const
{
ar(cereal::make_nvp("count", m_cnt));
ar(cereal::make_nvp("sum", m_sum));
ar(cereal::make_nvp("sqr", m_sqr));
ar(cereal::make_nvp("min", m_min));
ar(cereal::make_nvp("max", m_max));
ar(cereal::make_nvp("mean", get_mean()));
ar(cereal::make_nvp("stddev", get_stddev()));
ar(cereal::make_nvp("variance", get_variance()));
}
};
template <typename Tp, typename Fp>
typename statistics<Tp, Fp>::float_type
statistics<Tp, Fp>::get_percent(float_type _total) const
{
constexpr float_type one_hundred = 100.0;
const float_type _sum = get_sum();
ROCP_WARNING_IF(static_cast<int64_t>(_sum) > static_cast<int64_t>(_total))
<< "percentage calculation > 100%. sum=" << _sum << " > total=" << _total;
return (_sum / _total) * one_hundred;
}
template <typename Tp, typename Fp>
typename statistics<Tp, Fp>::float_type
statistics<Tp, Fp>::get_percent(const statistics<Tp, Fp>& _rhs) const
{
return get_percent(_rhs.get_sum());
}
using float_type = double;
using stats_data_t = statistics<uint64_t, float_type>;
using stats_map_t = std::map<std::string_view, stats_data_t>;
using stats_pair_t = std::pair<std::string_view, stats_data_t>;
using stats_entry_vec_t = std::vector<stats_pair_t>;
inline bool
default_stats_sorter(const stats_pair_t& lhs, const stats_pair_t& rhs)
{
return (lhs.second.get_sum() > rhs.second.get_sum());
}
struct stats_entry_t
{
using sort_predicate_t = bool (*)(const stats_pair_t&, const stats_pair_t&);
stats_entry_t() = default;
~stats_entry_t() = default;
stats_entry_t(const stats_entry_t&) = default;
stats_entry_t(stats_entry_t&&) noexcept = default;
stats_entry_t& operator=(const stats_entry_t&) = default;
stats_entry_t& operator=(stats_entry_t&&) noexcept = default;
template <typename FuncT = sort_predicate_t>
stats_entry_t& sort(FuncT&& _predicate = default_stats_sorter);
explicit operator bool() const { return (total.get_count() > 0 && !entries.empty()); }
stats_data_t total = {};
stats_entry_vec_t entries = {};
template <typename ArchiveT>
void serialize(ArchiveT& ar, const unsigned int) const
{
total.serialize(ar, 0);
auto _entries_map = std::map<std::string, stats_data_t>{};
for(const auto& itr : entries)
_entries_map.emplace(std::string{itr.first}, itr.second);
ar(cereal::make_nvp("operations", _entries_map));
}
};
template <typename FuncT>
stats_entry_t&
stats_entry_t::sort(FuncT&& _predicate)
{
std::sort(entries.begin(), entries.end(), std::forward<FuncT>(_predicate));
return *this;
}
using domain_stats_t = std::pair<domain_type, stats_entry_t>;
using domain_stats_vec_t = std::vector<domain_stats_t>;
struct stats_formatter
{
template <typename Tp>
std::ostream& operator()(std::ostream& ofs, const Tp& _val) const;
};
struct percentage
{
float_type value = {};
friend std::ostream& operator<<(std::ostream& os, percentage val)
{
return (stats_formatter{}(os, val) << val.value);
}
};
} // namespace tool
} // namespace rocprofiler
@@ -228,4 +340,12 @@ min(::rocprofiler::tool::statistics<Tp> lhs, const Tp& rhs)
{
return lhs.get_min(rhs);
}
inline std::string
to_string(::rocprofiler::tool::percentage val)
{
auto _ss = std::stringstream{};
_ss << val;
return _ss.str();
}
} // namespace std
@@ -22,6 +22,7 @@
#pragma once
#include "domain_type.hpp"
#include "helper.hpp"
#include "tmp_file.hpp"
@@ -35,6 +36,7 @@
#include <mutex>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
template <typename Tp>
@@ -71,15 +73,60 @@ void
write_ring_buffer(Tp _v, domain_type type)
{
auto [_tmp_buf, _tmp_file] = get_tmp_file_buffer<ring_buffer_t<Tp>>(type);
if(_tmp_buf->capacity() == 0) return;
if(_tmp_buf->capacity() == 0)
{
ROCP_INFO << "rocprofv3 is dropping record from domain " << get_domain_column_name(type)
<< ". Buffer has a capacity of zero.";
return;
}
auto* ptr = _tmp_buf->request(false);
if(ptr == nullptr)
{
offload_buffer<ring_buffer_t<Tp>>(type);
ptr = _tmp_buf->request(false);
CHECK(ptr != nullptr);
// if failed, try again
if(!ptr) ptr = _tmp_buf->request(false);
// after second failure, emit warning message
ROCP_CI_LOG_IF(WARNING, !ptr)
<< "rocprofv3 is dropping record from domain " << get_domain_column_name(type)
<< ". No space in buffer: "
<< fmt::format(
"capacity={}, record_size={}, used_count={}, free_count={} | raw_info=[{}]",
_tmp_buf->capacity(),
_tmp_buf->data_size(),
_tmp_buf->count(),
_tmp_buf->free(),
_tmp_buf->as_string());
}
if(ptr)
{
if constexpr(std::is_move_constructible<Tp>::value)
{
new(ptr) Tp{std::move(_v)};
}
else if constexpr(std::is_move_assignable<Tp>::value)
{
*ptr = std::move(_v);
}
else if constexpr(std::is_copy_constructible<Tp>::value)
{
new(ptr) Tp{_v};
}
else if constexpr(std::is_copy_assignable<Tp>::value)
{
*ptr = _v;
}
else
{
static_assert(std::is_void<Tp>::value,
"data type is neither move/copy constructible nor move/copy assignable");
}
}
*ptr = std::move(_v);
}
template <typename Tp>
@@ -28,8 +28,10 @@
#include "generateJSON.hpp"
#include "generateOTF2.hpp"
#include "generatePerfetto.hpp"
#include "generateStats.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "statistics.hpp"
#include "tmp_file.hpp"
#include "lib/common/environment.hpp"
@@ -1335,7 +1337,7 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
"buffer tracing service for memory copy configure");
}
if(tool::get_config().scratch_memory)
if(tool::get_config().scratch_memory_trace)
{
ROCPROFILER_CALL(rocprofiler_create_buffer(get_client_ctx(),
buffer_size,
@@ -1500,21 +1502,32 @@ api_registration_callback(rocprofiler_intercept_table_t,
"Iterate rocporfiler agents")
}
using stats_data_t = ::rocprofiler::tool::stats_data_t;
using stats_data_t = ::rocprofiler::tool::stats_data_t;
using stats_entry_t = ::rocprofiler::tool::stats_entry_t;
using domain_stats_vec_t = ::rocprofiler::tool::domain_stats_vec_t;
template <typename Tp, domain_type DomainT>
void
generate_output(rocprofiler::tool::buffered_output<Tp, DomainT>& output_v,
std::unordered_map<domain_type, stats_data_t>& contributions_v)
domain_stats_vec_t& contributions_v)
{
if(!output_v) return;
output_v.read();
if(tool::get_config().stats || tool::get_config().summary_output)
{
output_v.stats = rocprofiler::tool::generate_stats(tool_functions, output_v.element_data);
}
if(output_v.stats)
{
contributions_v.emplace_back(output_v.buffer_type_v, output_v.stats);
}
if(tool::get_config().csv_output)
{
output_v.stats = rocprofiler::tool::generate_csv(tool_functions, output_v.element_data);
contributions_v.emplace(output_v.buffer_type_v, output_v.stats);
rocprofiler::tool::generate_csv(tool_functions, output_v.element_data, output_v.stats);
}
}
@@ -1543,7 +1556,7 @@ tool_fini(void* /*tool_data*/)
auto counters_output =
counter_collection_buffered_output_t{tool::get_config().counter_collection};
auto scratch_memory_output =
scratch_memory_buffered_output_t{tool::get_config().scratch_memory};
scratch_memory_buffered_output_t{tool::get_config().scratch_memory_trace};
auto node_id_sort = [](const auto& lhs, const auto& rhs) { return lhs.node_id < rhs.node_id; };
@@ -1561,7 +1574,7 @@ tool_fini(void* /*tool_data*/)
rocprofiler::tool::generate_csv(tool_functions, _agents);
}
auto contributions = std::unordered_map<domain_type, stats_data_t>{};
auto contributions = domain_stats_vec_t{};
generate_output(kernel_dispatch_output, contributions);
generate_output(hsa_output, contributions);
@@ -1580,6 +1593,7 @@ tool_fini(void* /*tool_data*/)
{
rocprofiler::tool::write_json(tool_functions,
getpid(),
contributions,
_agents,
_counters,
&hip_output.element_data,
@@ -1617,6 +1631,11 @@ tool_fini(void* /*tool_data*/)
&scratch_memory_output.element_data);
}
if(tool::get_config().summary_output)
{
rocprofiler::tool::generate_stats(tool_functions, contributions);
}
auto destroy_output = [](auto& _buffered_output_v) { _buffered_output_v.destroy(); };
destroy_output(kernel_dispatch_output);
@@ -72,7 +72,7 @@ struct async_copy_info;
struct async_copy_info<ROCPROFILER_MEMORY_COPY_##DIRECTION> \
{ \
static constexpr auto operation_idx = ROCPROFILER_MEMORY_COPY_##DIRECTION; \
static constexpr auto name = #DIRECTION; \
static constexpr auto name = "MEMORY_COPY_" #DIRECTION; \
};
SPECIALIZE_ASYNC_COPY_INFO(NONE)
@@ -35,18 +35,65 @@ set(CMAKE_HIP_EXTENSIONS OFF)
set(CMAKE_HIP_STANDARD_REQUIRED ON)
set_source_files_properties(transpose.cpp PROPERTIES LANGUAGE HIP)
add_executable(transpose)
target_sources(transpose PRIVATE transpose.cpp)
target_compile_options(transpose PRIVATE -W -Wall -Wextra -Wpedantic -Wshadow -Werror)
function(transpose_build_target _NAME _COMPILE_DEFS _LINK_TARGETS)
add_executable(${_NAME})
target_sources(${_NAME} PRIVATE transpose.cpp)
target_compile_definitions(${_NAME} PRIVATE ${_COMPILE_DEFS})
target_compile_options(${_NAME} PRIVATE -W -Wall -Wextra -Wpedantic -Wshadow -Werror)
target_link_libraries(
${_NAME} PRIVATE Threads::Threads rocprofiler-sdk::tests-build-flags
${_LINK_TARGETS})
if(TRANSPOSE_USE_MPI)
find_package(MPI REQUIRED)
target_compile_definitions(${_NAME} PRIVATE USE_MPI=1)
target_link_libraries(${_NAME} PRIVATE MPI::MPI_C)
endif()
endfunction()
find_package(Threads REQUIRED)
target_link_libraries(transpose PRIVATE Threads::Threads)
find_package(rocprofiler-sdk-roctx REQUIRED)
target_link_libraries(transpose PRIVATE rocprofiler-sdk-roctx::rocprofiler-sdk-roctx)
if(TRANSPOSE_USE_MPI)
find_package(MPI REQUIRED)
target_compile_definitions(transpose PRIVATE USE_MPI)
target_link_libraries(transpose PRIVATE MPI::MPI_C)
transpose_build_target(transpose "" rocprofiler-sdk-roctx::rocprofiler-sdk-roctx)
#
# using old roctracer roctx
#
find_path(
roctracer_roctx_ROOT_DIR
NAMES include/roctracer/roctx.h
HINTS ${hip_DIR}
PATHS ${hip_DIR})
find_path(
roctracer_roctx_INCLUDE_DIR
NAMES roctracer/roctx.h
HINTS ${roctracer_roctx_ROOT_DIR} ${hip_DIR}
PATHS ${roctracer_roctx_ROOT_DIR} ${hip_DIR}
PATH_SUFFIXES include)
find_library(
roctracer_roctx_LIBRARY
NAMES roctx64
HINTS ${roctracer_roctx_ROOT_DIR} ${hip_DIR}
PATHS ${roctracer_roctx_ROOT_DIR} ${hip_DIR}
PATH_SUFFIXES lib lib64)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(roctracer-roctx DEFAULT_MSG roctracer_roctx_ROOT_DIR
roctracer_roctx_INCLUDE_DIR roctracer_roctx_LIBRARY)
if(roctracer-roctx_FOUND)
add_library(roctracer-roctx INTERFACE IMPORTED)
add_library(roctracer-roctx::roctracer-roctx ALIAS roctracer-roctx)
target_include_directories(roctracer-roctx INTERFACE ${roctracer_roctx_INCLUDE_DIR})
target_link_libraries(roctracer-roctx INTERFACE ${roctracer_roctx_LIBRARY})
endif()
if(TARGET roctracer-roctx::roctracer-roctx)
transpose_build_target(transpose-roctracer-roctx "USE_ROCTRACER_ROCTX=1"
roctracer-roctx::roctracer-roctx)
endif()
@@ -20,8 +20,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "hip/hip_runtime.h"
#include "rocprofiler-sdk-roctx/roctx.h"
#include <hip/hip_runtime.h>
#include <sstream>
#if defined(USE_ROCTRACER_ROCTX)
# include <roctracer/roctx.h>
#else
# include <rocprofiler-sdk-roctx/roctx.h>
#endif
#include <chrono>
#include <cstdio>
@@ -99,6 +105,15 @@ main(int argc, char** argv)
printf("[transpose] Number of iterations: %zu\n", nitr);
printf("[transpose] Syncing every %zu iterations\n", nsync);
#if defined(USE_ROCTRACER_ROCTX)
{
auto _roctracer_roctx_ss = std::stringstream{};
_roctracer_roctx_ss << "roctracer/roctx v" << roctx_version_major() << "."
<< roctx_version_minor();
roctxMark(_roctracer_roctx_ss.str().c_str());
}
#endif
#if defined(USE_MPI)
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
@@ -174,7 +189,15 @@ transpose(const int* in, int* out, int M, int N)
void
run(int rank, int tid, int devid, int argc, char** argv)
{
roctxRangePush("run");
auto roctx_run_id = roctxRangeStart("run");
const auto mark = [rank, tid, devid](std::string_view suffix) {
auto _ss = std::stringstream{};
_ss << "run/rank-" << rank << "/thread-" << tid << "/device-" << devid << "/" << suffix;
roctxMark(_ss.str().c_str());
};
mark("begin");
constexpr unsigned int M = 4960 * 2;
constexpr unsigned int N = 4960 * 2;
@@ -219,9 +242,16 @@ run(int rank, int tid, int devid, int argc, char** argv)
auto t1 = std::chrono::high_resolution_clock::now();
for(size_t i = 0; i < nitr; ++i)
{
roctxRangePush("run/iteration");
transpose<<<grid, block, 0, stream>>>(in, out, M, N);
check_hip_error();
if(i % nsync == (nsync - 1)) HIP_API_CALL(hipStreamSynchronize(stream));
if(i % nsync == (nsync - 1))
{
roctxRangePush("run/iteration/sync");
HIP_API_CALL(hipStreamSynchronize(stream));
roctxRangePop();
}
roctxRangePop();
}
auto t2 = std::chrono::high_resolution_clock::now();
HIP_API_CALL(hipStreamSynchronize(stream));
@@ -251,7 +281,9 @@ run(int rank, int tid, int devid, int argc, char** argv)
delete[] inp_matrix;
delete[] out_matrix;
roctxRangePop();
mark("end");
roctxRangeStop(roctx_run_id);
}
namespace
@@ -54,6 +54,16 @@ def test_otf2_data(
otf2_data, json_data, categories=("hip", "hsa", "marker", "kernel", "memory_copy")
):
def get_operation_name(kind_id, op_id):
return json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][kind_id][
"operations"
][op_id]
def get_kind_name(kind_id):
return json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][kind_id][
"kind"
]
mapping = {
"hip": ("hip_api", "hip_api"),
"hsa": ("hsa_api", "hsa_api"),
@@ -72,6 +82,20 @@ def test_otf2_data(
_otf2_data = otf2_data.loc[otf2_data["category"] == otf2_category]
_json_data = json_data["rocprofiler-sdk-tool"]["buffer_records"][json_category]
# we do not encode the roctxMark "regions" in OTF2 because
# they don't map to the OTF2_REGION_ROLE_FUNCTION well
if json_category == "marker_api":
def roctx_mark_filter(val):
return (
None
if get_kind_name(val.kind) == "MARKER_CORE_API"
and get_operation_name(val.kind, val.operation) == "roctxMarkA"
else val
)
_json_data = [itr for itr in _json_data if roctx_mark_filter(itr) is not None]
assert len(_otf2_data) == len(
_json_data
), f"{otf2_category} ({len(_otf2_data)}):\n\t{_otf2_data}\n{json_category} ({len(_json_data)}):\n\t{_json_data}"
@@ -30,3 +30,4 @@ add_subdirectory(counter-collection)
add_subdirectory(hsa-queue-dependency)
add_subdirectory(kernel-rename)
add_subdirectory(aborted-app)
add_subdirectory(summary)
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import re
import sys
import pytest
@@ -83,7 +84,15 @@ def test_kernel_trace(json_data):
def get_kind_name(kind_id):
return data["strings"]["buffer_records"][kind_id]["kind"]
valid_kernel_names = ("run",)
valid_kernel_names = "|".join(
(
"run",
"run/iteration",
"run/rank-([0-9]+)/thread-([0-9]+)/device-([0-9]+)/(begin|end)",
)
)
valid_kernel_regex = re.compile("^({})$".format(valid_kernel_names))
kernel_dispatch_data = data["buffer_records"]["kernel_dispatch"]
for dispatch in kernel_dispatch_data:
assert get_kind_name(dispatch["kind"]) == "KERNEL_DISPATCH"
@@ -97,6 +106,10 @@ def test_kernel_trace(json_data):
assert dispatch["end_timestamp"] >= dispatch["start_timestamp"]
kernel_name = get_kernel_name(dispatch_info["kernel_id"])
assert (
re.search(valid_kernel_regex, kernel_name) is None
), f"kernel '{kernel_name}' matches regular expression '{valid_kernel_names}'"
assert kernel_name not in valid_kernel_names
external_corr_id = dispatch["correlation_id"]["external"]
@@ -104,7 +117,10 @@ def test_kernel_trace(json_data):
kernel_rename = get_kernel_rename(external_corr_id)
assert kernel_rename is not None, f"{dispatch}"
assert kernel_rename in valid_kernel_names
assert kernel_rename != kernel_name, f"{dispatch}"
assert (
re.search(valid_kernel_regex, kernel_rename) is not None
), f"renamed kernel '{kernel_rename}' does not match regular expression '{valid_kernel_names}'"
def test_memory_copy_json_trace(json_data):
@@ -0,0 +1,127 @@
#
# rocprofv3 tool tests for summaries
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
project(
rocprofiler-tests-rocprofv3-summary
LANGUAGES CXX
VERSION 0.0.0)
find_package(rocprofiler-sdk REQUIRED)
if(ROCPROFILER_MEMCHECK STREQUAL "LeakSanitizer")
set(LOG_LEVEL "warning") # info produces memory leak
else()
set(LOG_LEVEL "info")
endif()
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
set(summary-env "${PRELOAD_ENV}")
rocprofiler_configure_pytest_files(CONFIG pytest.ini input-summary.yaml COPY validate.py
conftest.py)
##########################################################################################
#
# Command line input
#
##########################################################################################
set(summary-cmdl-env ${summary-env} "ARBITRARY_ENV_VARIABLE=%tag%-trace-cmd-line")
add_test(
NAME rocprofv3-test-summary-cmd-line-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> -d
${CMAKE_CURRENT_BINARY_DIR}/%tag%-trace-cmd-line -o out --output-format pftrace
json otf2 --log-level env --runtime-trace --kernel-rename --summary
--summary-output-file summary --summary-per-domain --summary-groups
"KERNEL_DISPATCH|MEMORY_COPY" ".*_API" -- $<TARGET_FILE:transpose> 2 500 10)
set_tests_properties(
rocprofv3-test-summary-cmd-line-execute
PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT "${summary-cmdl-env}"
FAIL_REGULAR_EXPRESSION "${ROCPROFILER_DEFAULT_FAIL_REGEX}")
add_test(
NAME rocprofv3-test-summary-cmd-line-validate
COMMAND
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_results.pftrace
--otf2-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_results.otf2
--summary-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_summary.txt)
set(VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_results.pftrace
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_results.json
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-cmd-line/out_summary.txt)
set_tests_properties(
rocprofv3-test-summary-cmd-line-validate
PROPERTIES TIMEOUT
45
LABELS
"integration-tests"
DEPENDS
"rocprofv3-test-summary-execute"
FAIL_REGULAR_EXPRESSION
"AssertionError"
ATTACHED_FILES_ON_FAIL
"${VALIDATION_FILES}")
##########################################################################################
#
# YAML input
#
##########################################################################################
set(summary-yaml-env ${summary-env} "ARBITRARY_ENV_VARIABLE=%tag%-trace-inp-yaml")
add_test(
NAME rocprofv3-test-summary-inp-yaml-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> -i
${CMAKE_CURRENT_BINARY_DIR}/input-summary.yaml -- $<TARGET_FILE:transpose> 2 500
10)
set_tests_properties(
rocprofv3-test-summary-inp-yaml-execute
PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT "${summary-yaml-env}"
FAIL_REGULAR_EXPRESSION "${ROCPROFILER_DEFAULT_FAIL_REGEX}")
add_test(
NAME rocprofv3-test-summary-inp-yaml-validate
COMMAND
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_results.pftrace
--otf2-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_results.otf2
--summary-input
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_summary.txt)
set(VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_results.pftrace
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_results.json
${CMAKE_CURRENT_BINARY_DIR}/transpose-trace-inp-yaml/out_summary.txt)
set_tests_properties(
rocprofv3-test-summary-inp-yaml-validate
PROPERTIES TIMEOUT
45
LABELS
"integration-tests"
DEPENDS
"rocprofv3-test-summary-inp-yaml-execute"
FAIL_REGULAR_EXPRESSION
"AssertionError"
ATTACHED_FILES_ON_FAIL
"${VALIDATION_FILES}")
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
import pandas as pd
import pytest
import json
import os
import io
from rocprofiler_sdk.pytest_utils.dotdict import dotdict
from rocprofiler_sdk.pytest_utils import collapse_dict_list
from rocprofiler_sdk.pytest_utils.perfetto_reader import PerfettoReader
from rocprofiler_sdk.pytest_utils.otf2_reader import OTF2Reader
def pytest_addoption(parser):
parser.addoption(
"--json-input",
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
parser.addoption(
"--otf2-input",
action="store",
help="Path to OTF2 trace file.",
)
parser.addoption(
"--summary-input",
action="store",
help="Path to summary markdown file.",
)
@pytest.fixture
def json_data(request):
filename = request.config.getoption("--json-input")
with open(filename, "r") as inp:
return dotdict(collapse_dict_list(json.load(inp)))
@pytest.fixture
def pftrace_data(request):
filename = request.config.getoption("--pftrace-input")
return PerfettoReader(filename).read()[0]
@pytest.fixture
def otf2_data(request):
filename = request.config.getoption("--otf2-input")
if not os.path.exists(filename):
raise FileExistsError(f"{filename} does not exist")
return OTF2Reader(filename).read()[0]
@pytest.fixture
def summary_data(request):
filename = request.config.getoption("--summary-input")
if not os.path.exists(filename):
raise FileExistsError(f"{filename} does not exist")
domains = {}
with open(filename, "r") as inp:
lines = [itr.strip() for itr in inp.readlines()]
lines = [itr for itr in lines if itr and not itr.startswith("|--")]
def rework(x):
tmp = [itr.strip(" ") for itr in x.split("|")]
tmp = [itr.strip() for itr in tmp if len(itr.strip()) > 0]
if tmp[0] == "NAME":
tmp = [f'"{itr}"' for itr in tmp]
else:
tmp[0] = f'"{tmp[0]}"'
tmp[1] = f'"{tmp[1]}"'
return ",".join(tmp)
def process_current_domain(_name, _list):
if _name and _list:
_list = [rework(itr) for itr in _list]
_contents = "{}\n".format("\n".join(_list))
ifs = io.StringIO(_contents)
df = pd.read_csv(ifs)
domains[_name] = df
_name = None
_list = []
return (None, [])
current_name = None
current_list = []
for itr in lines:
if not itr.startswith("|") or itr.startswith("ROCPROFV3 "):
current_name, current_list = process_current_domain(
current_name, current_list
)
current_name = itr.strip().strip(":").replace("ROCPROFV3 ", "", 1)
rpos = current_name.rfind(" SUMMARY")
if rpos >= 0:
current_name = current_name[:rpos]
else:
current_list += [itr]
process_current_domain(current_name, current_list)
return domains
@@ -0,0 +1,11 @@
jobs:
- output_directory: "@CMAKE_CURRENT_BINARY_DIR@/%env{ARBITRARY_ENV_VARIABLE}%"
output_file: out
output_format: [pftrace, json, otf2]
log_level: env
runtime_trace: True
kernel_rename: True
summary: True
summary_per_domain: True
summary_groups: ["KERNEL_DISPATCH|MEMORY_COPY"]
summary_output_file: "summary"
@@ -0,0 +1,5 @@
[pytest]
addopts = --durations=20 -rA -s -vv
testpaths = validate.py
pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages
@@ -0,0 +1,267 @@
#!/usr/bin/env python3
import os
import re
import sys
import pytest
def test_hip_api_trace(json_data):
data = json_data["rocprofiler-sdk-tool"]
def get_operation_name(kind_id, op_id):
return data["strings"]["buffer_records"][kind_id]["operations"][op_id]
def get_kind_name(kind_id):
return data["strings"]["buffer_records"][kind_id]["kind"]
valid_domain_names = ("HIP_RUNTIME_API",)
hip_api_data = data["buffer_records"]["hip_api"]
functions = []
for api in hip_api_data:
kind = get_kind_name(api["kind"])
assert kind in valid_domain_names
assert api["end_timestamp"] >= api["start_timestamp"]
functions.append(get_operation_name(api["kind"], api["operation"]))
functions = list(set(functions))
for itr in (
"__hipPushCallConfiguration",
"__hipPopCallConfiguration",
"__hipRegisterFatBinary",
"__hipRegisterFunction",
):
assert itr not in functions, f"{itr}"
for itr in (
"hipMallocAsync",
"hipMemcpyAsync",
"hipMemsetAsync",
"hipFreeAsync",
"hipLaunchKernel",
):
assert itr in functions, f"{itr}"
def test_kernel_trace(json_data):
data = json_data["rocprofiler-sdk-tool"]
def get_kernel_name(kernel_id):
return data["kernel_symbols"][kernel_id]["formatted_kernel_name"]
def get_kernel_rename(corr_id):
for itr in data.strings.correlation_id.external:
if itr.key == corr_id:
return itr.value
return None
def get_kind_name(kind_id):
return data["strings"]["buffer_records"][kind_id]["kind"]
valid_kernel_names = "|".join(
(
"run",
"run/iteration",
"run/rank-([0-9]+)/thread-([0-9]+)/device-([0-9]+)/(begin|end)",
)
)
valid_kernel_regex = re.compile("^({})$".format(valid_kernel_names))
kernel_dispatch_data = data["buffer_records"]["kernel_dispatch"]
for dispatch in kernel_dispatch_data:
assert get_kind_name(dispatch["kind"]) == "KERNEL_DISPATCH"
assert dispatch["correlation_id"]["internal"] > 0
assert dispatch["correlation_id"]["external"] > 0
dispatch_info = dispatch["dispatch_info"]
assert dispatch_info["agent_id"]["handle"] > 0
assert dispatch_info["queue_id"]["handle"] > 0
assert dispatch_info["kernel_id"] > 0
assert dispatch["end_timestamp"] >= dispatch["start_timestamp"]
kernel_name = get_kernel_name(dispatch_info["kernel_id"])
assert (
re.search(valid_kernel_regex, kernel_name) is None
), f"kernel '{kernel_name}' matches regular expression '{valid_kernel_names}'"
assert kernel_name not in valid_kernel_names
external_corr_id = dispatch["correlation_id"]["external"]
assert external_corr_id > 0
kernel_rename = get_kernel_rename(external_corr_id)
assert kernel_rename is not None, f"{dispatch}"
assert kernel_rename != kernel_name, f"{dispatch}"
assert (
re.search(valid_kernel_regex, kernel_rename) is not None
), f"renamed kernel '{kernel_rename}' does not match regular expression '{valid_kernel_names}'"
def test_memory_copy_trace(json_data):
data = json_data["rocprofiler-sdk-tool"]
buffer_records = data["buffer_records"]
agent_data = data["agents"]
memory_copy_data = buffer_records["memory_copy"]
def get_kind_name(kind_id):
return data["strings"]["buffer_records"][kind_id]["kind"]
def get_agent(node_id):
for agent in agent_data:
if agent["id"]["handle"] == node_id["handle"]:
return agent
return None
assert len(memory_copy_data) == 12
for row in memory_copy_data:
src_agent = get_agent(row["src_agent_id"])
dst_agent = get_agent(row["dst_agent_id"])
assert get_kind_name(row["kind"]) == "MEMORY_COPY"
assert src_agent is not None, f"{row}"
assert dst_agent is not None, f"{row}"
assert row["correlation_id"]["internal"] > 0
assert row["end_timestamp"] >= row["start_timestamp"]
def test_metadata_data(json_data):
data = json_data["rocprofiler-sdk-tool"]
# patch summary groups for testing purposes
# (it appears sometimes these contain single quotes and other times it doesn't)
data.metadata.config.summary_groups = [
f"{itr}".strip("'") for itr in data.metadata.config.summary_groups
]
num_summary_grps = len(data.metadata.config.summary_groups)
expected_summary_grps = (
[
"KERNEL_DISPATCH|MEMORY_COPY",
]
if num_summary_grps == 1
else [
"KERNEL_DISPATCH|MEMORY_COPY",
".*_API",
]
)
assert data.metadata.config.summary is True
assert data.metadata.config.summary_per_domain is True
assert data.metadata.config.summary_unit == "nsec"
assert data.metadata.config.summary_file == "summary"
assert data.metadata.config.summary_groups == expected_summary_grps
assert len(data.metadata.command) == 4
# patch command to make it easier to test
data.metadata.command[0] = os.path.basename(data.metadata.command[0])
assert data.metadata.command == ["transpose", "2", "500", "10"]
def test_summary_data(json_data):
data = json_data["rocprofiler-sdk-tool"]
domains = []
for itr in data.summary:
domains.append(itr.domain)
if itr.domain == "KERNEL_DISPATCH":
assert itr.stats.count == 1004
expected = dict([["run/iteration", 1000]])
for oitr in itr.stats.operations:
if oitr.key in expected.keys():
assert oitr.value.count == expected[oitr.key]
else:
assert oitr.key.startswith("run/rank-")
assert (
re.match(
r"run/rank-([0-9]+)/thread-([0-9]+)/device-([0-9]+)/begin",
oitr.key,
)
is not None
)
assert oitr.value.count == 2
elif itr.domain == "HIP_API":
assert itr.stats.count == 2135
elif itr.domain == "MEMORY_COPY":
assert itr.stats.count == 12
elif itr.domain == "MARKER_API":
assert itr.stats.count == 1106
expected = dict(
[
["run", 2],
["run/iteration", 1000],
["run/iteration/sync", 100],
]
)
for oitr in itr.stats.operations:
if oitr.key in expected.keys():
assert oitr.value.count == expected[oitr.key]
else:
assert oitr.key.startswith("run/rank-")
assert (
re.match(
r"run/rank-([0-9]+)/thread-([0-9]+)/device-([0-9]+)/(begin|end)",
oitr.key,
)
is not None
)
assert oitr.value.count == 1
else:
assert False, f"unhandled domain: {itr.domain}"
assert len(list(set(domains))) == len(domains)
def test_summary_display_data(json_data, summary_data):
data = json_data["rocprofiler-sdk-tool"]
num_summary_grps = len(data.metadata.config.summary_groups)
def get_df(domain):
return summary_data[domain]
def get_dims(df):
# return rows x cols
return [df.shape[0], df.shape[1]] if df is not None else [0, 0]
hip = get_df("HIP_API")
marker = get_df("MARKER_API")
dispatch = get_df("KERNEL_DISPATCH")
memcpy = get_df("MEMORY_COPY")
dispatch_and_copy = get_df("KERNEL_DISPATCH + MEMORY_COPY")
hip_and_marker = get_df("HIP_API + MARKER_API") if num_summary_grps > 1 else None
total = get_df("SUMMARY")
expected_hip_and_marker_dims = [20, 9] if hip_and_marker is not None else [0, 0]
assert get_dims(hip) == [13, 9]
assert get_dims(marker) == [7, 9]
assert get_dims(dispatch) == [3, 9]
assert get_dims(memcpy) == [2, 9]
assert get_dims(dispatch_and_copy) == [5, 9]
assert get_dims(hip_and_marker) == expected_hip_and_marker_dims
assert get_dims(total) == [22, 9]
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "marker", "kernel", "memory_copy")
)
def test_otf2_data(otf2_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_otf2_data(
otf2_data, json_data, ("hip", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
@@ -53,8 +53,10 @@ add_test(
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_agent_info.csv
--kernel-stats
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_kernel_stats.csv
--hip-stats ${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hip_stats.csv
--hsa-stats ${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_stats.csv
--hip-stats
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hip_api_stats.csv
--hsa-stats
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_api_stats.csv
--memory-copy-stats
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_memory_copy_stats.csv
--json-input ${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.json
@@ -71,8 +73,8 @@ set(VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_kernel_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_agent_info.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_kernel_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hip_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hip_api_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_api_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_memory_copy_stats.csv)
set_tests_properties(
@@ -392,16 +392,19 @@ def test_memory_copy_trace(
for row in memory_copy_input_data:
assert row["Kind"] == "MEMORY_COPY"
assert row["Direction"] in ("HOST_TO_DEVICE", "DEVICE_TO_HOST")
assert row["Direction"] in (
"MEMORY_COPY_HOST_TO_DEVICE",
"MEMORY_COPY_DEVICE_TO_HOST",
)
src_agent = get_agent(row["Source_Agent_Id"])
dst_agent = get_agent(row["Destination_Agent_Id"])
assert src_agent is not None and dst_agent is not None, f"{agent_info_input_data}"
if row["Direction"] == "HOST_TO_DEVICE":
if row["Direction"] == "MEMORY_COPY_HOST_TO_DEVICE":
assert src_agent["Agent_Type"] == "CPU"
assert dst_agent["Agent_Type"] == "GPU"
elif row["Direction"] == "DEVICE_TO_HOST":
elif row["Direction"] == "MEMORY_COPY_DEVICE_TO_HOST":
assert src_agent["Agent_Type"] == "GPU"
assert dst_agent["Agent_Type"] == "CPU"
@@ -170,13 +170,13 @@ def test_memory_copy_trace(agent_info_input_data, memory_copy_input_data):
assert len(memory_copy_input_data) == 2
def test_row(idx, direction):
assert direction in ("HOST_TO_DEVICE", "DEVICE_TO_HOST")
assert direction in ("MEMORY_COPY_HOST_TO_DEVICE", "MEMORY_COPY_DEVICE_TO_HOST")
row = memory_copy_input_data[idx]
assert row["Direction"] == direction
src_agent = get_agent(row["Source_Agent_Id"])
dst_agent = get_agent(row["Destination_Agent_Id"])
assert src_agent is not None and dst_agent is not None, f"{agent_info_input_data}"
if direction == "HOST_TO_DEVICE":
if direction == "MEMORY_COPY_HOST_TO_DEVICE":
assert src_agent["Agent_Type"] == "CPU"
assert dst_agent["Agent_Type"] == "GPU"
else:
@@ -185,8 +185,8 @@ def test_memory_copy_trace(agent_info_input_data, memory_copy_input_data):
assert int(row["Correlation_Id"]) > 0
assert int(row["End_Timestamp"]) >= int(row["Start_Timestamp"])
test_row(0, "HOST_TO_DEVICE")
test_row(1, "DEVICE_TO_HOST")
test_row(0, "MEMORY_COPY_HOST_TO_DEVICE")
test_row(1, "MEMORY_COPY_DEVICE_TO_HOST")
def test_memory_copy_json_trace(json_data):
@@ -208,14 +208,14 @@ def test_memory_copy_json_trace(json_data):
assert len(memory_copy_data) == 2
def test_row(idx, direction):
assert direction in ("HOST_TO_DEVICE", "DEVICE_TO_HOST")
assert direction in ("MEMORY_COPY_HOST_TO_DEVICE", "MEMORY_COPY_DEVICE_TO_HOST")
row = memory_copy_data[idx]
src_agent = get_agent(row["src_agent_id"])
dst_agent = get_agent(row["dst_agent_id"])
assert get_kind_name(row["kind"]) == "MEMORY_COPY"
assert src_agent is not None, f"{row}"
assert dst_agent is not None, f"{row}"
if direction == "HOST_TO_DEVICE":
if direction == "MEMORY_COPY_HOST_TO_DEVICE":
assert src_agent["type"] == 1
assert dst_agent["type"] == 2
else:
@@ -224,8 +224,8 @@ def test_memory_copy_json_trace(json_data):
assert row["correlation_id"]["internal"] > 0
assert row["end_timestamp"] >= row["start_timestamp"]
test_row(0, "HOST_TO_DEVICE")
test_row(1, "DEVICE_TO_HOST")
test_row(0, "MEMORY_COPY_HOST_TO_DEVICE")
test_row(1, "MEMORY_COPY_DEVICE_TO_HOST")
def test_marker_api_trace(marker_input_data):