rocprofv3 OTF2 Output Support (#995)
* CMake support for OTF2 library * Preliminary OTF2 generation implementation * Completed OTF2 Support - HSA API - HIP API - Marker API - Async Memory Copies - Kernel Dispatch * Update lib/rocprofiler-sdk-tool/generateOTF2.cpp - fix location type for dispatches * Testing for OTF2 output * Add OTF2 to requirements.txt * Update lib/rocprofiler-sdk-tool/generateOTF2.cpp - fix getting kernel name * OTF2 testing with rocprofv3/tracing-hip-in-libraries * Format external/otf2/CMakeLists.txt * Update external/otf2/CMakeLists.txt - guard CMP0135 for cmake < 3.24 * Update lib/rocprofiler-sdk-tool/generateOTF2.cpp - fix duplicate string ref issue * Update lib/rocprofiler-sdk-tool/generateOTF2.cpp - fix header includes * Update CI workflow - sudo install pypi requirements for core-rpm for $HOME/.local installs * Update pytest_utils/otf2_reader.py - modifications for reading trace * Update pytest_utils/otf2_reader.py - misc cleanup * Update CI workflow - fix installer artifact naming * Update pytest_utils/otf2_reader.py - handle slightly overlapping kernel timestamps for MI300 * OTF2 attributes for category * Testing with OTF2Reader category attributes * Fix memory leak in OTF2 generation - leaking OTF2_AttributeList
Este commit está contenido en:
cometido por
GitHub
padre
3cd080e63b
commit
16d535ef48
@@ -79,9 +79,11 @@ get_ticks(clockid_t clk_id_v) noexcept
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * nanosec) + static_cast<uint64_t>(ts.tv_nsec);
|
||||
}
|
||||
|
||||
static constexpr int default_clock_id = CLOCK_BOOTTIME;
|
||||
|
||||
// CLOCK_MONOTONIC_RAW equates to HSA-runtime library implementation of os::ReadAccurateClock()
|
||||
// CLOCK_BOOTTIME equates to HSA-runtime library implementation of os::ReadSystemClock()
|
||||
template <int ClockT = CLOCK_BOOTTIME>
|
||||
template <int ClockT = default_clock_id>
|
||||
inline uint64_t
|
||||
timestamp_ns()
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ set(TOOL_HEADERS
|
||||
domain_type.hpp
|
||||
generateCSV.hpp
|
||||
generateJSON.hpp
|
||||
generateOTF2.hpp
|
||||
generatePerfetto.hpp
|
||||
helper.hpp
|
||||
output_file.hpp
|
||||
@@ -23,6 +24,7 @@ set(TOOL_SOURCES
|
||||
domain_type.cpp
|
||||
generateCSV.cpp
|
||||
generateJSON.cpp
|
||||
generateOTF2.cpp
|
||||
generatePerfetto.cpp
|
||||
helper.cpp
|
||||
main.c
|
||||
@@ -44,7 +46,8 @@ target_link_libraries(
|
||||
rocprofiler-sdk::rocprofiler-memcheck
|
||||
rocprofiler-sdk::rocprofiler-common-library
|
||||
rocprofiler-sdk::rocprofiler-cereal
|
||||
rocprofiler-sdk::rocprofiler-perfetto)
|
||||
rocprofiler-sdk::rocprofiler-perfetto
|
||||
rocprofiler-sdk::rocprofiler-otf2)
|
||||
|
||||
set_target_properties(
|
||||
rocprofiler-sdk-tool
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
@@ -51,24 +52,35 @@ namespace tool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string*
|
||||
get_local_datetime(const std::string& dt_format);
|
||||
template <typename Tp>
|
||||
auto
|
||||
as_pointer(Tp&& _val)
|
||||
{
|
||||
return new Tp{_val};
|
||||
}
|
||||
|
||||
const auto* launch_datetime = get_local_datetime(get_env("ROCP_TIME_FORMAT", "%F_%H.%M"));
|
||||
const auto env_regexes =
|
||||
std::string*
|
||||
get_local_datetime(const std::string& dt_format, std::time_t*& dt_curr);
|
||||
|
||||
std::time_t* launch_time = nullptr;
|
||||
const auto* launch_clock = as_pointer(std::chrono::system_clock::now());
|
||||
const auto* launch_datetime =
|
||||
get_local_datetime(get_env("ROCP_TIME_FORMAT", "%F_%H.%M"), launch_time);
|
||||
const auto env_regexes =
|
||||
new std::array<std::regex, 2>{std::regex{"(.*)%(env|ENV)\\{([A-Z0-9_]+)\\}%(.*)"},
|
||||
std::regex{"(.*)\\$(env|ENV)\\{([A-Z0-9_]+)\\}(.*)"}};
|
||||
|
||||
std::string*
|
||||
get_local_datetime(const std::string& dt_format)
|
||||
get_local_datetime(const std::string& dt_format, std::time_t*& _dt_curr)
|
||||
{
|
||||
constexpr auto strsize = 512;
|
||||
auto dt_curr = std::time_t{std::time(nullptr)};
|
||||
|
||||
char mbstr[strsize];
|
||||
if(!_dt_curr) _dt_curr = new std::time_t{std::time_t{std::time(nullptr)}};
|
||||
|
||||
char mbstr[strsize] = {};
|
||||
memset(mbstr, '\0', sizeof(mbstr) * sizeof(char));
|
||||
|
||||
if(std::strftime(mbstr, sizeof(mbstr) - 1, dt_format.c_str(), std::localtime(&dt_curr)) != 0)
|
||||
if(std::strftime(mbstr, sizeof(mbstr) - 1, dt_format.c_str(), std::localtime(_dt_curr)) != 0)
|
||||
return new std::string{mbstr};
|
||||
|
||||
return nullptr;
|
||||
@@ -245,39 +257,23 @@ config::config()
|
||||
get_env("ROCPROF_KERNEL_FILTER_RANGE", std::string{}))}
|
||||
, counters{parse_counters(get_env("ROCPROF_COUNTERS", std::string{}))}
|
||||
{
|
||||
auto to_upper = [](std::string val) {
|
||||
for(auto& vitr : val)
|
||||
vitr = toupper(vitr);
|
||||
return val;
|
||||
};
|
||||
|
||||
auto output_format = get_env("ROCPROF_OUTPUT_FORMAT", "CSV");
|
||||
|
||||
for(auto& itr : output_format)
|
||||
itr = toupper(itr);
|
||||
|
||||
for(auto itr : {',', ';', ':'})
|
||||
{
|
||||
auto pos = std::string::npos;
|
||||
do
|
||||
{
|
||||
pos = output_format.find(itr);
|
||||
if(pos != std::string::npos) output_format.replace(pos, 1, " ");
|
||||
} while(pos != std::string::npos);
|
||||
}
|
||||
|
||||
auto entries = std::set<std::string>{};
|
||||
auto parser = std::stringstream{output_format};
|
||||
|
||||
while(true)
|
||||
{
|
||||
auto _val = std::string{};
|
||||
parser >> _val;
|
||||
if(!_val.empty())
|
||||
entries.emplace(_val);
|
||||
else
|
||||
break;
|
||||
}
|
||||
auto entries = std::set<std::string>{};
|
||||
for(const auto& itr : sdk::parse::tokenize(output_format, " \t,;:"))
|
||||
entries.emplace(to_upper(itr));
|
||||
|
||||
csv_output = entries.count("CSV") > 0 || entries.empty();
|
||||
json_output = entries.count("JSON") > 0;
|
||||
pftrace_output = entries.count("PFTRACE") > 0;
|
||||
otf2_output = entries.count("OTF2") > 0;
|
||||
|
||||
const auto supported_formats = std::set<std::string_view>{"CSV", "JSON", "PFTRACE"};
|
||||
const auto supported_formats = std::set<std::string_view>{"CSV", "JSON", "PFTRACE", "OTF2"};
|
||||
for(const auto& itr : entries)
|
||||
{
|
||||
LOG_IF(FATAL, supported_formats.count(itr) == 0)
|
||||
|
||||
@@ -77,6 +77,7 @@ struct config
|
||||
bool csv_output = false;
|
||||
bool json_output = false;
|
||||
bool pftrace_output = false;
|
||||
bool otf2_output = false;
|
||||
bool kernel_rename = get_env("ROCPROF_KERNEL_RENAME", false);
|
||||
int mpi_size = get_mpi_size();
|
||||
int mpi_rank = get_mpi_rank();
|
||||
|
||||
@@ -0,0 +1,796 @@
|
||||
// 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 "generateOTF2.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "lib/common/filesystem.hpp"
|
||||
#include "lib/common/mpl.hpp"
|
||||
#include "lib/common/units.hpp"
|
||||
#include "lib/common/utility.hpp"
|
||||
#include "lib/rocprofiler-sdk-tool/config.hpp"
|
||||
#include "output_file.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/fwd.h>
|
||||
#include <rocprofiler-sdk/marker/api_id.h>
|
||||
#include <rocprofiler-sdk/rocprofiler.h>
|
||||
#include <rocprofiler-sdk/cxx/hash.hpp>
|
||||
#include <rocprofiler-sdk/cxx/operators.hpp>
|
||||
#include <rocprofiler-sdk/cxx/perfetto.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <otf2/OTF2_AttributeList.h>
|
||||
#include <otf2/OTF2_AttributeValue.h>
|
||||
#include <otf2/OTF2_Definitions.h>
|
||||
#include <otf2/OTF2_GeneralDefinitions.h>
|
||||
#include <otf2/OTF2_Pthread_Locks.h>
|
||||
#include <otf2/otf2.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#define OTF2_CHECK(result) \
|
||||
{ \
|
||||
OTF2_ErrorCode ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) = result; \
|
||||
if(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) != OTF2_SUCCESS) \
|
||||
{ \
|
||||
auto _err_name = OTF2_Error_GetName(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__)); \
|
||||
auto _err_msg = \
|
||||
OTF2_Error_GetDescription(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__)); \
|
||||
ROCP_FATAL << #result << " failed with error code " << _err_name \
|
||||
<< " (code=" << ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) \
|
||||
<< ") :: " << _err_msg; \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace tool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename Tp, size_t N>
|
||||
struct array_hash
|
||||
{
|
||||
size_t operator()(const std::array<Tp, N>& _data) const
|
||||
{
|
||||
constexpr size_t seed = 0x9e3779b9;
|
||||
size_t _val = 0;
|
||||
for(const auto& itr : _data)
|
||||
_val ^= std::hash<Tp>{}(itr) + seed + (_val << 6) + (_val >> 2);
|
||||
return _val;
|
||||
}
|
||||
|
||||
template <typename... Up>
|
||||
size_t operator()(Up... _data) const
|
||||
{
|
||||
static_assert(sizeof...(Up) == N, "Insufficient data");
|
||||
return operator()(std::array<Tp, N>{std::forward<Up>(_data)...});
|
||||
}
|
||||
};
|
||||
|
||||
struct region_info
|
||||
{
|
||||
std::string name = {};
|
||||
OTF2_RegionRole_enum region_role = OTF2_REGION_ROLE_FUNCTION;
|
||||
OTF2_Paradigm_enum paradigm = OTF2_PARADIGM_HIP;
|
||||
};
|
||||
|
||||
OTF2_FlushType
|
||||
pre_flush(void* userData,
|
||||
OTF2_FileType fileType,
|
||||
OTF2_LocationRef location,
|
||||
void* callerData,
|
||||
bool fini);
|
||||
|
||||
OTF2_TimeStamp
|
||||
post_flush(void* userData, OTF2_FileType fileType, OTF2_LocationRef location);
|
||||
|
||||
template <typename... Args>
|
||||
void
|
||||
consume_variables(Args&&...)
|
||||
{}
|
||||
|
||||
using event_writer_t = OTF2_EvtWriter;
|
||||
using archive_t = OTF2_Archive;
|
||||
using attribute_list_t = OTF2_AttributeList;
|
||||
using hash_value_t = size_t;
|
||||
using hash_map_t = std::unordered_map<hash_value_t, region_info>;
|
||||
|
||||
auto main_tid = common::get_tid();
|
||||
archive_t* archive = nullptr;
|
||||
auto flush_callbacks = OTF2_FlushCallbacks{pre_flush, post_flush};
|
||||
|
||||
struct location_base
|
||||
{
|
||||
uint64_t pid = 0;
|
||||
rocprofiler_thread_id_t tid = 0;
|
||||
rocprofiler_agent_id_t agent = {.handle = 0};
|
||||
rocprofiler_queue_id_t queue = {.handle = 0};
|
||||
|
||||
location_base(uint64_t _pid,
|
||||
rocprofiler_thread_id_t _tid,
|
||||
rocprofiler_agent_id_t _agent = {.handle = 0},
|
||||
rocprofiler_queue_id_t _queue = {.handle = 0})
|
||||
: pid{_pid}
|
||||
, tid{_tid}
|
||||
, agent{_agent}
|
||||
, queue{_queue}
|
||||
{}
|
||||
|
||||
auto hash() const
|
||||
{
|
||||
return array_hash<uint64_t, 4>{}(pid, tid, agent.handle + 1, queue.handle + 1);
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
operator<(const location_base& lhs, const location_base& rhs)
|
||||
{
|
||||
return std::tie(lhs.pid, lhs.tid, lhs.agent.handle, lhs.queue.handle) <
|
||||
std::tie(rhs.pid, rhs.tid, rhs.agent.handle, rhs.queue.handle);
|
||||
}
|
||||
|
||||
struct location_data : location_base
|
||||
{
|
||||
location_data(uint64_t _pid,
|
||||
rocprofiler_thread_id_t _tid,
|
||||
rocprofiler_agent_id_t _agent = {.handle = 0},
|
||||
rocprofiler_queue_id_t _queue = {.handle = 0})
|
||||
: location_base{_pid, _tid, _agent, _queue}
|
||||
, index{++index_counter}
|
||||
, event_writer{OTF2_Archive_GetEvtWriter(CHECK_NOTNULL(archive), index)}
|
||||
{
|
||||
CHECK_NOTNULL(event_writer);
|
||||
}
|
||||
|
||||
using location_base::hash;
|
||||
|
||||
static uint64_t index_counter;
|
||||
|
||||
uint64_t index = 0;
|
||||
event_writer_t* event_writer = nullptr;
|
||||
|
||||
bool operator==(const location_base& rhs) const { return (hash() == rhs.hash()); }
|
||||
};
|
||||
|
||||
uint64_t location_data::index_counter = 0;
|
||||
|
||||
OTF2_TimeStamp
|
||||
get_time()
|
||||
{
|
||||
auto _ts = rocprofiler_timestamp_t{};
|
||||
rocprofiler_get_timestamp(&_ts);
|
||||
return static_cast<OTF2_TimeStamp>(_ts);
|
||||
}
|
||||
|
||||
auto&
|
||||
get_locations()
|
||||
{
|
||||
static auto _v = std::vector<std::unique_ptr<location_data>>{};
|
||||
return _v;
|
||||
}
|
||||
|
||||
const location_data*
|
||||
get_location(const location_base& _location, bool _init = false)
|
||||
{
|
||||
for(auto& itr : get_locations())
|
||||
if(*itr == _location) return itr.get();
|
||||
|
||||
if(_init)
|
||||
return get_locations()
|
||||
.emplace_back(std::make_unique<location_data>(
|
||||
_location.pid, _location.tid, _location.agent, _location.queue))
|
||||
.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
event_writer_t*
|
||||
get_event_writer(const location_base& _location, bool _init = false)
|
||||
{
|
||||
const auto* _loc = get_location(_location, _init);
|
||||
return (_loc) ? _loc->event_writer : nullptr;
|
||||
}
|
||||
|
||||
OTF2_FlushType
|
||||
pre_flush(void* userData,
|
||||
OTF2_FileType fileType,
|
||||
OTF2_LocationRef location,
|
||||
void* callerData,
|
||||
bool fini)
|
||||
{
|
||||
consume_variables(userData, fileType, location, callerData, fini);
|
||||
return OTF2_FLUSH;
|
||||
}
|
||||
|
||||
OTF2_TimeStamp
|
||||
post_flush(void* userData, OTF2_FileType fileType, OTF2_LocationRef location)
|
||||
{
|
||||
consume_variables(userData, fileType, location);
|
||||
return get_time();
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
size_t
|
||||
get_hash_id(Tp&& _val)
|
||||
{
|
||||
using value_type = common::mpl::unqualified_type_t<Tp>;
|
||||
|
||||
if constexpr(!std::is_pointer<Tp>::value)
|
||||
return std::hash<value_type>{}(std::forward<Tp>(_val));
|
||||
else if constexpr(std::is_same<value_type, const char*>::value ||
|
||||
std::is_same<value_type, char*>::value)
|
||||
return get_hash_id(std::string_view{_val});
|
||||
else
|
||||
return get_hash_id(*_val);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto
|
||||
add_event(std::string_view name,
|
||||
const location_base& _location,
|
||||
rocprofiler_callback_phase_t _phase,
|
||||
OTF2_TimeStamp _ts,
|
||||
attribute_list_t* _attributes = nullptr)
|
||||
{
|
||||
auto* evt_writer = get_event_writer(_location, true);
|
||||
auto _hash = get_hash_id(name);
|
||||
|
||||
if(_phase == ROCPROFILER_CALLBACK_PHASE_ENTER)
|
||||
OTF2_CHECK(OTF2_EvtWriter_Enter(evt_writer, _attributes, _ts, _hash))
|
||||
else if(_phase == ROCPROFILER_CALLBACK_PHASE_EXIT)
|
||||
OTF2_CHECK(OTF2_EvtWriter_Leave(evt_writer, _attributes, _ts, _hash))
|
||||
else
|
||||
ROCP_FATAL << "otf2::add_event phase is not enter or exit";
|
||||
}
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
namespace fs = common::filesystem;
|
||||
|
||||
auto _filename = get_output_filename("results", std::string_view{});
|
||||
auto _filepath = fs::path{_filename};
|
||||
auto _name = _filepath.filename().string();
|
||||
auto _path = _filepath.parent_path().string();
|
||||
|
||||
if(fs::exists(_filepath)) fs::remove_all(_filepath);
|
||||
|
||||
constexpr uint64_t evt_chunk_size = 2 * common::units::MB;
|
||||
constexpr uint64_t def_chunk_size = 8 * common::units::MB;
|
||||
|
||||
archive = OTF2_Archive_Open(_path.c_str(),
|
||||
_name.c_str(),
|
||||
OTF2_FILEMODE_WRITE,
|
||||
evt_chunk_size, // event chunk size
|
||||
def_chunk_size, // def chunk size
|
||||
OTF2_SUBSTRATE_POSIX,
|
||||
OTF2_COMPRESSION_NONE);
|
||||
|
||||
OTF2_CHECK(OTF2_Archive_SetFlushCallbacks(archive, &flush_callbacks, nullptr));
|
||||
OTF2_CHECK(OTF2_Archive_SetSerialCollectiveCallbacks(archive));
|
||||
OTF2_CHECK(OTF2_Pthread_Archive_SetLockingCallbacks(archive, nullptr));
|
||||
OTF2_CHECK(OTF2_Archive_OpenEvtFiles(archive));
|
||||
|
||||
ROCP_ERROR << "Opened result file: " << _filename << ".oft2";
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
OTF2_CHECK(OTF2_Archive_Close(archive));
|
||||
}
|
||||
|
||||
struct event_info
|
||||
{
|
||||
explicit event_info(location_base&& _loc)
|
||||
: m_location{tool::get_location(std::forward<location_base>(_loc), true)}
|
||||
{}
|
||||
|
||||
auto id() const { return m_location->index; }
|
||||
auto hash() const { return m_location->hash(); }
|
||||
const location_base* get_location() const { return m_location; }
|
||||
|
||||
std::string name = {};
|
||||
uint64_t event_count = 0;
|
||||
|
||||
private:
|
||||
const location_data* m_location = nullptr;
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
attribute_list_t*
|
||||
create_attribute_list()
|
||||
{
|
||||
auto* _val = OTF2_AttributeList_New();
|
||||
|
||||
const auto* _name = sdk::perfetto_category<Tp>::name;
|
||||
auto _hash = get_hash_id(_name);
|
||||
|
||||
auto _attr_value = OTF2_AttributeValue{};
|
||||
_attr_value.stringRef = _hash;
|
||||
OTF2_AttributeList_AddAttribute(_val, 0, OTF2_TYPE_STRING, _attr_value);
|
||||
|
||||
return _val;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
write_otf2(tool_table* tool_functions,
|
||||
uint64_t pid,
|
||||
const 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_memory_data*/)
|
||||
{
|
||||
namespace sdk = ::rocprofiler::sdk;
|
||||
|
||||
setup();
|
||||
|
||||
auto _app_ts = *tool_functions->tool_get_app_timestamps_fn();
|
||||
auto agents_map = std::unordered_map<rocprofiler_agent_id_t, rocprofiler_agent_t>{};
|
||||
for(auto itr : agent_data)
|
||||
agents_map.emplace(itr.id, itr);
|
||||
|
||||
const auto kernel_sym_data = get_kernel_symbol_data();
|
||||
const auto buffer_names = sdk::get_buffer_tracing_names();
|
||||
auto tids = std::set<rocprofiler_thread_id_t>{};
|
||||
auto agent_thread_ids = std::map<rocprofiler_thread_id_t, std::set<rocprofiler_agent_id_t>>{};
|
||||
auto agent_queue_ids =
|
||||
std::map<rocprofiler_thread_id_t,
|
||||
std::map<rocprofiler_agent_id_t, std::unordered_set<rocprofiler_queue_id_t>>>{};
|
||||
|
||||
auto thread_event_info = std::map<rocprofiler_thread_id_t, event_info>{};
|
||||
auto agent_memcpy_info =
|
||||
std::map<rocprofiler_thread_id_t, std::map<rocprofiler_agent_id_t, event_info>>{};
|
||||
auto agent_dispatch_info =
|
||||
std::map<rocprofiler_thread_id_t,
|
||||
std::map<rocprofiler_agent_id_t, std::map<rocprofiler_queue_id_t, event_info>>>{};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
auto _get_kernel_sym_data =
|
||||
[&kernel_sym_data](
|
||||
const rocprofiler_kernel_dispatch_info_t& _info) -> const kernel_symbol_data* {
|
||||
for(const auto& kitr : kernel_sym_data)
|
||||
if(kitr.kernel_id == _info.kernel_id) return &kitr;
|
||||
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.thread_id].emplace(itr.dst_agent_id);
|
||||
|
||||
for(auto itr : *kernel_dispatch_data)
|
||||
agent_queue_ids[itr.thread_id][itr.dispatch_info.agent_id].emplace(
|
||||
itr.dispatch_info.queue_id);
|
||||
}
|
||||
|
||||
{
|
||||
for(auto itr : tids)
|
||||
thread_event_info.emplace(itr, location_base{pid, itr});
|
||||
|
||||
for(const auto& [tid, itr] : agent_thread_ids)
|
||||
for(auto agent : itr)
|
||||
agent_memcpy_info[tid].emplace(agent, location_base{pid, tid, agent});
|
||||
|
||||
for(auto [tid, itr] : agent_queue_ids)
|
||||
for(auto [agent, qitr] : itr)
|
||||
for(auto queue : qitr)
|
||||
agent_dispatch_info[tid][agent].emplace(queue,
|
||||
location_base{pid, tid, agent, queue});
|
||||
}
|
||||
|
||||
for(auto& [tid, evt] : thread_event_info)
|
||||
{
|
||||
evt.name = fmt::format("Thread {}", tid);
|
||||
}
|
||||
|
||||
for(auto& [tid, itr] : agent_memcpy_info)
|
||||
{
|
||||
for(auto& [agent, evt] : itr)
|
||||
{
|
||||
const auto* _agent = _get_agent(agent);
|
||||
auto _type_name = std::string_view{"UNK"};
|
||||
if(_agent->type == ROCPROFILER_AGENT_TYPE_CPU)
|
||||
_type_name = "CPU";
|
||||
else if(_agent->type == ROCPROFILER_AGENT_TYPE_GPU)
|
||||
_type_name = "GPU";
|
||||
|
||||
evt.name = fmt::format(
|
||||
"Thread {}, Copy to {} {}", tid, _type_name, _agent->logical_node_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
auto _queue_ids = std::map<rocprofiler_queue_id_t, uint64_t>{};
|
||||
for(auto& [tid, itr] : agent_dispatch_info)
|
||||
for(auto& [agent, qitr] : itr)
|
||||
for(auto& [queue, evt] : qitr)
|
||||
_queue_ids.emplace(queue, 0);
|
||||
|
||||
{
|
||||
uint64_t _n = 0;
|
||||
for(auto& qitr : _queue_ids)
|
||||
qitr.second = _n++;
|
||||
}
|
||||
|
||||
for(auto& [tid, itr] : agent_dispatch_info)
|
||||
{
|
||||
for(auto& [agent, qitr] : itr)
|
||||
{
|
||||
for(auto& [queue, evt] : qitr)
|
||||
{
|
||||
const auto* _agent = _get_agent(agent);
|
||||
auto _type_name = std::string_view{"UNK"};
|
||||
if(_agent->type == ROCPROFILER_AGENT_TYPE_CPU)
|
||||
_type_name = "CPU";
|
||||
else if(_agent->type == ROCPROFILER_AGENT_TYPE_GPU)
|
||||
_type_name = "GPU";
|
||||
|
||||
evt.name = fmt::format("Thread {}, Compute on {} {}, Queue {}",
|
||||
tid,
|
||||
_type_name,
|
||||
_agent->logical_node_type_id,
|
||||
_queue_ids.at(queue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto _hash_data = hash_map_t{};
|
||||
|
||||
struct evt_data
|
||||
{
|
||||
rocprofiler_callback_phase_t phase = ROCPROFILER_CALLBACK_PHASE_NONE;
|
||||
std::string_view name = {};
|
||||
const location_base* location = nullptr;
|
||||
uint64_t timestamp = 0;
|
||||
OTF2_AttributeList* attributes = nullptr;
|
||||
};
|
||||
|
||||
auto _data = std::deque<evt_data>{};
|
||||
auto _attr_str = std::unordered_map<size_t, std::string_view>{};
|
||||
auto get_attr = [&_attr_str](auto _category) {
|
||||
using category_t = common::mpl::unqualified_type_t<decltype(_category)>;
|
||||
auto _name = sdk::perfetto_category<category_t>::name;
|
||||
_attr_str.emplace(get_hash_id(_name), _name);
|
||||
return create_attribute_list<category_t>();
|
||||
};
|
||||
|
||||
// trace events
|
||||
{
|
||||
auto callbk_name_info = sdk::get_callback_tracing_names();
|
||||
|
||||
auto add_event_data = [&buffer_names,
|
||||
&_hash_data,
|
||||
&_data,
|
||||
&tool_functions,
|
||||
&thread_event_info,
|
||||
&get_attr](const auto* _inp, auto _attrib) {
|
||||
if(!_inp) return;
|
||||
for(auto itr : *_inp)
|
||||
{
|
||||
using value_type = common::mpl::unqualified_type_t<decltype(itr)>;
|
||||
auto name = buffer_names.at(itr.kind, itr.operation);
|
||||
auto paradigm = OTF2_PARADIGM_HIP;
|
||||
if constexpr(std::is_same<value_type,
|
||||
rocprofiler_buffer_tracing_marker_api_record_t>::value)
|
||||
{
|
||||
paradigm = OTF2_PARADIGM_USER;
|
||||
if(itr.kind == ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API &&
|
||||
itr.operation != ROCPROFILER_MARKER_CORE_API_ID_roctxGetThreadId)
|
||||
name = tool_functions->tool_get_roctx_msg_fn(itr.correlation_id.internal);
|
||||
}
|
||||
|
||||
_hash_data.emplace(
|
||||
get_hash_id(name),
|
||||
region_info{std::string{name}, OTF2_REGION_ROLE_FUNCTION, paradigm});
|
||||
|
||||
auto& _evt_info = thread_event_info.at(itr.thread_id);
|
||||
_evt_info.event_count += 1;
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.start_timestamp,
|
||||
get_attr(_attrib)});
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.end_timestamp,
|
||||
nullptr});
|
||||
}
|
||||
};
|
||||
|
||||
add_event_data(hsa_api_data, sdk::category::hsa_api{});
|
||||
add_event_data(hip_api_data, sdk::category::hip_api{});
|
||||
add_event_data(marker_api_data, sdk::category::marker_api{});
|
||||
}
|
||||
|
||||
for(auto itr : *memory_copy_data)
|
||||
{
|
||||
auto name = buffer_names.at(itr.kind, itr.operation);
|
||||
_hash_data.emplace(
|
||||
get_hash_id(name),
|
||||
region_info{std::string{name}, OTF2_REGION_ROLE_DATA_TRANSFER, OTF2_PARADIGM_HIP});
|
||||
|
||||
// TODO: add attributes for memory copy parameters
|
||||
|
||||
auto& _evt_info = agent_memcpy_info.at(itr.thread_id).at(itr.dst_agent_id);
|
||||
_evt_info.event_count += 1;
|
||||
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.start_timestamp,
|
||||
get_attr(sdk::category::memory_copy{})});
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.end_timestamp,
|
||||
nullptr});
|
||||
}
|
||||
|
||||
for(auto itr : *kernel_dispatch_data)
|
||||
{
|
||||
const auto& info = itr.dispatch_info;
|
||||
const auto* sym = _get_kernel_sym_data(info);
|
||||
CHECK(sym != nullptr);
|
||||
|
||||
auto name = tool_functions->tool_get_kernel_name_fn(info.kernel_id,
|
||||
itr.correlation_id.external.value);
|
||||
_hash_data.emplace(
|
||||
get_hash_id(name),
|
||||
region_info{std::string{name}, OTF2_REGION_ROLE_FUNCTION, OTF2_PARADIGM_HIP});
|
||||
|
||||
// TODO: add attributes for kernel dispatch parameters
|
||||
|
||||
auto& _evt_info = agent_dispatch_info.at(itr.thread_id).at(info.agent_id).at(info.queue_id);
|
||||
_evt_info.event_count += 1;
|
||||
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.start_timestamp,
|
||||
get_attr(sdk::category::kernel_dispatch{})});
|
||||
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
|
||||
name,
|
||||
_evt_info.get_location(),
|
||||
itr.end_timestamp,
|
||||
nullptr});
|
||||
}
|
||||
|
||||
std::sort(_data.begin(), _data.end(), [](const evt_data& lhs, const evt_data& rhs) {
|
||||
if(lhs.timestamp != rhs.timestamp) return (lhs.timestamp < rhs.timestamp);
|
||||
if(lhs.phase != rhs.phase) return (lhs.phase > rhs.phase);
|
||||
return (*lhs.location < *rhs.location);
|
||||
});
|
||||
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
add_event(itr.name, *itr.location, itr.phase, itr.timestamp, itr.attributes);
|
||||
ROCP_ERROR_IF(itr.timestamp < _app_ts.app_start_time)
|
||||
<< "event found with timestamp < app start time by "
|
||||
<< (_app_ts.app_start_time - itr.timestamp) << " nsec :: " << itr.name;
|
||||
ROCP_ERROR_IF(itr.timestamp > _app_ts.app_end_time)
|
||||
<< "event found with timestamp > app end time by "
|
||||
<< (itr.timestamp - _app_ts.app_end_time) << " nsec :: " << itr.name;
|
||||
}
|
||||
|
||||
for(const auto& itr : _data)
|
||||
{
|
||||
if(itr.attributes) OTF2_AttributeList_Delete(itr.attributes);
|
||||
}
|
||||
|
||||
OTF2_CHECK(OTF2_Archive_CloseEvtFiles(archive));
|
||||
|
||||
OTF2_CHECK(OTF2_Archive_OpenDefFiles(archive));
|
||||
for(auto& itr : get_locations())
|
||||
{
|
||||
OTF2_DefWriter* def_writer = OTF2_Archive_GetDefWriter(archive, itr->index);
|
||||
OTF2_Archive_CloseDefWriter(archive, def_writer);
|
||||
}
|
||||
OTF2_CHECK(OTF2_Archive_CloseDefFiles(archive));
|
||||
|
||||
auto _timer_resolution =
|
||||
common::get_clock_period_ns_impl(common::default_clock_id) * std::nano::den;
|
||||
auto _global_offset = _app_ts.app_start_time;
|
||||
auto _max_trace_length = (_app_ts.app_end_time - _app_ts.app_start_time);
|
||||
|
||||
OTF2_GlobalDefWriter* global_def_writer = OTF2_Archive_GetGlobalDefWriter(archive);
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteClockProperties(
|
||||
global_def_writer,
|
||||
_timer_resolution,
|
||||
_global_offset,
|
||||
_max_trace_length,
|
||||
std::chrono::system_clock::now().time_since_epoch().count()));
|
||||
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(global_def_writer, 0, ""));
|
||||
for(const auto& itr : _hash_data)
|
||||
{
|
||||
if(itr.first != 0)
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(
|
||||
global_def_writer, itr.first, itr.second.name.c_str()));
|
||||
}
|
||||
|
||||
for(const auto& itr : _hash_data)
|
||||
{
|
||||
if(itr.first != 0)
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteRegion(global_def_writer,
|
||||
itr.first,
|
||||
itr.first,
|
||||
0,
|
||||
0,
|
||||
itr.second.region_role,
|
||||
itr.second.paradigm,
|
||||
OTF2_REGION_FLAG_NONE,
|
||||
0,
|
||||
0,
|
||||
0));
|
||||
}
|
||||
|
||||
auto add_write_string = [&global_def_writer](size_t _hash, std::string_view _name) {
|
||||
static auto _existing = std::unordered_set<size_t>{};
|
||||
if(_hash > 0 && _existing.count(_hash) == 0)
|
||||
{
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(global_def_writer, _hash, _name.data()));
|
||||
_existing.emplace(_hash);
|
||||
}
|
||||
};
|
||||
|
||||
auto add_write_string_val = [&add_write_string](std::string_view _name_v) {
|
||||
auto _hash_v = get_hash_id(_name_v);
|
||||
add_write_string(_hash_v, _name_v);
|
||||
return _hash_v;
|
||||
};
|
||||
|
||||
auto _attr_name = std::string_view{"category"};
|
||||
auto _attr_desc = std::string_view{"tracing category"};
|
||||
|
||||
auto _attr_name_hash = add_write_string_val(_attr_name);
|
||||
auto _attr_desc_hash = add_write_string_val(_attr_desc);
|
||||
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteAttribute(
|
||||
global_def_writer, 0, _attr_name_hash, _attr_desc_hash, OTF2_TYPE_STRING));
|
||||
|
||||
for(const auto& itr : _attr_str)
|
||||
add_write_string(itr.first, itr.second);
|
||||
|
||||
auto _cmdline = common::read_command_line(pid);
|
||||
auto _exe_name = (_cmdline.empty()) ? std::string{"??"} : _cmdline.at(0);
|
||||
auto _exe_hash = get_hash_id(_exe_name);
|
||||
add_write_string(_exe_hash, _exe_name);
|
||||
|
||||
auto _node_name = std::string{"node"};
|
||||
{
|
||||
char _hostname_c[PATH_MAX];
|
||||
if(::gethostname(_hostname_c, PATH_MAX) == 0 && ::strnlen(_hostname_c, PATH_MAX) < PATH_MAX)
|
||||
_node_name = std::string{_hostname_c};
|
||||
}
|
||||
auto _node_hash = get_hash_id(_node_name);
|
||||
add_write_string(_node_hash, _node_name);
|
||||
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteSystemTreeNode(
|
||||
global_def_writer, 0, _exe_hash, _node_hash, OTF2_UNDEFINED_SYSTEM_TREE_NODE));
|
||||
|
||||
// Process
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocationGroup(global_def_writer,
|
||||
0,
|
||||
_exe_hash,
|
||||
OTF2_LOCATION_GROUP_TYPE_PROCESS,
|
||||
0,
|
||||
OTF2_UNDEFINED_LOCATION_GROUP));
|
||||
|
||||
// Accelerators
|
||||
for(const auto& agent_v : agent_data)
|
||||
{
|
||||
const auto* _name = agent_v.name;
|
||||
auto _hash = get_hash_id(_name);
|
||||
|
||||
add_write_string(_hash, _name);
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocationGroup(global_def_writer,
|
||||
agent_v.id.handle,
|
||||
_hash,
|
||||
OTF2_LOCATION_GROUP_TYPE_ACCELERATOR,
|
||||
0,
|
||||
OTF2_UNDEFINED_LOCATION_GROUP));
|
||||
}
|
||||
|
||||
// Thread Events
|
||||
for(auto& [tid, evt] : thread_event_info)
|
||||
{
|
||||
auto _hash = get_hash_id(evt.name);
|
||||
|
||||
add_write_string(_hash, evt.name);
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
|
||||
evt.id(), // id
|
||||
_hash,
|
||||
OTF2_LOCATION_TYPE_CPU_THREAD,
|
||||
2 * evt.event_count, // # events
|
||||
0 // location group
|
||||
));
|
||||
}
|
||||
|
||||
// Memcpy Events
|
||||
for(auto& [tid, itr] : agent_memcpy_info)
|
||||
{
|
||||
for(auto& [agent, evt] : itr)
|
||||
{
|
||||
auto _hash = get_hash_id(evt.name);
|
||||
|
||||
add_write_string(_hash, evt.name);
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
|
||||
evt.id(), // id
|
||||
_hash,
|
||||
OTF2_LOCATION_TYPE_ACCELERATOR_STREAM,
|
||||
2 * evt.event_count, // # events
|
||||
agent.handle // location group
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch Events
|
||||
for(auto& [tid, itr] : agent_dispatch_info)
|
||||
{
|
||||
for(auto& [agent, qitr] : itr)
|
||||
{
|
||||
for(auto& [queue, evt] : qitr)
|
||||
{
|
||||
auto _hash = get_hash_id(evt.name);
|
||||
|
||||
add_write_string(_hash, evt.name);
|
||||
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
|
||||
evt.id(), // id
|
||||
_hash,
|
||||
OTF2_LOCATION_TYPE_ACCELERATOR_STREAM,
|
||||
2 * evt.event_count, // # events
|
||||
agent.handle // location group
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
} // namespace tool
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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_otf2(tool_table* tool_functions,
|
||||
uint64_t pid,
|
||||
const 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_memory_data);
|
||||
} // namespace tool
|
||||
} // namespace rocprofiler
|
||||
@@ -33,18 +33,11 @@ namespace tool
|
||||
{
|
||||
namespace fs = common::filesystem;
|
||||
|
||||
std::pair<std::ostream*, output_stream_dtor_t>
|
||||
get_output_stream(std::string_view fname, std::string_view ext)
|
||||
std::string
|
||||
get_output_filename(std::string_view fname, std::string_view ext)
|
||||
{
|
||||
auto cfg_output_path = tool::format(tool::get_config().output_path);
|
||||
|
||||
if(cfg_output_path == "stdout" || cfg_output_path == "STDOUT")
|
||||
return {&std::cout, [](auto*&) {}};
|
||||
else if(cfg_output_path == "stderr" || cfg_output_path == "STDERR")
|
||||
return {&std::cout, [](auto*&) {}};
|
||||
else if(cfg_output_path.empty())
|
||||
return {&std::clog, [](auto*&) {}};
|
||||
|
||||
// add a period to provided file extension if necessary
|
||||
constexpr auto period = std::string_view{"."};
|
||||
constexpr auto noperiod = std::string_view{};
|
||||
@@ -60,10 +53,22 @@ get_output_stream(std::string_view fname, std::string_view ext)
|
||||
output_path.string())};
|
||||
if(!fs::exists(output_path)) fs::create_directories(output_path);
|
||||
|
||||
auto output_file =
|
||||
tool::format(output_path / fmt::format("{}_{}{}", output_prefix, fname, _ext));
|
||||
return tool::format(output_path / fmt::format("{}_{}{}", output_prefix, fname, _ext));
|
||||
}
|
||||
std::pair<std::ostream*, output_stream_dtor_t>
|
||||
get_output_stream(std::string_view fname, std::string_view ext)
|
||||
{
|
||||
auto cfg_output_path = tool::format(tool::get_config().output_path);
|
||||
|
||||
auto* _ofs = new std::ofstream{output_file};
|
||||
if(cfg_output_path == "stdout" || cfg_output_path == "STDOUT")
|
||||
return {&std::cout, [](auto*&) {}};
|
||||
else if(cfg_output_path == "stderr" || cfg_output_path == "STDERR")
|
||||
return {&std::cout, [](auto*&) {}};
|
||||
else if(cfg_output_path.empty())
|
||||
return {&std::clog, [](auto*&) {}};
|
||||
|
||||
auto output_file = get_output_filename(fname, ext);
|
||||
auto* _ofs = new std::ofstream{output_file};
|
||||
|
||||
LOG_IF(FATAL, !_ofs && !*_ofs) << fmt::format("Failed to open {} for output", output_file);
|
||||
ROCP_ERROR << "Opened result file: " << output_file;
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace tool
|
||||
{
|
||||
using output_stream_dtor_t = void (*)(std::ostream*&);
|
||||
|
||||
std::string
|
||||
get_output_filename(std::string_view fname, std::string_view ext);
|
||||
|
||||
std::pair<std::ostream*, output_stream_dtor_t>
|
||||
get_output_stream(std::string_view fname, std::string_view ext);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "domain_type.hpp"
|
||||
#include "generateCSV.hpp"
|
||||
#include "generateJSON.hpp"
|
||||
#include "generateOTF2.hpp"
|
||||
#include "generatePerfetto.hpp"
|
||||
#include "helper.hpp"
|
||||
#include "output_file.hpp"
|
||||
@@ -1249,8 +1250,8 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
|
||||
{
|
||||
client_finalizer = fini_func;
|
||||
|
||||
const uint64_t buffer_size = 32 * common::units::get_page_size();
|
||||
const uint64_t buffer_watermark = 31 * common::units::get_page_size();
|
||||
constexpr uint64_t buffer_size = 32 * common::units::KiB;
|
||||
constexpr uint64_t buffer_watermark = 31 * common::units::KiB;
|
||||
|
||||
rocprofiler_get_timestamp(&(stats_timestamp->app_start_time));
|
||||
|
||||
@@ -1528,6 +1529,7 @@ tool_fini(void* /*tool_data*/)
|
||||
|
||||
rocprofiler_get_timestamp(&(stats_timestamp->app_end_time));
|
||||
|
||||
flush();
|
||||
rocprofiler_stop_context(get_client_ctx());
|
||||
flush();
|
||||
|
||||
@@ -1605,6 +1607,19 @@ tool_fini(void* /*tool_data*/)
|
||||
&scratch_memory_output.element_data);
|
||||
}
|
||||
|
||||
if(tool::get_config().otf2_output)
|
||||
{
|
||||
rocprofiler::tool::write_otf2(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);
|
||||
|
||||
Referencia en una nueva incidencia
Block a user