* Perfetto submodule

* include/rocprofiler-sdk/cxx/perfetto.hpp

- adapted from tests/common/perfetto.hpp
- updated json-tool to use <rocprofiler-sdk/cxx/perfetto.hpp>

* Update include/rocprofiler-sdk/cxx

- add details/delimit.hpp
- add details/join.hpp
- extend details/mpl.hpp
- extend details/operators.hpp

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

- update MEMORY_COPY direction names

* Preliminary perfetto support

* Update lib/rocprofiler-sdk-tool/generatePerfetto.cpp

- fix getting roctx msg vs. buffer operation name

* Temporary variable restructuring

* Perfetto patches after rebasing onto main

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

- revert name

* Update lib/rocprofiler-sdk-tool/generatePerfetto.cpp

- fix ReadTrace

* Update tests/bin/hip-in-libraries

- sleep_for

* Support PFTRACE output format option in rocprofv3

* Change perfetto logging

* Update rocprofv3 tests to generate pftrace output

* Minor tweak to json-tool.cpp

* Update requirements.txt for perfetto testing

* Fix data race on amount_read in generatePerfetto.cpp

* Add testing for pftrace output

- relatively simple testing which verifies that the pftrace file has the same number of entries as JSON data for HIP/HSA/marker/kernel/memory_copy

* Fix import in perfetto_reader.py

* Fix data race in generatePerfetto.cpp
Этот коммит содержится в:
Jonathan R. Madsen
2024-05-22 15:51:12 -05:00
коммит произвёл GitHub
родитель 92b7326910
Коммит 957bb7a4e5
39 изменённых файлов: 2530 добавлений и 291 удалений
+3
Просмотреть файл
@@ -19,3 +19,6 @@
[submodule "external/filesystem"]
path = external/filesystem
url = https://github.com/gulrak/filesystem.git
[submodule "external/perfetto"]
path = external/perfetto
url = https://android.googlesource.com/platform/external/perfetto
поставляемый
+22
Просмотреть файл
@@ -150,3 +150,25 @@ if(ROCPROFILER_BUILD_DOCS)
REPO_URL https://github.com/jothepro/doxygen-awesome-css.git
REPO_BRANCH "main")
endif()
rocprofiler_checkout_git_submodule(
RECURSIVE
RELATIVE_PATH external/perfetto
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
TEST_FILE meson.build
REPO_URL https://android.googlesource.com/platform/external/perfetto external/perfetto
REPO_BRANCH "v44.0")
add_library(rocprofiler-perfetto-static-library STATIC)
target_sources(
rocprofiler-perfetto-static-library
PRIVATE ${PROJECT_SOURCE_DIR}/external/perfetto/sdk/perfetto.h
${PROJECT_SOURCE_DIR}/external/perfetto/sdk/perfetto.cc)
target_include_directories(
rocprofiler-perfetto-static-library SYSTEM
INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/external/perfetto/sdk>)
set_target_properties(
rocprofiler-perfetto-static-library PROPERTIES POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME rocprofiler-sdk-perfetto)
target_link_libraries(rocprofiler-perfetto
INTERFACE $<BUILD_INTERFACE:rocprofiler-perfetto-static-library>)
поставляемый Подмодуль
+1
Submodule external/perfetto added at eb5ef24c58
+3
Просмотреть файл
@@ -3,7 +3,10 @@ clang-format>=11.0.0,<12.0.0
clang-tidy>=15.0.0,<18.0.0
cmake>=3.21.0
cmake-format
dataclasses
numpy
pandas
perfetto
pycobertura
pytest
pyyaml
+6 -1
Просмотреть файл
@@ -55,9 +55,14 @@ usage() {
echo -e "\t#${GREY} usage (with custom dir): rocprofv3 --hsa-trace -d <out_dir> -o <file_name> <executable>${RESET}\n"
echo -e ""
echo -e "${GREEN}-d | --output-directory ${RESET} For adding output path where the output files will be saved"
echo -e "${GREEN} | --output-format ${RESET} For adding output format"
echo -e "\t#${GREY} usage (with custom dir): rocprofv3 --hsa-trace -d <out_dir> <executable>${RESET}"
echo -e ""
echo -e "${GREEN}--output-format ${RESET} For specifying output format. Case-insensitive, comma separated. Options: CSV, JSON, PFTRACE"
echo -e "\t#${GREY} Examples:"
echo -e "\t#${GREY} (JSON output): rocprofv3 --sys-trace --output-format JSON <executable>"
echo -e "\t#${GREY} (JSON + CSV): rocprofv3 --sys-trace --output-format JSON,CSV <executable>"
echo -e "\t#${GREY} (JSON + PFTRACE): rocprofv3 --sys-trace --output-format JSON,PFTRACE <executable>"
echo -e ""
echo -e "${GREEN}-M | --mangled-kernels ${RESET} Do not demangle the kernel names"
echo -e "${GREEN}-T | --truncate-kernels ${RESET} Truncate the demangled kernel names"
echo -e ""
+2 -1
Просмотреть файл
@@ -3,7 +3,8 @@
# Installation of public C++ headers
#
#
set(ROCPROFILER_CXX_HEADER_FILES hash.hpp name_info.hpp operators.hpp serialization.hpp)
set(ROCPROFILER_CXX_HEADER_FILES hash.hpp name_info.hpp operators.hpp perfetto.hpp
serialization.hpp)
install(
FILES ${ROCPROFILER_CXX_HEADER_FILES}
+1 -1
Просмотреть файл
@@ -3,7 +3,7 @@
# Installation of public C++ headers (implementations)
#
#
set(ROCPROFILER_CXX_DETAILS_HEADER_FILES mpl.hpp name_info.hpp)
set(ROCPROFILER_CXX_DETAILS_HEADER_FILES delimit.hpp join.hpp mpl.hpp name_info.hpp)
install(
FILES ${ROCPROFILER_CXX_DETAILS_HEADER_FILES}
+158
Просмотреть файл
@@ -0,0 +1,158 @@
// 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 <rocprofiler-sdk/cxx/details/mpl.hpp>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
namespace rocprofiler
{
namespace sdk
{
namespace parse
{
template <typename Tp>
inline Tp
from_string(const std::string& str)
{
auto ss = std::stringstream{str};
auto val = Tp{};
ss >> val;
return val;
}
template <typename Tp>
inline Tp
from_string(const char* cstr)
{
auto ss = std::stringstream{cstr};
auto val = Tp{};
ss >> val;
return val;
}
/// \brief tokenize a string into a set
///
template <typename ContainerT = std::vector<std::string>,
typename ValueT = typename ContainerT::value_type,
typename PredicateT = std::function<ValueT(ValueT&&)>>
inline ContainerT
tokenize(
std::string_view line,
std::string_view delimiters = "\"',;: ",
PredicateT&& predicate = [](ValueT&& s) -> ValueT { return s; })
{
using value_type = ValueT;
size_t _beginp = 0; // position that is the beginning of the new string
size_t _delimp = 0; // position of the delimiter in the string
ContainerT _result = {};
if(mpl::reserve(_result, 0))
{
size_t _nmax = 0;
for(char itr : line)
{
if(delimiters.find(itr) != std::string::npos) ++_nmax;
}
mpl::reserve(_result, _nmax);
}
while(_beginp < line.length() && _delimp < line.length())
{
// find the first character (starting at _delimp) that is not a delimiter
_beginp = line.find_first_not_of(delimiters, _delimp);
// if no a character after or at _end that is not a delimiter is not found
// then we are done
if(_beginp == std::string::npos) break;
// starting at the position of the new string, find the next delimiter
_delimp = line.find_first_of(delimiters, _beginp);
auto _tmp = value_type{};
// starting at the position of the new string, get the characters
// between this position and the next delimiter
if(_beginp < line.length()) _tmp = line.substr(_beginp, _delimp - _beginp);
// don't add empty strings
if(!_tmp.empty())
{
mpl::emplace(_result, std::forward<PredicateT>(predicate)(std::move(_tmp)));
}
}
return _result;
}
/// \brief apply a string transformation to substring in between a common delimiter.
///
template <typename PredicateT = std::function<std::string(const std::string&)>>
inline std::string
str_transform(std::string_view input,
std::string_view _begin,
std::string_view _end,
PredicateT&& predicate)
{
size_t _beg_pos = 0; // position that is the beginning of the new string
size_t _end_pos = 0; // position of the delimiter in the string
std::string _result = std::string{input};
while(_beg_pos < _result.length() && _end_pos < _result.length())
{
// find the first sequence of characters after the end-position
_beg_pos = _result.find(_begin, _end_pos);
// if sequence wasn't found, we are done
if(_beg_pos == std::string::npos) break;
// starting after the position of the first delimiter, find the end sequence
if(!_end.empty())
_end_pos = _result.find(_end, _beg_pos + 1);
else
_end_pos = _beg_pos + _begin.length();
// break if not found
if(_end_pos == std::string::npos) break;
// length of the substr being operated on
auto _len = _end_pos - _beg_pos;
// get the substring between the two delimiters (including first delimiter)
auto _sub = _result.substr(_beg_pos, _len);
// apply the transform
auto _transformed = predicate(_sub);
// only replace if necessary
if(_sub != _transformed)
{
_result = _result.replace(_beg_pos, _len, _transformed);
// move end to the end of transformed string
_end_pos = _beg_pos + _transformed.length();
}
}
return _result;
}
} // namespace parse
} // namespace sdk
} // namespace rocprofiler
+281
Просмотреть файл
@@ -0,0 +1,281 @@
// 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 <rocprofiler-sdk/cxx/details/mpl.hpp>
#include <array>
#include <cstring>
#include <initializer_list>
#include <ios>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace rocprofiler
{
namespace sdk
{
namespace join
{
template <typename... ArgsT>
inline void
consume_args(ArgsT&&...)
{}
enum
{
NoQuoteStrings = 0x0,
QuoteStrings = 0x1
};
template <size_t Idx>
struct triplet_config
{
static constexpr auto index() { return Idx; }
std::string_view delimiter = {};
std::string_view prefix = {};
std::string_view suffix = {};
};
using generic_config = triplet_config<0>;
using array_config = triplet_config<1>;
using pair_config = triplet_config<2>;
struct config : generic_config
{
using format_flags_t = std::ios_base::fmtflags;
using base_type = generic_config;
config() = default;
~config() = default;
config(const config&) = default;
config(config&&) noexcept = default;
config& operator=(const config&) = default;
config& operator=(config&&) noexcept = default;
// converting constructor
config(std::string_view _delim)
: base_type{_delim}
{}
// converting constructor
config(const char* const _delim)
: base_type{_delim}
{}
config(generic_config _cfg)
: base_type{_cfg}
{}
config(array_config _cfg)
: array{_cfg}
{}
config(pair_config _cfg)
: pair{_cfg}
{}
config(generic_config _generic, array_config _array)
: base_type{_generic}
, array{_array}
{}
config(generic_config _generic, pair_config _pair)
: base_type{_generic}
, pair{_pair}
{}
config(array_config _array, pair_config _pair)
: array{_array}
, pair{_pair}
{}
format_flags_t flags = std::ios_base::boolalpha;
array_config array = {", ", "[", "]"};
pair_config pair = {", ", "{", "}"};
};
namespace impl
{
template <int TraitT, typename ArgT>
inline decltype(auto)
join_arg(config _cfg, ArgT&& _v)
{
using arg_type = mpl::basic_identity_t<ArgT>;
constexpr bool _is_string_type = mpl::is_string_type<arg_type>::value;
constexpr bool _is_iterable = mpl::is_iterable<arg_type>(0);
constexpr bool _has_traits_type = mpl::has_traits<arg_type>(0);
constexpr bool _has_key_type = mpl::has_key_type<arg_type>(0);
constexpr bool _has_value_type = mpl::has_value_type<arg_type>(0);
constexpr bool _has_mapped_type = mpl::has_mapped_type<arg_type>(0);
if constexpr(_is_string_type)
{
if constexpr(TraitT == QuoteStrings)
{
return std::string{"\""} + std::string{std::forward<ArgT>(_v)} + std::string{"\""};
}
else
{
return std::forward<ArgT>(_v);
}
}
else if constexpr(_is_iterable && !_has_traits_type &&
(_has_value_type || (_has_key_type && _has_mapped_type)))
{
if constexpr(_has_key_type && _has_mapped_type)
{
std::stringstream _ss{};
_ss.setf(_cfg.flags);
for(auto&& itr : std::forward<ArgT>(_v))
_ss << _cfg.array.delimiter << _cfg.pair.prefix << join_arg<TraitT>(_cfg, itr.first)
<< _cfg.pair.delimiter << join_arg<TraitT>(_cfg, itr.second)
<< _cfg.pair.suffix;
auto _ret = _ss.str();
auto&& _len = _cfg.array.delimiter.length();
return (_ret.length() > _len) ? (std::string{_cfg.array.prefix} + _ret.substr(_len) +
std::string{_cfg.array.suffix})
: std::string{};
}
else if constexpr(_has_value_type)
{
std::stringstream _ss{};
_ss.setf(_cfg.flags);
for(auto&& itr : std::forward<ArgT>(_v))
_ss << _cfg.array.delimiter << join_arg<TraitT>(_cfg, itr);
auto _ret = _ss.str();
auto&& _len = _cfg.array.delimiter.length();
return (_ret.length() > _len) ? (std::string{_cfg.array.prefix} + _ret.substr(_len) +
std::string{_cfg.array.suffix})
: std::string{};
}
}
else if constexpr(mpl::supports_ostream<ArgT>(0))
{
return std::forward<ArgT>(_v);
}
else
{
static_assert(_is_iterable, "Type is not iterable");
static_assert(!_has_traits_type, "Type has a traits type");
if constexpr(!_has_value_type)
{
static_assert(_has_key_type && _has_mapped_type,
"Type must have a key_type and mapped_type if there is no value_type");
}
else
{
static_assert(_has_value_type,
"Type must have a value_type if there is no key_type and mapped_type");
}
static_assert(std::is_empty<ArgT>::value,
"Error! argument type cannot be written to output stream");
}
// suppress any unused but set variable warnings
consume_args(_is_string_type,
_is_iterable,
_has_traits_type,
_has_key_type,
_has_value_type,
_has_mapped_type);
}
} // namespace impl
template <int TraitT = NoQuoteStrings, typename... Args>
auto
join(config _cfg, Args&&... _args)
{
static_assert(std::is_trivially_copyable<config>::value,
"Error! config is not trivially copyable");
std::stringstream _ss{};
_ss.setf(_cfg.flags);
((_ss << ((mpl::is_empty(_args)) ? std::string_view{} : std::string_view{_cfg.delimiter})
<< impl::join_arg<TraitT>(_cfg, _args)),
...);
auto _ret = _ss.str();
auto&& _len = _cfg.delimiter.length();
auto _cmp =
strncmp(std::string_view{_ret}.data(), std::string_view{_cfg.delimiter}.data(), _len) == 0;
return (_ret.length() > _len) ? (std::string{_cfg.prefix} +
((_cmp) ? _ret.substr(_len) : _ret) + std::string{_cfg.suffix})
: std::string{};
}
template <int TraitT = NoQuoteStrings, typename... Args>
auto
join(std::array<std::string_view, 3>&& _delims, Args&&... _args)
{
auto _cfg = config{};
_cfg.delimiter = _delims.at(0);
_cfg.prefix = _delims.at(1);
_cfg.suffix = _delims.at(2);
return join(_cfg, std::forward<Args>(_args)...);
}
template <int TraitT = NoQuoteStrings,
typename DelimT,
typename... Args,
std::enable_if_t<!mpl::is_basic_same<config, DelimT>::value, int> = 0>
auto
join(DelimT&& _delim, Args&&... _args)
{
using delim_type = mpl::basic_identity_t<DelimT>;
if constexpr(std::is_constructible<config, delim_type>::value)
{
auto _cfg = config{std::forward<DelimT>(_delim)};
return join<TraitT>(_cfg, std::forward<Args>(_args)...);
}
else if constexpr(std::is_same<delim_type, char>::value)
{
auto _cfg = config{};
const char _delim_c[2] = {_delim, '\0'};
_cfg.delimiter = _delim_c;
return join<TraitT>(_cfg, std::forward<Args>(_args)...);
}
else
{
auto _cfg = config{};
_cfg.delimiter = std::string_view{_delim};
return join<TraitT>(_cfg, std::forward<Args>(_args)...);
}
}
template <typename ArgT>
auto
quoted(ArgT&& _arg)
{
auto _cfg = config{};
_cfg.prefix = "\"";
_cfg.suffix = "\"";
return join(_cfg, std::forward<ArgT>(_arg));
}
} // namespace join
} // namespace sdk
} // namespace rocprofiler
+173
Просмотреть файл
@@ -23,17 +23,61 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#define ROCPROFILER_IMPL_HAS_CONCEPT(NAME, TRAIT) \
template <typename Tp, typename = typename Tp::TRAIT> \
inline constexpr bool NAME(int) \
{ \
return true; \
} \
\
template <typename Tp> \
inline constexpr bool NAME(long) \
{ \
return false; \
}
#define ROCPROFILER_IMPL_SFINAE_CONCEPT(NAME, ...) \
template <typename Tp> \
struct NAME \
{ \
private: \
static constexpr auto sfinae(int) -> decltype(__VA_ARGS__, bool()) { return true; } \
\
static constexpr auto sfinae(long) { return false; } \
\
public: \
static constexpr bool value = sfinae(0); \
constexpr auto operator()() const { return sfinae(0); } \
};
namespace rocprofiler
{
namespace sdk
{
namespace mpl
{
template <typename Tp>
struct unqualified_identity
{
using type = std::remove_cv_t<std::remove_reference_t<std::decay_t<Tp>>>;
};
template <typename Tp>
using unqualified_identity_t = typename unqualified_identity<Tp>::type;
template <typename Tp, typename Up>
struct is_same_unqualified_identity
: std::is_same<unqualified_identity_t<Tp>, unqualified_identity_t<Up>>
{};
template <typename Tp>
struct string_support
{
@@ -79,6 +123,135 @@ struct string_support<std::string>
type operator()(const char* val) const { return type{val}; }
};
namespace impl
{
template <typename Tp>
struct is_string_type : std::false_type
{};
template <>
struct is_string_type<std::string> : std::true_type
{};
template <>
struct is_string_type<char*> : std::true_type
{};
template <>
struct is_string_type<const char*> : std::true_type
{};
template <>
struct is_string_type<std::string_view> : std::true_type
{};
} // namespace impl
template <typename Tp>
struct is_string_type : impl::is_string_type<unqualified_identity_t<Tp>>
{};
// template <typename Tp>
// struct can_stringify
// {
// private:
// static constexpr auto sfinae(int)
// -> decltype(std::declval<std::ostream&>() << std::declval<Tp>(), bool())
// {
// return true;
// }
// static constexpr auto sfinae(long) { return false; }
// public:
// static constexpr bool value = sfinae(0);
// constexpr auto operator()() const { return sfinae(0); }
// };
ROCPROFILER_IMPL_HAS_CONCEPT(has_traits, traits_type)
ROCPROFILER_IMPL_HAS_CONCEPT(has_value_type, value_type)
ROCPROFILER_IMPL_HAS_CONCEPT(has_key_type, key_type)
ROCPROFILER_IMPL_HAS_CONCEPT(has_mapped_type, mapped_type)
ROCPROFILER_IMPL_SFINAE_CONCEPT(has_empty_member_function, std::declval<Tp>().empty())
ROCPROFILER_IMPL_SFINAE_CONCEPT(can_stringify, std::declval<std::ostream&>() << std::declval<Tp>())
ROCPROFILER_IMPL_SFINAE_CONCEPT(is_iterable,
std::begin(std::declval<Tp>()),
std::end(std::declval<Tp>()))
// compatability
template <typename Tp>
using supports_ostream = can_stringify<Tp>;
template <typename ArgT>
inline bool
is_empty(ArgT&& _v)
{
using arg_type = unqualified_identity_t<ArgT>;
if constexpr(has_empty_member_function<arg_type>::value)
{
return std::forward<ArgT>(_v).empty();
}
else if constexpr(is_string_type<arg_type>::value)
{
static_assert(std::is_constructible<std::string_view, ArgT>::value,
"not string_view constructible");
return std::string_view{std::forward<ArgT>(_v)}.empty();
}
return false;
}
namespace impl
{
template <typename ContainerT, typename... Args>
inline auto
emplace(ContainerT& _c, int, Args&&... _args)
-> decltype(_c.emplace_back(std::forward<Args>(_args)...))
{
return _c.emplace_back(std::forward<Args>(_args)...);
}
template <typename ContainerT, typename... Args>
inline auto
emplace(ContainerT& _c, long, Args&&... _args) -> decltype(_c.emplace(std::forward<Args>(_args)...))
{
return _c.emplace(std::forward<Args>(_args)...);
}
template <typename ContainerT, typename ArgT>
inline auto
reserve(ContainerT& _c, int, ArgT _arg) -> decltype(_c.reserve(_arg), bool())
{
_c.reserve(_arg);
return true;
}
template <typename ContainerT, typename ArgT>
inline auto
reserve(ContainerT&, long, ArgT)
{
return false;
}
} // namespace impl
template <typename ContainerT, typename... Args>
inline auto
emplace(ContainerT& _c, Args&&... _args)
{
return impl::emplace(_c, 0, std::forward<Args>(_args)...);
}
template <typename ContainerT, typename ArgT>
inline auto
reserve(ContainerT& _c, ArgT _arg)
{
return impl::reserve(_c, 0, _arg);
}
} // namespace mpl
} // namespace sdk
} // namespace rocprofiler
#undef ROCPROFILER_IMPL_HAS_CONCEPT
#undef ROCPROFILER_IMPL_SFINAE_CONCEPT
+73 -1
Просмотреть файл
@@ -32,7 +32,11 @@
#define ROCPROFILER_CXX_DECLARE_OPERATORS(TYPE) \
bool operator==(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure); \
bool operator!=(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure);
bool operator!=(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure); \
bool operator<(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure); \
bool operator>(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure); \
bool operator<=(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure); \
bool operator>=(TYPE lhs, TYPE rhs) ROCPROFILER_ATTRIBUTE(pure);
#define ROCPROFILER_CXX_DEFINE_NE_OPERATOR(TYPE) \
inline bool operator!=(TYPE lhs, TYPE rhs) { return !(lhs == rhs); }
@@ -43,6 +47,17 @@
return ::rocprofiler::sdk::operators::equal(lhs, rhs); \
}
#define ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(TYPE) \
inline bool operator<(TYPE lhs, TYPE rhs) \
{ \
return ::rocprofiler::sdk::operators::less(lhs, rhs); \
}
#define ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(TYPE) \
inline bool operator>(TYPE lhs, TYPE rhs) { return (lhs == rhs || !(lhs < rhs)); } \
inline bool operator<=(TYPE lhs, TYPE rhs) { return (lhs == rhs || lhs < rhs); } \
inline bool operator>=(TYPE lhs, TYPE rhs) { return !(lhs < rhs); }
namespace rocprofiler
{
namespace sdk
@@ -53,6 +68,10 @@ template <typename Tp>
bool
equal(Tp lhs, Tp rhs) ROCPROFILER_ATTRIBUTE(pure);
template <typename Tp>
bool
less(Tp lhs, Tp rhs) ROCPROFILER_ATTRIBUTE(pure);
template <typename Tp>
bool
equal(Tp lhs, Tp rhs)
@@ -60,6 +79,14 @@ equal(Tp lhs, Tp rhs)
static_assert(sizeof(Tp) == sizeof(uint64_t), "error! only for opaque handle types");
return lhs.handle == rhs.handle;
}
template <typename Tp>
bool
less(Tp lhs, Tp rhs)
{
static_assert(sizeof(Tp) == sizeof(uint64_t), "error! only for opaque handle types");
return lhs.handle < rhs.handle;
}
} // namespace operators
} // namespace sdk
} // namespace rocprofiler
@@ -116,7 +143,52 @@ ROCPROFILER_CXX_DEFINE_NE_OPERATOR(hsa_executable_t)
ROCPROFILER_CXX_DEFINE_NE_OPERATOR(const rocprofiler_agent_v0_t&)
ROCPROFILER_CXX_DEFINE_NE_OPERATOR(rocprofiler_dim3_t)
// definitions of operator<
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_context_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_agent_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_queue_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_buffer_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_counter_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_profile_config_id_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_callback_thread_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_agent_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_signal_t)
ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_executable_t)
inline bool
operator<(const rocprofiler_agent_v0_t& lhs, const rocprofiler_agent_v0_t& rhs)
{
return (lhs.id < rhs.id);
}
inline bool
operator<(rocprofiler_dim3_t lhs, rocprofiler_dim3_t rhs)
{
const auto magnitude = [](rocprofiler_dim3_t dim_v) { return dim_v.x * dim_v.y * dim_v.z; };
auto lhs_m = magnitude(lhs);
auto rhs_m = magnitude(rhs);
return (lhs_m == rhs_m) ? std::tie(lhs.x, lhs.y, lhs.z) < std::tie(rhs.x, rhs.y, rhs.z)
: (lhs_m < rhs_m);
}
// definitions of operator>, operator<=, operator>=
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_context_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_agent_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_queue_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_buffer_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_counter_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_profile_config_id_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_callback_thread_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(hsa_agent_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(hsa_signal_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(hsa_executable_t)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(const rocprofiler_agent_v0_t&)
ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_dim3_t)
// cleanup defines
#undef ROCPROFILER_CXX_DECLARE_OPERATORS
#undef ROCPROFILER_CXX_DEFINE_NE_OPERATOR
#undef ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR
#undef ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR
#undef ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS
+168
Просмотреть файл
@@ -0,0 +1,168 @@
// 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 <rocprofiler-sdk/cxx/details/mpl.hpp>
#include <cstddef>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#define ROCPROFILER_DEFINE_PERFETTO_CATEGORY(NAME, DESC, ...) \
namespace rocprofiler \
{ \
namespace sdk \
{ \
template <> \
struct perfetto_category<__VA_ARGS__> \
{ \
static constexpr auto name = NAME; \
static constexpr auto description = DESC; \
}; \
} \
}
#define ROCPROFILER_DEFINE_CATEGORY(NS, VALUE, DESC) \
namespace rocprofiler \
{ \
namespace sdk \
{ \
namespace NS \
{ \
struct VALUE; \
} \
} \
} \
ROCPROFILER_DEFINE_PERFETTO_CATEGORY(#VALUE, DESC, NS::VALUE)
#define ROCPROFILER_PERFETTO_CATEGORY(TYPE) \
::perfetto::Category(::rocprofiler::sdk::perfetto_category<::rocprofiler::sdk::TYPE>::name) \
.SetDescription( \
::rocprofiler::sdk::perfetto_category<::rocprofiler::sdk::TYPE>::description)
namespace rocprofiler
{
namespace sdk
{
template <typename Tp>
struct perfetto_category;
} // namespace sdk
} // namespace rocprofiler
ROCPROFILER_DEFINE_CATEGORY(category, hsa_api, "HSA API function")
ROCPROFILER_DEFINE_CATEGORY(category, hip_api, "HIP API function")
ROCPROFILER_DEFINE_CATEGORY(category, marker_api, "Marker API region")
ROCPROFILER_DEFINE_CATEGORY(category, kernel_dispatch, "GPU kernel dispatch")
ROCPROFILER_DEFINE_CATEGORY(category, memory_copy, "Async memory copy")
#define ROCPROFILER_PERFETTO_CATEGORIES \
ROCPROFILER_PERFETTO_CATEGORY(category::hsa_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::hip_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::marker_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::kernel_dispatch), \
ROCPROFILER_PERFETTO_CATEGORY(category::memory_copy)
#include <perfetto.h>
PERFETTO_DEFINE_CATEGORIES(ROCPROFILER_PERFETTO_CATEGORIES);
namespace rocprofiler
{
namespace sdk
{
using perfetto_event_context_t = ::perfetto::EventContext;
template <typename Np, typename Tp>
auto
add_perfetto_annotation(perfetto_event_context_t& ctx, Np&& _name, Tp&& _val)
{
namespace mpl = ::rocprofiler::sdk::mpl;
using named_type = mpl::unqualified_identity_t<Np>;
using value_type = mpl::unqualified_identity_t<Tp>;
static_assert(mpl::is_string_type<named_type>::value, "Error! name is not a string type");
auto _get_dbg = [&]() {
auto* _dbg = ctx.event()->add_debug_annotations();
_dbg->set_name(std::string_view{std::forward<Np>(_name)}.data());
return _dbg;
};
if constexpr(std::is_same<value_type, std::string_view>::value)
{
_get_dbg()->set_string_value(_val.data());
}
else if constexpr(mpl::is_string_type<value_type>::value)
{
_get_dbg()->set_string_value(std::forward<Tp>(_val));
}
else if constexpr(std::is_same<value_type, bool>::value)
{
_get_dbg()->set_bool_value(_val);
}
else if constexpr(std::is_enum<value_type>::value)
{
_get_dbg()->set_int_value(static_cast<int64_t>(_val));
}
else if constexpr(std::is_floating_point<value_type>::value)
{
_get_dbg()->set_double_value(static_cast<double>(_val));
}
else if constexpr(std::is_integral<value_type>::value)
{
if constexpr(std::is_unsigned<value_type>::value)
{
_get_dbg()->set_uint_value(_val);
}
else
{
_get_dbg()->set_int_value(_val);
}
}
else if constexpr(std::is_pointer<value_type>::value)
{
_get_dbg()->set_pointer_value(reinterpret_cast<uint64_t>(_val));
}
else if constexpr(mpl::can_stringify<value_type>::value)
{
auto _ss = std::stringstream{};
_ss << std::forward<Tp>(_val);
_get_dbg()->set_string_value(_ss.str());
}
else
{
static_assert(std::is_empty<value_type>::value, "Error! unsupported data type");
}
}
} // namespace sdk
} // namespace rocprofiler
#undef ROCPROFILER_DEFINE_PERFETTO_CATEGORY
#undef ROCPROFILER_DEFINE_CATEGORY
#undef ROCPROFILER_PERFETTO_CATEGORY
#undef ROCPROFILER_PERFETTO_CATEGORIES
+9 -3
Просмотреть файл
@@ -11,6 +11,7 @@ set(TOOL_HEADERS
domain_type.hpp
generateCSV.hpp
generateJSON.hpp
generatePerfetto.hpp
helper.hpp
output_file.hpp
statistics.hpp
@@ -22,6 +23,7 @@ set(TOOL_SOURCES
domain_type.cpp
generateCSV.cpp
generateJSON.cpp
generatePerfetto.cpp
helper.cpp
main.c
output_file.cpp
@@ -36,9 +38,13 @@ add_subdirectory(plugins)
target_link_libraries(
rocprofiler-sdk-tool
PRIVATE rocprofiler::rocprofiler-shared-library rocprofiler::rocprofiler-headers
rocprofiler::rocprofiler-build-flags rocprofiler::rocprofiler-memcheck
rocprofiler::rocprofiler-common-library rocprofiler::rocprofiler-cereal)
PRIVATE rocprofiler::rocprofiler-shared-library
rocprofiler::rocprofiler-headers
rocprofiler::rocprofiler-build-flags
rocprofiler::rocprofiler-memcheck
rocprofiler::rocprofiler-common-library
rocprofiler::rocprofiler-cereal
rocprofiler::rocprofiler-perfetto)
set_target_properties(
rocprofiler-sdk-tool
+4 -3
Просмотреть файл
@@ -271,10 +271,11 @@ config::config()
break;
}
csv_output = entries.count("CSV") > 0 || entries.empty();
json_output = entries.count("JSON") > 0;
csv_output = entries.count("CSV") > 0 || entries.empty();
json_output = entries.count("JSON") > 0;
pftrace_output = entries.count("PFTRACE") > 0;
const auto supported_formats = std::set<std::string_view>{"CSV", "JSON"};
const auto supported_formats = std::set<std::string_view>{"CSV", "JSON", "PFTRACE"};
for(const auto& itr : entries)
{
LOG_IF(FATAL, supported_formats.count(itr) == 0)
+1
Просмотреть файл
@@ -75,6 +75,7 @@ struct config
bool stats = get_env("ROCPROF_STATS", false);
bool csv_output = false;
bool json_output = false;
bool pftrace_output = false;
int mpi_size = get_mpi_size();
int mpi_rank = get_mpi_rank();
std::string output_path = get_env("ROCPROF_OUTPUT_PATH", fs::current_path().string());
+1
Просмотреть файл
@@ -28,6 +28,7 @@
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
#include <unistd.h>
#include <iomanip>
#include <string_view>
+529
Просмотреть файл
@@ -0,0 +1,529 @@
// 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 "generatePerfetto.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "lib/common/utility.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
#include <atomic>
#include <future>
#include <rocprofiler-sdk/cxx/hash.hpp>
#include <rocprofiler-sdk/cxx/operators.hpp>
#include <rocprofiler-sdk/cxx/perfetto.hpp>
#include <map>
#include <thread>
#include <unordered_map>
#include <utility>
namespace rocprofiler
{
namespace tool
{
namespace
{
auto main_tid = common::get_tid();
template <typename Tp>
size_t
get_hash_id(Tp&& _val)
{
if constexpr(!std::is_pointer<Tp>::value)
return std::hash<Tp>{}(std::forward<Tp>(_val));
else if constexpr(std::is_same<Tp, const char*>::value)
return get_hash_id(std::string_view{_val});
else
return get_hash_id(*_val);
}
} // namespace
void
write_perfetto(
tool_table* tool_functions,
uint64_t /*pid*/,
std::vector<rocprofiler_agent_v0_t> agent_data,
std::deque<rocprofiler_buffer_tracing_hip_api_record_t>* hip_api_data,
std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>* hsa_api_data,
std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>* kernel_dispatch_data,
std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>* memory_copy_data,
std::deque<rocprofiler_buffer_tracing_marker_api_record_t>* marker_api_data,
std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>* /*scratch_api_data*/)
{
namespace sdk = ::rocprofiler::sdk;
auto agents_map = std::unordered_map<rocprofiler_agent_id_t, rocprofiler_agent_t>{};
for(auto itr : agent_data)
agents_map.emplace(itr.id, itr);
auto args = ::perfetto::TracingInitArgs{};
auto track_event_cfg = ::perfetto::protos::gen::TrackEventConfig{};
auto cfg = ::perfetto::TraceConfig{};
// environment settings
auto shmem_size_hint = size_t{64};
auto buffer_size_kb = size_t{1024000};
auto* buffer_config = cfg.add_buffers();
buffer_config->set_size_kb(buffer_size_kb);
buffer_config->set_fill_policy(
::perfetto::protos::gen::TraceConfig_BufferConfig_FillPolicy_DISCARD);
auto* ds_cfg = cfg.add_data_sources()->mutable_config();
ds_cfg->set_name("track_event"); // this MUST be track_event
ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
args.shmem_size_hint_kb = shmem_size_hint;
args.backends |= ::perfetto::kInProcessBackend;
::perfetto::Tracing::Initialize(args);
::perfetto::TrackEvent::Register();
auto tracing_session = ::perfetto::Tracing::NewTrace();
tracing_session->Setup(cfg);
tracing_session->StartBlocking();
auto tids = std::set<rocprofiler_thread_id_t>{};
auto demangled = std::unordered_map<std::string_view, std::string>{};
auto agent_thread_ids = std::unordered_map<rocprofiler_agent_id_t, std::set<uint64_t>>{};
auto agent_queue_ids =
std::unordered_map<rocprofiler_agent_id_t, std::unordered_set<rocprofiler_queue_id_t>>{};
auto thread_indexes = std::unordered_map<rocprofiler_thread_id_t, uint64_t>{};
auto kernel_sym_data = get_kernel_symbol_data();
auto thread_tracks = std::unordered_map<rocprofiler_thread_id_t, ::perfetto::Track>{};
auto agent_thread_tracks =
std::unordered_map<rocprofiler_agent_id_t,
std::unordered_map<uint64_t, ::perfetto::Track>>{};
auto agent_queue_tracks =
std::unordered_map<rocprofiler_agent_id_t,
std::unordered_map<rocprofiler_queue_id_t, ::perfetto::Track>>{};
auto _get_agent = [&agent_data](rocprofiler_agent_id_t _id) -> const rocprofiler_agent_t* {
for(const auto& itr : agent_data)
{
if(_id == itr.id) return &itr;
}
return CHECK_NOTNULL(nullptr);
};
{
for(auto itr : *hsa_api_data)
tids.emplace(itr.thread_id);
for(auto itr : *hip_api_data)
tids.emplace(itr.thread_id);
for(auto itr : *marker_api_data)
tids.emplace(itr.thread_id);
for(auto itr : *memory_copy_data)
agent_thread_ids[itr.dst_agent_id].emplace(itr.thread_id);
for(auto itr : *kernel_dispatch_data)
agent_queue_ids[itr.dispatch_info.agent_id].emplace(itr.dispatch_info.queue_id);
}
uint64_t nthrn = 0;
for(auto itr : tids)
{
if(itr == main_tid)
{
thread_indexes.emplace(main_tid, 0);
thread_tracks.emplace(main_tid, ::perfetto::ThreadTrack::Current());
}
else
{
auto _idx = ++nthrn;
thread_indexes.emplace(itr, _idx);
auto _track = ::perfetto::Track{itr};
auto _desc = _track.Serialize();
auto _namess = std::stringstream{};
_namess << "THREAD " << _idx << " (" << itr << ")";
_desc.set_name(_namess.str());
perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
thread_tracks.emplace(itr, _track);
}
}
for(const auto& itr : agent_thread_ids)
{
const auto* _agent = _get_agent(itr.first);
for(auto titr : itr.second)
{
auto _namess = std::stringstream{};
_namess << "COPY to [" << _agent->logical_node_id << "] THREAD ["
<< thread_indexes.at(titr) << "]";
if(_agent->type == ROCPROFILER_AGENT_TYPE_CPU)
_namess << " CPU";
else if(_agent->type == ROCPROFILER_AGENT_TYPE_GPU)
_namess << " GPU";
auto _track = ::perfetto::Track{get_hash_id(_namess.str())};
auto _desc = _track.Serialize();
_desc.set_name(_namess.str());
perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
agent_thread_tracks[itr.first].emplace(titr, _track);
}
}
for(const auto& aitr : agent_queue_ids)
{
uint32_t nqueue = 0;
for(auto qitr : aitr.second)
{
const auto* _agent = _get_agent(aitr.first);
auto _namess = std::stringstream{};
_namess << "COMPUTE [" << _agent->logical_node_id << "] QUEUE [" << nqueue++ << "] ";
if(_agent->type == ROCPROFILER_AGENT_TYPE_CPU)
_namess << "CPU";
else if(_agent->type == ROCPROFILER_AGENT_TYPE_GPU)
_namess << "GPU";
auto _track = ::perfetto::Track{get_hash_id(_namess.str())};
auto _desc = _track.Serialize();
_desc.set_name(_namess.str());
perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
agent_queue_tracks[aitr.first].emplace(qitr, _track);
}
}
// trace events
{
auto buffer_names = sdk::get_buffer_tracing_names();
auto callbk_name_info = sdk::get_callback_tracing_names();
for(auto itr : *hsa_api_data)
{
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = thread_tracks.at(itr.thread_id);
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::hsa_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"end_ns",
itr.end_timestamp,
"delta_ns",
(itr.end_timestamp - itr.start_timestamp),
"tid",
itr.thread_id,
"kind",
itr.kind,
"operation",
itr.operation,
"corr_id",
itr.correlation_id.internal);
TRACE_EVENT_END(
sdk::perfetto_category<sdk::category::hsa_api>::name, track, itr.end_timestamp);
}
for(auto itr : *hip_api_data)
{
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = thread_tracks.at(itr.thread_id);
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::hip_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"end_ns",
itr.end_timestamp,
"delta_ns",
(itr.end_timestamp - itr.start_timestamp),
"tid",
itr.thread_id,
"kind",
itr.kind,
"operation",
itr.operation,
"corr_id",
itr.correlation_id.internal);
TRACE_EVENT_END(
sdk::perfetto_category<sdk::category::hip_api>::name, track, itr.end_timestamp);
}
for(auto itr : *marker_api_data)
{
auto& track = thread_tracks.at(itr.thread_id);
auto name = (itr.kind == ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API &&
itr.operation != ROCPROFILER_MARKER_CORE_API_ID_roctxGetThreadId)
? tool_functions->tool_get_roctx_msg_fn(itr.correlation_id.internal)
: buffer_names.at(itr.kind, itr.operation);
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::marker_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"end_ns",
itr.end_timestamp,
"delta_ns",
(itr.end_timestamp - itr.start_timestamp),
"tid",
itr.thread_id,
"kind",
itr.kind,
"operation",
itr.operation,
"corr_id",
itr.correlation_id.internal);
TRACE_EVENT_END(
sdk::perfetto_category<sdk::category::marker_api>::name, track, itr.end_timestamp);
}
for(auto itr : *memory_copy_data)
{
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = agent_thread_tracks.at(itr.dst_agent_id).at(itr.thread_id);
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::memory_copy>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"end_ns",
itr.end_timestamp,
"delta_ns",
(itr.end_timestamp - itr.start_timestamp),
"kind",
itr.kind,
"operation",
itr.operation,
"src_agent",
agents_map.at(itr.src_agent_id).logical_node_id,
"dst_agent",
agents_map.at(itr.dst_agent_id).logical_node_id,
"copy_bytes",
itr.bytes,
"corr_id",
itr.correlation_id.internal,
"tid",
itr.thread_id);
TRACE_EVENT_END(
sdk::perfetto_category<sdk::category::memory_copy>::name, track, itr.end_timestamp);
}
for(auto itr : *kernel_dispatch_data)
{
const auto& info = itr.dispatch_info;
const kernel_symbol_data* sym = nullptr;
for(const auto& kitr : kernel_sym_data)
{
if(kitr.kernel_id == info.kernel_id)
{
sym = &kitr;
break;
}
}
CHECK(sym != nullptr);
auto name = std::string_view{sym->kernel_name};
auto& track = agent_queue_tracks.at(info.agent_id).at(info.queue_id);
if(demangled.find(name) == demangled.end())
{
demangled.emplace(name, common::cxx_demangle(name));
}
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
::perfetto::StaticString(demangled.at(name).c_str()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"end_ns",
itr.end_timestamp,
"delta_ns",
(itr.end_timestamp - itr.start_timestamp),
"kind",
itr.kind,
"agent",
agents_map.at(info.agent_id).logical_node_id,
"corr_id",
itr.correlation_id.internal,
"queue",
info.queue_id.handle,
"tid",
itr.thread_id,
"kernel_id",
info.kernel_id,
"private_segment_size",
info.private_segment_size,
"group_segment_size",
info.group_segment_size,
"workgroup_size",
info.workgroup_size.x * info.workgroup_size.y * info.workgroup_size.z,
"grid_size",
info.grid_size.x * info.grid_size.y * info.grid_size.z);
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
track,
itr.end_timestamp);
}
}
// counter tracks
{
// memory copy counter track
auto mem_cpy_endpoints = std::map<rocprofiler_agent_id_t, std::map<uint64_t, uint64_t>>{};
auto mem_cpy_extremes = std::pair<uint64_t, uint64_t>{};
for(auto itr : *memory_copy_data)
{
uint64_t _mean_timestamp =
itr.start_timestamp + (0.5 * (itr.end_timestamp - itr.start_timestamp));
mem_cpy_endpoints[itr.dst_agent_id].emplace(itr.start_timestamp - 1000, 0);
mem_cpy_endpoints[itr.dst_agent_id].emplace(itr.start_timestamp, 0);
mem_cpy_endpoints[itr.dst_agent_id].emplace(_mean_timestamp, 0);
mem_cpy_endpoints[itr.dst_agent_id].emplace(itr.end_timestamp, 0);
mem_cpy_endpoints[itr.dst_agent_id].emplace(itr.end_timestamp + 1000, 0);
mem_cpy_extremes = std::make_pair(std::min(mem_cpy_extremes.first, itr.start_timestamp),
std::max(mem_cpy_extremes.second, itr.end_timestamp));
}
for(auto itr : *memory_copy_data)
{
auto mbeg = mem_cpy_endpoints.at(itr.dst_agent_id).lower_bound(itr.start_timestamp);
auto mend = mem_cpy_endpoints.at(itr.dst_agent_id).upper_bound(itr.end_timestamp);
LOG_IF(FATAL, mbeg == mend) << "Missing range for timestamp [" << itr.start_timestamp
<< ", " << itr.end_timestamp << "]";
for(auto mitr = mbeg; mitr != mend; ++mitr)
mitr->second += itr.bytes;
}
constexpr auto bytes_multiplier = 1024;
auto mem_cpy_tracks =
std::unordered_map<rocprofiler_agent_id_t, ::perfetto::CounterTrack>{};
auto mem_cpy_cnt_names = std::vector<std::string>{};
mem_cpy_cnt_names.reserve(mem_cpy_endpoints.size());
for(auto& mitr : mem_cpy_endpoints)
{
mem_cpy_endpoints[mitr.first].emplace(mem_cpy_extremes.first - 5000, 0);
mem_cpy_endpoints[mitr.first].emplace(mem_cpy_extremes.second + 5000, 0);
auto _track_name = std::stringstream{};
const auto* _agent = _get_agent(mitr.first);
if(_agent->type == ROCPROFILER_AGENT_TYPE_CPU)
_track_name << "COPY BYTES to [" << _agent->logical_node_id << "] CPU";
else if(_agent->type == ROCPROFILER_AGENT_TYPE_GPU)
_track_name << "COPY BYTES to [" << _agent->logical_node_id << "] GPU";
constexpr auto _unit = ::perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES;
auto& _name = mem_cpy_cnt_names.emplace_back(_track_name.str());
mem_cpy_tracks.emplace(mitr.first,
::perfetto::CounterTrack{_name.c_str()}
.set_unit(_unit)
.set_unit_multiplier(bytes_multiplier)
.set_is_incremental(false));
}
for(auto& mitr : mem_cpy_endpoints)
{
for(auto itr : mitr.second)
{
TRACE_COUNTER(sdk::perfetto_category<sdk::category::memory_copy>::name,
mem_cpy_tracks.at(mitr.first),
itr.first,
itr.second / bytes_multiplier);
}
}
}
::perfetto::TrackEvent::Flush();
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 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](
::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);
amount_read += _args.size;
}
ROCP_INFO_IF(!_args.has_more && amount_read > 0)
<< "Wrote " << amount_read << " B to perfetto trace file";
if(!_args.has_more) is_done.set_value();
};
for(size_t i = 0; i < 2; ++i)
{
ROCP_TRACE << "Reading trace...";
amount_read = 0;
is_done = std::promise<void>{};
tracing_session->ReadTrace(_reader);
is_done.get_future().wait();
}
ROCP_TRACE << "Destroying tracing session...";
tracing_session.reset();
ROCP_TRACE << "Flushing trace output stream...";
(*ofs) << std::flush;
ROCP_TRACE << "Destroying trace output stream...";
if(cleanup) cleanup(ofs);
}
} // namespace tool
} // namespace rocprofiler
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+45
Просмотреть файл
@@ -0,0 +1,45 @@
// 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 <deque>
namespace rocprofiler
{
namespace tool
{
void
write_perfetto(
tool_table* tool_functions,
uint64_t pid,
std::vector<rocprofiler_agent_v0_t> agent_data,
std::deque<rocprofiler_buffer_tracing_hip_api_record_t>* hip_api_data,
std::deque<rocprofiler_buffer_tracing_hsa_api_record_t>* hsa_api_data,
std::deque<rocprofiler_buffer_tracing_kernel_dispatch_record_t>* kernel_dispatch_data,
std::deque<rocprofiler_buffer_tracing_memory_copy_record_t>* memory_copy_data,
std::deque<rocprofiler_buffer_tracing_marker_api_record_t>* marker_api_data,
std::deque<rocprofiler_buffer_tracing_scratch_memory_record_t>* scratch_api_data);
} // namespace tool
} // namespace rocprofiler
+14
Просмотреть файл
@@ -26,6 +26,7 @@
#include "domain_type.hpp"
#include "generateCSV.hpp"
#include "generateJSON.hpp"
#include "generatePerfetto.hpp"
#include "helper.hpp"
#include "output_file.hpp"
#include "tmp_file.hpp"
@@ -1433,6 +1434,19 @@ tool_fini(void* /*tool_data*/)
&scratch_memory_output.element_data);
}
if(tool::get_config().pftrace_output)
{
rocprofiler::tool::write_perfetto(tool_functions,
getpid(),
_agents,
&hip_output.element_data,
&hsa_output.element_data,
&kernel_dispatch_output.element_data,
&memory_copy_output.element_data,
&marker_output.element_data,
&scratch_memory_output.element_data);
}
auto destroy_output = [](auto& _buffered_output_v) { _buffered_output_v.destroy(); };
destroy_output(kernel_dispatch_output);
+3 -2
Просмотреть файл
@@ -56,7 +56,7 @@ namespace
{
using auto_lock_t = std::unique_lock<std::mutex>;
auto print_lock = std::mutex{};
size_t nqueues = 8;
size_t nqueues = 4;
size_t nthreads = 4;
size_t nitr = 500;
size_t nsync = 10;
@@ -112,7 +112,8 @@ main(int argc, char** argv)
{
auto vector_ops_thread = std::thread{run_vector_ops, nthreads, nqueues};
auto transpose_thread = std::thread{run_transpose, nthreads, nitr, nsync};
std::this_thread::sleep_for(std::chrono::milliseconds{100});
auto transpose_thread = std::thread{run_transpose, nthreads, nitr, nsync};
vector_ops_thread.join();
transpose_thread.join();
+30 -22
Просмотреть файл
@@ -84,32 +84,40 @@ if(NOT TARGET rocprofiler::rocprofiler-cereal)
endif()
endif()
# perfetto
fetchcontent_declare(
perfetto
GIT_REPOSITORY https://android.googlesource.com/platform/external/perfetto
GIT_TAG v42.0
SOURCE_DIR ${PROJECT_BINARY_DIR}/external/perfetto BINARY_DIR
${PROJECT_BINARY_DIR}/external/build/perfetto-build SUBBUILD_DIR
${PROJECT_BINARY_DIR}/external/build/perfetto-subdir)
if(NOT TARGET rocprofiler::rocprofiler-perfetto)
# perfetto
fetchcontent_declare(
perfetto
GIT_REPOSITORY https://android.googlesource.com/platform/external/perfetto
GIT_TAG v44.0
SOURCE_DIR ${PROJECT_BINARY_DIR}/external/perfetto BINARY_DIR
${PROJECT_BINARY_DIR}/external/build/perfetto-build SUBBUILD_DIR
${PROJECT_BINARY_DIR}/external/build/perfetto-subdir)
fetchcontent_getproperties(perfetto)
fetchcontent_getproperties(perfetto)
if(NOT perfetto_POPULATED)
fetchcontent_populate(perfetto)
if(NOT perfetto_POPULATED)
fetchcontent_populate(perfetto)
endif()
add_library(rocprofiler-tests-perfetto STATIC)
add_library(rocprofiler::tests-perfetto ALIAS rocprofiler-tests-perfetto)
target_sources(
rocprofiler-tests-perfetto
PRIVATE ${PROJECT_BINARY_DIR}/external/perfetto/sdk/perfetto.h
${PROJECT_BINARY_DIR}/external/perfetto/sdk/perfetto.cc)
target_include_directories(
rocprofiler-tests-perfetto SYSTEM
INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/external/perfetto/sdk>)
set_target_properties(rocprofiler-tests-perfetto PROPERTIES POSITION_INDEPENDENT_CODE
ON)
else()
add_library(rocprofiler-tests-perfetto INTERFACE)
add_library(rocprofiler::tests-perfetto ALIAS rocprofiler-tests-perfetto)
target_link_libraries(rocprofiler-tests-perfetto
INTERFACE rocprofiler::rocprofiler-perfetto)
endif()
add_library(rocprofiler-tests-perfetto STATIC)
add_library(rocprofiler::tests-perfetto ALIAS rocprofiler-tests-perfetto)
target_sources(
rocprofiler-tests-perfetto
PRIVATE ${PROJECT_BINARY_DIR}/external/perfetto/sdk/perfetto.h
${PROJECT_BINARY_DIR}/external/perfetto/sdk/perfetto.cc)
target_include_directories(
rocprofiler-tests-perfetto SYSTEM
INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/external/perfetto/sdk>)
set_target_properties(rocprofiler-tests-perfetto PROPERTIES POSITION_INDEPENDENT_CODE ON)
# common utilities
cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH COMMON_LIBRARY_INCLUDE_DIR)
+1 -201
Просмотреть файл
@@ -22,204 +22,4 @@
#pragma once
#include <cstddef>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#define ROCPROFILER_DEFINE_NAME_TRAIT(NAME, DESC, ...) \
namespace rocprofiler \
{ \
template <> \
struct perfetto_category<__VA_ARGS__> \
{ \
static constexpr auto value = NAME; \
static constexpr auto description = DESC; \
}; \
}
namespace rocprofiler
{
template <typename Tp>
struct perfetto_category;
namespace trait
{
template <typename... Tp>
using name = perfetto_category<Tp...>;
}
} // namespace rocprofiler
#define ROCPROFILER_DEFINE_NS_API(NS, NAME) \
namespace rocprofiler \
{ \
namespace NS \
{ \
struct NAME; \
} \
}
#define ROCPROFILER_DEFINE_CATEGORY(NS, VALUE, DESC) \
ROCPROFILER_DEFINE_NS_API(NS, VALUE) \
ROCPROFILER_DEFINE_NAME_TRAIT(#VALUE, DESC, NS::VALUE)
ROCPROFILER_DEFINE_CATEGORY(category, hsa_api, "HSA API function")
ROCPROFILER_DEFINE_CATEGORY(category, hip_api, "HIP API function")
ROCPROFILER_DEFINE_CATEGORY(category, marker_api, "Marker API region")
ROCPROFILER_DEFINE_CATEGORY(category, kernel_dispatch, "GPU kernel dispatch")
ROCPROFILER_DEFINE_CATEGORY(category, memory_copy, "Async memory copy")
#define ROCPROFILER_PERFETTO_CATEGORY(TYPE) \
::perfetto::Category(::rocprofiler::perfetto_category<::rocprofiler::TYPE>::value) \
.SetDescription(::rocprofiler::perfetto_category<::rocprofiler::TYPE>::description)
#define ROCPROFILER_PERFETTO_CATEGORIES \
ROCPROFILER_PERFETTO_CATEGORY(category::hsa_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::hip_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::marker_api), \
ROCPROFILER_PERFETTO_CATEGORY(category::kernel_dispatch), \
ROCPROFILER_PERFETTO_CATEGORY(category::memory_copy)
#include <perfetto.h>
PERFETTO_DEFINE_CATEGORIES(ROCPROFILER_PERFETTO_CATEGORIES);
namespace concepts
{
template <typename Tp>
struct is_string_type : std::false_type
{};
template <>
struct is_string_type<std::string> : std::true_type
{};
template <>
struct is_string_type<char*> : std::true_type
{};
template <>
struct is_string_type<const char*> : std::true_type
{};
template <>
struct is_string_type<std::string_view> : std::true_type
{};
template <typename Tp>
struct is_string_type<const Tp> : is_string_type<std::decay_t<Tp>>
{};
template <typename Tp>
struct is_string_type<Tp&> : is_string_type<std::decay_t<Tp>>
{};
template <typename Tp>
struct is_string_type<volatile Tp> : is_string_type<std::decay_t<Tp>>
{};
template <typename Tp, size_t N>
struct is_string_type<Tp[N]> : is_string_type<std::decay_t<Tp[N]>>
{};
template <typename Tp, size_t N>
struct is_string_type<const Tp[N]> : is_string_type<std::decay_t<Tp[N]>>
{};
template <typename Tp, size_t N>
struct is_string_type<volatile Tp[N]> : is_string_type<std::decay_t<Tp[N]>>
{};
template <typename Tp>
struct unqualified_type
{
using type = std::remove_reference_t<std::remove_cv_t<std::decay_t<Tp>>>;
};
template <typename Tp>
using unqualified_type_t = typename unqualified_type<Tp>::type;
template <typename Tp>
struct can_stringify
{
private:
static constexpr auto sfinae(int)
-> decltype(std::declval<std::ostream&>() << std::declval<Tp>(), bool())
{
return true;
}
static constexpr auto sfinae(long) { return false; }
public:
static constexpr bool value = sfinae(0);
constexpr auto operator()() const { return sfinae(0); }
};
} // namespace concepts
using perfetto_event_context_t = ::perfetto::EventContext;
template <typename Np, typename Tp>
auto
add_perfetto_annotation(perfetto_event_context_t& ctx, Np&& _name, Tp&& _val)
{
using named_type = concepts::unqualified_type_t<Np>;
using value_type = concepts::unqualified_type_t<Tp>;
static_assert(concepts::is_string_type<named_type>::value, "Error! name is not a string type");
auto _get_dbg = [&]() {
auto* _dbg = ctx.event()->add_debug_annotations();
_dbg->set_name(std::string_view{std::forward<Np>(_name)}.data());
return _dbg;
};
if constexpr(std::is_same<value_type, std::string_view>::value)
{
_get_dbg()->set_string_value(_val.data());
}
else if constexpr(concepts::is_string_type<value_type>::value)
{
_get_dbg()->set_string_value(std::forward<Tp>(_val));
}
else if constexpr(std::is_same<value_type, bool>::value)
{
_get_dbg()->set_bool_value(_val);
}
else if constexpr(std::is_enum<value_type>::value)
{
_get_dbg()->set_int_value(static_cast<int64_t>(_val));
}
else if constexpr(std::is_floating_point<value_type>::value)
{
_get_dbg()->set_double_value(static_cast<double>(_val));
}
else if constexpr(std::is_integral<value_type>::value)
{
if constexpr(std::is_unsigned<value_type>::value)
{
_get_dbg()->set_uint_value(_val);
}
else
{
_get_dbg()->set_int_value(_val);
}
}
else if constexpr(std::is_pointer<value_type>::value)
{
_get_dbg()->set_pointer_value(reinterpret_cast<uint64_t>(_val));
}
else if constexpr(concepts::can_stringify<value_type>::value)
{
auto _ss = std::stringstream{};
_ss << std::forward<Tp>(_val);
_get_dbg()->set_string_value(_ss.str());
}
else
{
static_assert(std::is_empty<value_type>::value, "Error! unsupported data type");
}
}
#include <rocprofiler-sdk/cxx/perfetto.hpp>
+8 -1
Просмотреть файл
@@ -4,13 +4,20 @@
set(PACKAGE_OUTPUT_DIR
${ROCPROFILER_SDK_TESTS_BINARY_DIR}/pytest-packages/rocprofiler_sdk)
set(PYTEST_UTILS_PYTHON_SOURCES __init__.py dotdict.py)
set(PYTEST_UTILS_PYTHON_SOURCES __init__.py dotdict.py perfetto_reader.py)
set(TESTS_PYTHON_SOURCES __init__.py rocprofv3.py)
foreach(_FILE ${PYTEST_UTILS_PYTHON_SOURCES})
configure_file(${CMAKE_CURRENT_LIST_DIR}/pytest_utils/${_FILE}
${PACKAGE_OUTPUT_DIR}/pytest_utils/${_FILE} COPYONLY)
endforeach()
foreach(_FILE ${TESTS_PYTHON_SOURCES})
configure_file(${CMAKE_CURRENT_LIST_DIR}/tests/${_FILE}
${PACKAGE_OUTPUT_DIR}/tests/${_FILE} COPYONLY)
endforeach()
file(
WRITE "${PACKAGE_OUTPUT_DIR}/__init__.py"
"#
+750
Просмотреть файл
@@ -0,0 +1,750 @@
# 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.
from __future__ import absolute_import
import re
import pandas as pd
from collections import OrderedDict
from perfetto.trace_processor import TraceProcessor
class PerfettoReader:
"""Read in perfetto protobuf output"""
def __init__(self, filename, select=None, **kwargs):
"""Arguments:
filename (str or list/tuple of str):
Valid arguments should be a list of files
verbose (int):
Information about processes, threads, categories, performance, etc.
report (list of str):
alternative for verbose. Accepts:
- category (report categories in files)
- process (report process info in files)
- threads (report thread info in files)
- track_ids (report track id info in files)
- profile (report timing info from processing)
exclude_category (list of str):
Every slice has an associated category, e.g., slices from sampling may be
in "sampling" category, slices from instrumentation may be in "instrumentation"
category. The categories in protobuf is specific to the tool that generated the
protobuf. Use this option to exclude the slices from specific categories
include_category (list of str):
Every slice has an associated category, e.g., slices from sampling may be
in "sampling" category, slices from instrumentation may be in "instrumentation"
category. The categories in protobuf is specific to the tool that generated the
protobuf. Use this option to restrict the slices to the specified categories
default_categories (list of str):
Use this option as a safety value when include/exclude are used. These categories
are used when include/exclude category filtering resulted in no data in the data
frame. Accepts "all" if you want to fallback to just including all categories.
patterns (list of regex str):
Use this option to specify how the function/file/line are extracted from labels.
For example, the default patterns are:
r"(?P<func>.*) \\[(?P<file>\\S+):(?P<line>[0-9]+)\\]$"
r"(?P<func>.*) \\[(?P<file>\\S+)\\]$"
r"^(?P<file>\\S+):(?P<line>[0-9]+)$"
To extract the function/file/line from the labeling patterns:
func [file:line]
func [file]
file:line
respectively.
thread_index_regex (regex str):
Use this option to determine thread IDs from the name of the threads. For example,
Omnitrace labels certain process tracks "Thread X (S)" to indicate that the given
process track are samples of Thread X. The default pattern is:
r"(T|t)hread (?P<thread_index>[0-9]+)( |$)"
to extract the "thread_index" field.
max_depth (int):
Set this a positive non-zero number signifying the maximum call-stack depth to
process. This can significantly reduce the processing time for large traces.
"""
self.filename = filename if isinstance(filename, (list, tuple)) else [filename]
self.metadata = {"hatchet_inclusive_suffix": ".inc"}
self.default_metric = "time{}".format(self.metadata["hatchet_inclusive_suffix"])
self.verbose = 0
self.report = []
self.exclude = []
self.include = []
self.categories = []
self.default_categories = []
self.df_categories = []
self.dataframe = pd.DataFrame()
self.process = pd.DataFrame()
self.threads = pd.DataFrame()
self.track_ids = []
self.trace_processor = []
self.compiled_patterns = []
self.thread_index_regex = None
self.max_depth = None
self.configure(**kwargs)
def configure(self, **kwargs):
# pre-compile the regex patterns for extracting the func, file, and line info
# users can use their own pattens via patterns=[...]. An empty set of patterns
# is valid for avoiding parsing func/file/line info
_default_patterns = [
# func [file:line]
r"(?P<func>.*) \[(?P<file>\S+):(?P<line>[0-9]+)\]$",
# func [file]
r"(?P<func>.*) \[(?P<file>\S+)\]$",
# file:line
r"^(?P<file>\S+):(?P<line>[0-9]+)$",
]
_patterns = kwargs["patterns"] if "patterns" in kwargs else None
if _patterns is None:
_patterns = _default_patterns
elif kwargs.get("use_default_patterns", True):
_patterns += _default_patterns
self.compiled_patterns = [re.compile(x) for x in _patterns]
self.thread_index_regex = re.compile(
kwargs.get("thread_index_regex", "(T|t)hread (?P<thread_index>[0-9]+)( |$)")
)
def report_at_verbosity(key, lvl):
if self.verbose >= lvl and key not in self.report:
self.report.append(key)
self.verbose = self.verbose if "verbose" not in kwargs else kwargs["verbose"]
self.report = kwargs["report"] if "report" in kwargs else self.report
report_at_verbosity("category", 1)
report_at_verbosity("process", 2)
report_at_verbosity("threads", 2)
report_at_verbosity("track_ids", 2)
_filenames = sorted(self.filename)
self.filename = kwargs.get("filename", sorted(self.filename))
_new_filenames = [x for x in self.filename if x not in _filenames]
if len(self.filename) + len(_new_filenames) != len(self.trace_processor):
self.trace_processor = [TraceProcessor(trace=(f)) for f in self.filename]
elif _new_filenames:
self.trace_processor += [TraceProcessor(trace=(f)) for f in _new_filenames]
self.max_depth = kwargs.get("max_depth", None)
def query_tp(self, query, index_name=lambda x: "tp_index"):
"""Simplifies querying the trace processor and always adds a
"tp_index" column for referencing which trace_processor
generated the data
"""
def _append_column(df, name, idx):
"""Used to add a tp_index column to our data which is used to identify
which trace-processor the query results came from"""
if name and name not in df:
df.insert(0, name, idx)
return df
def _get_dataframe(tp):
"""workaround for bug in TraceProcessor.QueryResultIterator.as_pandas_dataframe()"""
query_itr = tp.query(f"{query}")
# the perfetto trace processor query function looks like this:
#
# def query(self, sql: str):
# response = self.http.execute_query(sql)
# if response.error:
# raise TraceProcessorException(response.error)
# return TraceProcessor.QueryResultIterator(response.column_names,
# response.batch)
#
# unfortunately, data type of response.column_names is RepeatedScalarContainer
# and in a lot of versions of pandas, this type does not satisfy any of it's
# checks for whether this is a valid Index-type for the columns:
# isinstance(..., Index)
# isinstance(..., ABCSeries)
# is_iterator(...)
# isinstance(..., list)
#
# and thus as_pandas_dataframe() raises a TypeError exception. Queries can
# be VERY expensive for a large database (>> 10s of seconds) so instead of
# try -> except -> re-query (necessary) -> convert to list -> re-call
# as_pandas_dataframe() like so:
#
# try:
# return query_itr.as_pandas_dataframe()
# except TypeError:
# query_itr = tp.query(...)
# query_itr.__dict__["..."] = list(query_itr.__dict__["..."])
# return query_itr.as_pandas_dataframe()
#
# which would effectively result in two queries every single time, we just
# do it upfront if the dict entry that is known to cause the problem exists
# and is of the type that we know causes problems
#
_buggy_dict_entry = "_QueryResultIterator__column_names"
if (
_buggy_dict_entry in query_itr.__dict__
and type(query_itr.__dict__[_buggy_dict_entry]).__name__
== "RepeatedScalarContainer"
):
query_itr.__dict__[_buggy_dict_entry] = list(
query_itr.__dict__[_buggy_dict_entry]
)
return query_itr.as_pandas_dataframe()
return pd.concat(
[
_append_column(
_get_dataframe(tp),
index_name(idx),
idx,
)
for idx, tp in enumerate(self.trace_processor)
]
)
def extract_tp_data(self, **kwargs):
"""Extracts all the necessary data from the trace processor"""
self.configure(**kwargs)
self.dataframe = self.query_tp(
"SELECT slice_id, track_id, category, depth, stack_id, parent_stack_id, ts, dur, name FROM slice"
)
self.df_categories = sorted(list(self.dataframe["category"].unique()))
# check for update to include/exclude category
self.exclude = kwargs.get("exclude_category", self.exclude)
self.include = kwargs.get("include_category", self.include)
self.categories = self.df_categories[:]
# apply include first
if self.include:
self.categories = [x for x in self.categories if x in self.include]
# apply exclude after
if self.exclude:
self.categories = [x for x in self.categories if x not in self.exclude]
self.default_categories = kwargs.get(
"default_categories", self.default_categories
)
_acceptable_default_categories = (
'default_categories can be set to: "all", ["all"], or [list of categories...]'
)
if not self.categories and self.default_categories:
if not isinstance(self.default_categories, (tuple, list)):
self.default_categories = [self.default_categories]
if "all" in self.default_categories:
self.categories = self.df_categories[:]
else:
raise ValueError(
f"invalid default_categories value: {self.default_categories}. {_acceptable_default_categories}"
)
# filter out any categories that do not exist
self.categories = sorted([x for x in self.categories if x in self.df_categories])
if not self.categories:
raise ValueError(
f"The application of include_category={self.include} followed by exclude_category={self.exclude} rendered an empty set of categories (available={self.df_categories}). Either clear one of the configs or assign the default_categories. {_acceptable_default_categories}"
)
if "category" in self.report:
_ignore = [x for x in self.df_categories if x not in self.categories]
print(
"categories: {}{}".format(
", ".join(self.categories),
" (ignored: {})".format(", ".join(_ignore)) if _ignore else "",
)
)
# reduce the dataframe to given specified category data
# TODO: adjust the parent stack ids. if <user> category entry is child of <host> category entry, we lose <user> category entry
self.dataframe = self.dataframe[self.dataframe["category"].isin(self.categories)]
if self.dataframe.empty:
raise RuntimeError(
"category filtering resulted in an empty dataframe. categories: include={}, exclude={}, available={}".format(
self.include, self.exclude, self.df_categories
)
)
self.process = self.query_tp(
"SELECT process.upid AS process_upid, process.id AS process_id, process.pid, process.name AS process_name, process_track.upid as track_upid, process_track.id AS track_id, process_track.parent_id as track_parent_id, process_track.name AS track_name from process JOIN process_track ON process_track.upid = process.upid WHERE process.pid > 0"
)
self.threads = self.query_tp(
"SELECT thread.utid AS thread_utid, thread.id AS thread_id, thread.tid, thread.name as thread_name, thread.is_main_thread, thread_track.id AS track_id, thread_track.parent_id AS track_parent_id, thread_track.name AS track_name from thread JOIN thread_track ON thread_track.utid = thread.utid"
)
# generate empty dictionaries for each trace processor
self.track_ids = [{} for _ in range(len(self.trace_processor))]
# generate mapping from track IDs to process and thread info.
# the "pid" and "tid" fields are the system value. we want to
# assign a "rank" and "thread" value for "pid" and "tid",
# respectively which start at zero and monotonically increase
for thread in self.threads.itertuples():
_thread_name = (
thread.thread_name if thread.track_name is None else thread.track_name
)
for process in self.process.itertuples():
if process.tp_index != thread.tp_index:
continue
_process_name = (
process.process_name
if process.track_name is None
else process.track_name
)
if process.track_id == thread.track_parent_id:
self.track_ids[thread.tp_index][thread.track_id] = {
"tp_index": thread.tp_index,
"pid": process.pid,
"tid": thread.tid,
"rank": -1,
"thread": -1,
"prio": 0 if thread.is_main_thread else 1,
"process_name": _process_name,
"thread_name": _thread_name,
}
break
# some track ids do not have an associated system thread id so handle them here.
# for example, omnitrace post-processes sampling data collected on a thread
# during finalization and is inserted into perfetto on the main thread
# but not in the main thread track so perfetto does not associate the
# track_id with a system thread id.
for process in self.process.itertuples():
if process.track_id in self.track_ids[process.tp_index].keys():
continue
_process_name = (
process.track_name
if process.process_name is None
else process.process_name
)
_thread_name = (
process.process_name if process.track_name is None else process.track_name
)
self.track_ids[process.tp_index][process.track_id] = {
"tp_index": process.tp_index,
"pid": process.pid,
"tid": process.pid,
"rank": -1,
"thread": -1,
"prio": 0 if process.track_parent_id is None else 2,
"process_name": _process_name,
"thread_name": _thread_name,
}
if "track_ids" in self.report and self.verbose >= 3:
print("\ntrack ids (original):")
for idx, _track_ids in enumerate(self.track_ids):
for key, itr in _track_ids.items():
print(f" {idx:2}: {key:8} :: {itr}")
print("")
# since the protobuf just has raw (system) PID and TIDs and there may be multiple PIDs and TIDs
# in the same file, we need to map the system PIDs to rank IDs starting at zero and, for each
# PID, map the system TIDs to thread-ids starting at zero
pid_offset = 0
for idx, _track_ids in enumerate(self.track_ids):
_track_ids = dict(
sorted(
_track_ids.items(),
key=lambda x: [x[1]["pid"], x[1]["prio"], x[1]["tid"]],
)
)
pids = list(set([x["pid"] for _, x in _track_ids.items()]))
if self.verbose >= 3:
tids = list(set([x["tid"] for _, x in _track_ids.items()]))
print(f"pids: {pids}")
print(f"tids: {tids}")
# assign the rank and then increment the rank offset by the number of PIDs in the file
for pidx, pid in enumerate(pids):
for _, itr in _track_ids.items():
if itr["pid"] == pid:
itr["rank"] = pidx + pid_offset
pid_offset += len(pids)
for _, pid in enumerate(pids):
# dictionary containing only the data for this pid
_pid_track_ids = dict(
[[x, y] for x, y in _track_ids.items() if y["pid"] == pid]
)
assigned_track_ids = []
# filter out the main threads (priority == 0) for a given pid and set index to a value of zero
main_thr_info = set(
[x for x, y in _pid_track_ids.items() if y["prio"] == 0]
)
# for known "main" threads, assign index to zero
for track_id, track_id_data in _track_ids.items():
if track_id in main_thr_info:
track_id_data["thread"] = 0
assigned_track_ids.append(track_id)
# starting value for assignment. set before next step
offset = 1 if assigned_track_ids else 0
# search thread name to try to identify which thread it belongs to.
# needs to come after offset assignment
for track_id, track_id_data in _track_ids.items():
if (
track_id in assigned_track_ids
or track_id not in _pid_track_ids.keys()
):
continue
m = re.search(self.thread_index_regex, track_id_data["thread_name"])
if m:
track_id_data["thread"] = int(m.group("thread_index"))
assigned_track_ids.append(track_id)
# filter out the non-main threads (priority > 0) for a given pid that haven't already been assigned an index
chld_thr_info = set(
[
x
for x, y in _pid_track_ids.items()
if y["prio"] > 0 and x not in assigned_track_ids
]
)
# finally, assign remaining tracks thread indexes via incrementing offset value
for track_id, track_id_data in _track_ids.items():
if (
track_id in assigned_track_ids
or track_id not in _pid_track_ids.keys()
):
continue
if track_id in chld_thr_info:
track_id_data["thread"] = offset
assigned_track_ids.append(track_id)
offset += 1
# make sure the thread indexes are monotonically increasing
# this may not be the case because of the assignment via regex matching
_pid_track_ids = dict(
[[x, y] for x, y in _track_ids.items() if y["pid"] == pid]
)
tidx_max = max([y["thread"] for x, y in _pid_track_ids.items()])
tidx_uniq = len(
set(
[
y["thread"]
for x, y in _pid_track_ids.items()
if y["thread"] >= 0
]
)
)
if self.verbose >= 3:
print(f"\nTID :: max={tidx_max}, unique={tidx_uniq}\n")
# add one to comparison since one thread with a value of 0 would be a size of 1
while tidx_max + 1 > tidx_uniq:
for idx in range(tidx_max):
# if this is empty, we need to decrement all thread indexes > idx
_tidx_loc = [
x for x, y in _pid_track_ids.items() if y["thread"] == idx
]
if not _tidx_loc:
for itr in _pid_track_ids.keys():
if _track_ids[itr]["thread"] > idx:
_track_ids[itr]["thread"] -= 1
break
# exit the loop so that we recalculate tidx_max
tidx_max = max([y["thread"] for x, y in _pid_track_ids.items()])
if "process" in self.report:
print(f"\nprocess:\n{self.process.to_string()}\n")
if "threads" in self.report:
print(f"\nthreads:\n{self.threads.to_string()}\n")
if "track_ids" in self.report:
print("\ntrack ids:")
for idx, _track_ids in enumerate(self.track_ids):
for key, itr in _track_ids.items():
print(f" {idx:2}: {key:8} :: {itr}")
print("")
if self.verbose >= 3:
print("\nTID mapping:")
for idx, _track_ids in enumerate(self.track_ids):
for track_id, itr in _track_ids.items():
pid = itr["pid"]
tid = itr["tid"]
pidx = itr["rank"]
tidx = itr["thread"]
print(
f" {idx:2}: [{track_id:4}] {pid:8} -> {pidx:8} :: {tid:8} -> {tidx:8}"
)
print("")
def create_graph(self):
"""Create graph and dataframe"""
def get_frame_attributes(_name):
"""Get the standard set of dictionary entries for a Frame.
Also, parses the prefix for func-file-line info
which is typically in the form:
<FUNC> [<FILE>:<LINE>]
<FUNC> [<FILE>]
"""
if not self.compiled_patterns:
return {"type": "function", "name": _name}
def _process_regex(_data):
"""Process the regex data for func/file/line info"""
return _data.groupdict() if _data is not None else None
def _perform_regex(_name):
"""Performs a search for standard configurations of function + file + line"""
for _pattern in self.compiled_patterns:
_tmp = _process_regex(re.search(_pattern, _name))
if _tmp:
return _tmp
return None
_keys = {"type": "region", "name": _name}
_extra = {"file": "<unknown>", "line": "0"}
_pdict = _perform_regex(_name)
if _pdict is not None:
_func = _pdict.get("func", None)
_file = _pdict.get("file", "<unknown>")
_line = _pdict.get("line", "0")
_head = _pdict.get("head", None)
_tail = _pdict.get("tail", None)
_line_s = f":{_line}" if int(_line) > 0 else ""
_tail_s = f"/{_tail}" if _tail is not None else ""
_file_s = f"{_file}{_line_s}" if _file != "<unknown>" else _file
_extra["file"] = _file_s
_extra["line"] = _line
if "head" in _pdict:
_keys["name"] = _head.rstrip()
if _func is not None:
_extra["func"] = _func
else:
if _func is not None:
_keys["name"] = _func
else:
_keys["name"] = _file_s
_keys["name"] = "{}{}".format(_keys["name"], _tail_s)
return (_keys, _extra)
# list_roots = []
track_id_dict = OrderedDict()
callpath_to_node = {}
def_metric = self.default_metric
df = self.dataframe
_cols = [
"tp_index",
"track_id",
"category",
"slice_id",
"stack_id",
"parent_stack_id",
"name",
"depth",
"ts",
"dur",
]
_data = [df[x].to_list() for x in _cols]
assert min([len(x) for x in _data]) == max([len(x) for x in _data])
for _tp_index, _track_id in zip(
_data[_cols.index("tp_index")], _data[_cols.index("track_id")]
):
assert _tp_index < len(self.track_ids)
assert _track_id in self.track_ids[_tp_index].keys()
_track_info = self.track_ids[_tp_index][_track_id]
_rank = _track_info["rank"]
_thread = _track_info["thread"]
if _tp_index not in track_id_dict:
track_id_dict[_tp_index] = OrderedDict()
if _rank not in track_id_dict[_tp_index]:
track_id_dict[_tp_index][_rank] = OrderedDict()
if _thread not in track_id_dict[_tp_index][_rank]:
track_id_dict[_tp_index][_rank][_thread] = OrderedDict()
track_id_dict[_tp_index][_rank][_thread] = {0: None}
for (
_tp_index,
_track_id,
_category,
_slice_id,
_stack_id,
_parent_stack_id,
_name,
_depth,
_ts,
_dur,
) in zip(*_data):
_track_info = self.track_ids[_tp_index][_track_id]
_rank = _track_info["rank"]
_thread = _track_info["thread"]
_track_id_dict = track_id_dict[_tp_index][_rank][_thread]
# removed because of filtering
if _parent_stack_id not in _track_id_dict:
continue
# reduce processing time
if self.max_depth is not None and _depth > self.max_depth:
continue
_metrics = {}
_metrics["rank"] = _track_info["rank"]
_metrics["thread"] = _track_info["thread"]
_metrics["pid"] = _track_info["pid"]
_metrics["tid"] = _track_info["tid"]
_metrics["track_id"] = _track_id
_metrics["slice_id"] = _slice_id
_metrics["stack_id"] = _stack_id
_metrics["parent_stack_id"] = _parent_stack_id
_metrics["ts"] = _ts
_metrics[def_metric] = float(_dur) * 1.0e-9 # nsec -> sec
_frame_attrs, _extra = get_frame_attributes(_name)
_extra["tp_index"] = _tp_index
_extra["category"] = _category
_extra["depth"] = _depth
# look up the parent node specific to the TP index, rank, and thread
# stack ID is assigned by perfetto and parent stack ID is the
# stack ID of it's parent.
# _parent_node = _track_id_dict[_parent_stack_id]
# hnode = Node(Frame(_frame_attrs, **_extra), None)
# if _parent_node:
# _parent_node.add_child(hnode)
# else:
# list_roots.append(hnode)
# # make sure this stack ID is unique for the
# # TP index, rank, and thread and is equal to a
# # previously seen node with the same stack ID
# if _stack_id not in _track_id_dict:
# _track_id_dict[_stack_id] = hnode
# elif _track_id_dict[_stack_id] != hnode:
# _existing = _track_id_dict[_stack_id]
# raise RuntimeError(
# f"{_stack_id} already exists in track_id_dict[{_tp_index}][{_rank}][{_thread}]. failed to set:\n {hnode.frame} (current)\n {_existing.frame} (existing)"
# )
_hash = hash((_tp_index, _slice_id))
if _hash in callpath_to_node:
raise ValueError(f"{_hash} already exists in callpath_to_node dict")
_frame_attrs.pop("type") # should not be a column in dataframe
callpath_to_node[_hash] = dict(
**_frame_attrs,
**_metrics,
**_extra,
)
if not callpath_to_node:
raise RuntimeError(
"call-graph is empty. if category filtering was used, you may have filtered out all the root nodes and thus all of it's children"
)
return (df, callpath_to_node)
# graph, dataframe = graphframe_indexing_helper(
# list_roots,
# data=list(callpath_to_node.values()),
# extensions=["rank", "thread"],
# fill_value=0,
# )
# inc_metrics = [self.default_metric]
# exc_metrics = []
# return (graph, dataframe, exc_metrics, inc_metrics, self.default_metric)
def read(self, **kwargs):
"""Read perfetto json."""
self.configure(**kwargs)
self.extract_tp_data(**kwargs)
(
dataframe,
callpath_to_node,
) = self.create_graph()
# def _read(**kwargs):
# return self.read(**kwargs)
# def _configure(**kwargs):
# self.configure(**kwargs)
# def _query(*args, **kwargs):
# assert self.trace_processor
# return self.query_tp(*args, **kwargs)
return (
dataframe,
callpath_to_node,
)
# default_metric=def_metric,
# metadata=self.metadata,
# attributes={
# "reader": self,
# "read": _read,
# "query": _query,
# "configure": _configure,
# "selected_categories": lambda: self.categories,
# "available_categories": lambda: self.df_categories,
# },
# )
+23
Просмотреть файл
@@ -0,0 +1,23 @@
# 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.
from __future__ import absolute_import
+48
Просмотреть файл
@@ -0,0 +1,48 @@
# 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.
from __future__ import absolute_import
def test_perfetto_data(
pftrace_data, json_data, categories=("hip", "hsa", "marker", "kernel", "memory_copy")
):
mapping = {
"hip": ("hip_api", "hip_api"),
"hsa": ("hsa_api", "hsa_api"),
"marker": ("marker_api", "marker_api"),
"kernel": ("kernel_dispatch", "kernel_dispatches"),
"memory_copy": ("memory_copy", "memory_copy"),
}
# make sure they specified valid categories
for itr in categories:
assert itr in mapping.keys()
for pf_category, js_category in [
itr for key, itr in mapping.items() if key in categories
]:
_pf_data = pftrace_data.loc[pftrace_data["category"] == pf_category]
_js_data = json_data["rocprofiler-sdk-tool"]["buffer_records"][js_category]
assert len(_pf_data) == len(_js_data)
+4 -2
Просмотреть файл
@@ -22,7 +22,7 @@ add_test(
NAME rocprofv3-test-hsa-multiqueue-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --hsa-trace --kernel-trace -d
${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace -o out --output-format json:csv
${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace -o out --output-format json:csv:pftrace
$<TARGET_FILE:multiqueue_testapp>)
set_tests_properties(
@@ -38,7 +38,9 @@ add_test(
--kernel-trace-input
${CMAKE_CURRENT_BINARY_DIR}/multiqueue_testapp-trace/out_kernel_trace.csv
--json-input
${CMAKE_CURRENT_BINARY_DIR}/multiqueue_testapp-trace/out_results.json)
${CMAKE_CURRENT_BINARY_DIR}/multiqueue_testapp-trace/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/multiqueue_testapp-trace/out_results.pftrace)
set(MULTIQUEUE_VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/multiqueue_testapp-trace/out_hsa_api_trace.csv
+12
Просмотреть файл
@@ -6,6 +6,7 @@ import json
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
def pytest_addoption(parser):
@@ -24,6 +25,11 @@ def pytest_addoption(parser):
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
@pytest.fixture
@@ -55,3 +61,9 @@ 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]
+8
Просмотреть файл
@@ -154,6 +154,14 @@ def test_hsa_api_trace_json(json_data):
assert functions.count("hsa_signal_destroy") == num_hsa_signal_destroy_calls
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "hsa", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+29 -4
Просмотреть файл
@@ -27,6 +27,15 @@ add_test(
--hsa-finalizer-trace --kernel-trace --memory-copy-trace --output-format JSON -d
${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace -o out $<TARGET_FILE:hip-in-libraries>)
add_test(
NAME rocprofv3-test-trace-hip-in-libraries-pftrace-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --hip-runtime-trace
--hip-compiler-trace --hsa-core-trace --hsa-amd-trace --hsa-image-trace
--hsa-finalizer-trace --kernel-trace --memory-copy-trace --output-format pftrace
-d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace -o out
$<TARGET_FILE:hip-in-libraries>)
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
@@ -58,6 +67,19 @@ set_tests_properties(
"HSA_CORE_API|HSA_AMD_EXT_API|HSA_IMAGE_EXT_API|HSA_FINALIZER_EXT_API|HIP_API|HIP_COMPILER_API|KERNEL_DISPATCH|CODE_OBJECT"
)
set_tests_properties(
rocprofv3-test-trace-hip-in-libraries-pftrace-execute
PROPERTIES
TIMEOUT
100
LABELS
"integration-tests"
ENVIRONMENT
"${tracing-env}"
FAIL_REGULAR_EXPRESSION
"HSA_CORE_API|HSA_AMD_EXT_API|HSA_IMAGE_EXT_API|HSA_FINALIZER_EXT_API|HIP_API|HIP_COMPILER_API|KERNEL_DISPATCH|CODE_OBJECT"
)
rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY validate.py conftest.py)
add_test(
@@ -79,9 +101,13 @@ add_test(
--hsa-stats ${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_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)
--json-input ${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.pftrace)
set(VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.pftrace
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.json
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_memory_copy_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hsa_api_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_hip_api_trace.csv
@@ -90,8 +116,7 @@ set(VALIDATION_FILES
${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_memory_copy_stats.csv
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_results.json)
${CMAKE_CURRENT_BINARY_DIR}/hip-in-libraries-trace/out_memory_copy_stats.csv)
set_tests_properties(
rocprofv3-test-trace-hip-in-libraries-validate
@@ -101,7 +126,7 @@ set_tests_properties(
LABELS
"integration-tests"
DEPENDS
"rocprofv3-test-trace-hip-in-libraries-execute;rocprofv3-test-trace-hip-in-libraries-json-execute"
"rocprofv3-test-trace-hip-in-libraries-execute;rocprofv3-test-trace-hip-in-libraries-json-execute;rocprofv3-test-trace-hip-in-libraries-pftrace-execute"
FAIL_REGULAR_EXPRESSION
"AssertionError"
ATTACHED_FILES_ON_FAIL
+12
Просмотреть файл
@@ -7,6 +7,7 @@ import json
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
def pytest_addoption(parser):
@@ -66,6 +67,11 @@ def pytest_addoption(parser):
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
@pytest.fixture
@@ -198,3 +204,9 @@ 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]
+9
Просмотреть файл
@@ -398,6 +398,15 @@ def test_memory_copy_trace(
validate_stats(row)
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
# do not test for HSA since that may vary b/t two separate runs
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+3 -1
Просмотреть файл
@@ -20,7 +20,7 @@ add_test(
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --hsa-trace -i
${CMAKE_CURRENT_BINARY_DIR}/input.txt -d ${CMAKE_CURRENT_BINARY_DIR}/out_cc_trace
-o pmc3 --output-format JSON:CSV $<TARGET_FILE:simple-transpose>)
-o pmc3 --output-format JSON:PFTRACE:CSV $<TARGET_FILE:simple-transpose>)
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
@@ -39,6 +39,8 @@ foreach(_DIR "pmc_1" "pmc_2" "pmc_3" "pmc_4")
COMMAND
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input
"${CMAKE_CURRENT_BINARY_DIR}/out_cc_trace/${_DIR}/pmc3_results.json"
--pftrace-input
"${CMAKE_CURRENT_BINARY_DIR}/out_cc_trace/${_DIR}/pmc3_results.pftrace"
--hsa-input
"${CMAKE_CURRENT_BINARY_DIR}/out_cc_trace/${_DIR}/pmc3_hsa_api_trace.csv"
--agent-input
+12
Просмотреть файл
@@ -8,6 +8,7 @@ import pandas as pd
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
def pytest_addoption(parser):
@@ -46,6 +47,11 @@ def pytest_addoption(parser):
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
@pytest.fixture
@@ -184,3 +190,9 @@ 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]
+8
Просмотреть файл
@@ -20,6 +20,14 @@ def test_validate_counter_collection_plus_tracing(
assert len(counter_collection_data) > 0
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "hsa", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+15 -9
Просмотреть файл
@@ -15,7 +15,7 @@ add_test(
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> -M --hsa-trace --kernel-trace
--memory-copy-trace --marker-trace -d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace -o
out --output-format csv,json $<TARGET_FILE:simple-transpose>)
out --output-format pftrace,csv,json $<TARGET_FILE:simple-transpose>)
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
@@ -57,15 +57,18 @@ add_test(
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_marker_api_trace.csv
--agent-input
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_agent_info.csv
--json-input ${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.json)
--json-input ${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.pftrace)
set(VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.pftrace
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.json
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_memory_copy_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_hsa_api_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_kernel_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_marker_api_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_agent_info.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_results.json)
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-trace/out_agent_info.csv)
set_tests_properties(
rocprofv3-test-trace-validate
@@ -87,8 +90,8 @@ add_test(
NAME rocprofv3-test-systrace-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --sys-trace -d
${CMAKE_CURRENT_BINARY_DIR}/%argt%-systrace -o out --output-format csv,json
$<TARGET_FILE:simple-transpose>)
${CMAKE_CURRENT_BINARY_DIR}/%argt%-systrace -o out --output-format
pftrace,csv,json $<TARGET_FILE:simple-transpose>)
set_tests_properties(
rocprofv3-test-systrace-execute
@@ -118,15 +121,18 @@ add_test(
--agent-input
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_agent_info.csv
--json-input
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.json)
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.pftrace)
set(SYS_VALIDATION_FILES
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.pftrace
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.json
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_memory_copy_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_hsa_api_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_kernel_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_marker_api_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_agent_info.csv
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_results.json)
${CMAKE_CURRENT_BINARY_DIR}/simple-transpose-systrace/out_agent_info.csv)
set_tests_properties(
rocprofv3-test-systrace-validate
+12
Просмотреть файл
@@ -6,6 +6,7 @@ import json
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
def pytest_addoption(parser):
@@ -44,6 +45,11 @@ def pytest_addoption(parser):
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
@pytest.fixture
@@ -123,3 +129,9 @@ 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]
+8
Просмотреть файл
@@ -263,6 +263,14 @@ def test_marker_api_trace_json(json_data):
assert marker["end_timestamp"] >= marker["start_timestamp"]
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "hsa", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+41 -39
Просмотреть файл
@@ -1436,14 +1436,15 @@ write_json(call_stack_t* _call_stack)
}
{
namespace sdk = ::rocprofiler::sdk;
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 json_ar = JSONOutputArchive{*ofs, json_opts};
auto buffer_names = rocprofiler::sdk::get_buffer_tracing_names();
auto callbk_names = rocprofiler::sdk::get_callback_tracing_names();
auto buffer_names = sdk::get_buffer_tracing_names();
auto callbk_names = sdk::get_callback_tracing_names();
auto validate_page_migration =
(page_migration_status != ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_KERNEL);
@@ -1655,8 +1656,10 @@ write_perfetto()
}
{
auto buffer_names = rocprofiler::sdk::get_buffer_tracing_names();
auto callbk_name_info = rocprofiler::sdk::get_callback_tracing_names();
namespace sdk = ::rocprofiler::sdk;
auto buffer_names = sdk::get_buffer_tracing_names();
auto callbk_name_info = sdk::get_callback_tracing_names();
for(auto itr : hsa_api_bf_records)
{
@@ -1671,7 +1674,7 @@ write_perfetto()
});
if(ritr != hsa_api_cb_records.end()) _args = ritr->args;
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::hsa_api>::value,
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::hsa_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
@@ -1688,9 +1691,9 @@ write_perfetto()
itr.correlation_id.internal,
[&](::perfetto::EventContext ctx) {
for(const auto& aitr : _args)
add_perfetto_annotation(ctx, aitr.first, aitr.second);
sdk::add_perfetto_annotation(ctx, aitr.first, aitr.second);
});
TRACE_EVENT_END(rocprofiler::trait::name<rocprofiler::category::hsa_api>::value,
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::hsa_api>::name,
track,
itr.end_timestamp,
"end_ns",
@@ -1710,7 +1713,7 @@ write_perfetto()
});
if(ritr != hip_api_cb_records.end()) _args = ritr->args;
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::hip_api>::value,
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::hip_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
@@ -1727,9 +1730,9 @@ write_perfetto()
itr.correlation_id.internal,
[&](::perfetto::EventContext ctx) {
for(const auto& aitr : _args)
add_perfetto_annotation(ctx, aitr.first, aitr.second);
sdk::add_perfetto_annotation(ctx, aitr.first, aitr.second);
});
TRACE_EVENT_END(rocprofiler::trait::name<rocprofiler::category::hip_api>::value,
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::hip_api>::name,
track,
itr.end_timestamp,
"end_ns",
@@ -1741,7 +1744,7 @@ write_perfetto()
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = agent_tracks.at(itr.dst_agent_id.handle);
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::memory_copy>::value,
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::memory_copy>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
@@ -1758,7 +1761,7 @@ write_perfetto()
agents_map.at(itr.dst_agent_id).logical_node_id,
"copy_bytes",
itr.bytes);
TRACE_EVENT_END(rocprofiler::trait::name<rocprofiler::category::memory_copy>::value,
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::memory_copy>::name,
track,
itr.end_timestamp,
"end_ns",
@@ -1787,34 +1790,33 @@ write_perfetto()
demangled.emplace(name, demangle(name));
}
TRACE_EVENT_BEGIN(
rocprofiler::trait::name<rocprofiler::category::kernel_dispatch>::value,
::perfetto::StaticString(demangled.at(name).c_str()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"kind",
itr.kind,
"agent",
agents_map.at(info.agent_id).logical_node_id,
"corr_id",
itr.correlation_id.internal,
"queue",
info.queue_id.handle,
"kernel_id",
info.kernel_id,
"private_segment_size",
info.private_segment_size,
"group_segment_size",
info.group_segment_size,
"workgroup_size",
info.workgroup_size.x * info.workgroup_size.y * info.workgroup_size.z,
"grid_size",
info.grid_size.x * info.grid_size.y * info.grid_size.z);
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
::perfetto::StaticString(demangled.at(name).c_str()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"kind",
itr.kind,
"agent",
agents_map.at(info.agent_id).logical_node_id,
"corr_id",
itr.correlation_id.internal,
"queue",
info.queue_id.handle,
"kernel_id",
info.kernel_id,
"private_segment_size",
info.private_segment_size,
"group_segment_size",
info.group_segment_size,
"workgroup_size",
info.workgroup_size.x * info.workgroup_size.y * info.workgroup_size.z,
"grid_size",
info.grid_size.x * info.grid_size.y * info.grid_size.z);
TRACE_EVENT_END(rocprofiler::trait::name<rocprofiler::category::kernel_dispatch>::value,
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
track,
itr.end_timestamp,
"end_ns",