Initial skeleton (#1)
* googletest submodule * cmake folder * misc root files - clang-format - cmake-format - pyproject.toml - requirements.txt - VERSION * workflows * RPM files * external folder * samples folder * tests root folder * source/bin folder * source/include folder * source/lib/common folder * source/lib/plugins folder * source/lib/tests folder - for library unit tests * source/lib/rocprofiler folder - rocprofiler library implementation * Remaining cmake files * lib/common/containers - ring_buffer - atomic_ring_buffer - stable_vector - static_vector * Update .gitignore * Update hsa.hpp - include cstdint * cmake formatting (cmake-format) (#2) Co-authored-by: jrmadsen <jrmadsen@users.noreply.github.com> * Remove linting.yml - uses self-hosted runners --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7d1c7757a8
commit
527aa71f5a
@@ -0,0 +1,26 @@
|
||||
set(common_sources ${CMAKE_CURRENT_LIST_DIR}/config.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/helper.cpp)
|
||||
|
||||
set(common_headers
|
||||
${CMAKE_CURRENT_LIST_DIR}/config.hpp ${CMAKE_CURRENT_LIST_DIR}/defines.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/environment.hpp ${CMAKE_CURRENT_LIST_DIR}/join.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/log.hpp ${CMAKE_CURRENT_LIST_DIR}/helper.hpp)
|
||||
|
||||
add_library(rocprofiler-common-library STATIC)
|
||||
add_library(rocprofiler::rocprofiler-common-library ALIAS rocprofiler-common-library)
|
||||
|
||||
add_subdirectory(container)
|
||||
|
||||
target_sources(rocprofiler-common-library PRIVATE ${common_sources} ${common_headers})
|
||||
target_include_directories(rocprofiler-common-library
|
||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>)
|
||||
|
||||
target_link_libraries(
|
||||
rocprofiler-common-library
|
||||
PUBLIC rocprofiler::rocprofiler-amd-comgr
|
||||
$<BUILD_INTERFACE:rocprofiler::rocprofiler-build-flags>
|
||||
$<BUILD_INTERFACE:rocprofiler::rocprofiler-memcheck>
|
||||
$<BUILD_INTERFACE:rocprofiler::rocprofiler-stdcxxfs>
|
||||
$<BUILD_INTERFACE:rocprofiler::rocprofiler-dl>)
|
||||
set_target_properties(rocprofiler-common-library PROPERTIES OUTPUT_NAME
|
||||
rocprofiler-common)
|
||||
@@ -0,0 +1,433 @@
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#include "lib/common/config.hpp"
|
||||
#include "lib/common/environment.hpp"
|
||||
#include "lib/common/join.hpp"
|
||||
#include "lib/common/log.hpp"
|
||||
#include "lib/common/helper.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::time_t* launch_time = new std::time_t{std::time(nullptr)};
|
||||
|
||||
std::vector<std::string>
|
||||
read_command_line(pid_t _pid)
|
||||
{
|
||||
auto _cmdline = std::vector<std::string>{};
|
||||
auto fcmdline = std::stringstream{};
|
||||
fcmdline << "/proc/" << _pid << "/cmdline";
|
||||
auto ifs = std::ifstream{fcmdline.str().c_str()};
|
||||
if(ifs)
|
||||
{
|
||||
char cstr;
|
||||
std::string sarg;
|
||||
while(!ifs.eof())
|
||||
{
|
||||
ifs >> cstr;
|
||||
if(!ifs.eof())
|
||||
{
|
||||
if(cstr != '\0')
|
||||
{
|
||||
sarg += cstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cmdline.push_back(sarg);
|
||||
sarg = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
return _cmdline;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_local_datetime(const char* dt_format, std::time_t* dt_curr)
|
||||
{
|
||||
char mbstr[512];
|
||||
if(!dt_curr) dt_curr = launch_time;
|
||||
|
||||
if(std::strftime(mbstr, sizeof(mbstr), dt_format, std::localtime(dt_curr)) != 0)
|
||||
return std::string{mbstr};
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
inline bool
|
||||
not_is_space(int ch)
|
||||
{
|
||||
return std::isspace(ch) == 0;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
ltrim(std::string s, bool (*f)(int) = not_is_space)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
rtrim(std::string s, bool (*f)(int) = not_is_space)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
trim(std::string s, bool (*f)(int) = not_is_space)
|
||||
{
|
||||
ltrim(s, f);
|
||||
rtrim(s, f);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::vector<pid_t>
|
||||
get_siblings(pid_t _id = getppid())
|
||||
{
|
||||
auto _data = std::vector<pid_t>{};
|
||||
|
||||
std::ifstream _ifs{"/proc/" + std::to_string(_id) + "/task/" + std::to_string(_id) +
|
||||
"/children"};
|
||||
while(_ifs)
|
||||
{
|
||||
pid_t _n = 0;
|
||||
_ifs >> _n;
|
||||
if(!_ifs || _n <= 0) break;
|
||||
_data.emplace_back(_n);
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline auto
|
||||
get_num_siblings(pid_t _id = getppid())
|
||||
{
|
||||
return get_siblings(_id).size();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int
|
||||
get_mpi_size()
|
||||
{
|
||||
static int _v = get_env<int>("OMPI_COMM_WORLD_SIZE",
|
||||
get_env<int>("MV2_COMM_WORLD_SIZE", get_env<int>("MPI_SIZE", 0)));
|
||||
return _v;
|
||||
}
|
||||
|
||||
int
|
||||
get_mpi_rank()
|
||||
{
|
||||
static int _v = get_env<int>("OMPI_COMM_WORLD_RANK",
|
||||
get_env<int>("MV2_COMM_WORLD_RANK", get_env<int>("MPI_RANK", -1)));
|
||||
return _v;
|
||||
}
|
||||
|
||||
std::vector<output_key>
|
||||
output_keys(std::string _tag)
|
||||
{
|
||||
using strpair_t = std::pair<std::string, std::string>;
|
||||
|
||||
auto _cmdline = read_command_line(getpid());
|
||||
|
||||
if(_tag.empty() && !_cmdline.empty()) _tag = ::basename(_cmdline.front().c_str());
|
||||
|
||||
std::string _argv_string = {}; // entire argv cmd
|
||||
std::string _args_string = {}; // cmdline args
|
||||
std::string _argt_string = _tag; // prefix + cmdline args
|
||||
const std::string& _tag0_string = _tag; // only the basic prefix
|
||||
auto _options = std::vector<output_key>{};
|
||||
|
||||
auto _replace = [](auto& _v, const strpair_t& pitr) {
|
||||
auto pos = std::string::npos;
|
||||
while((pos = _v.find(pitr.first)) != std::string::npos)
|
||||
_v.replace(pos, pitr.first.length(), pitr.second);
|
||||
};
|
||||
|
||||
if(_cmdline.size() > 1 && _cmdline.at(1) == "--") _cmdline.erase(_cmdline.begin() + 1);
|
||||
|
||||
for(auto& itr : _cmdline)
|
||||
{
|
||||
itr = trim(itr);
|
||||
_replace(itr, {"/", "_"});
|
||||
while(!itr.empty() && itr.at(0) == '.')
|
||||
itr = itr.substr(1);
|
||||
while(!itr.empty() && itr.at(0) == '_')
|
||||
itr = itr.substr(1);
|
||||
}
|
||||
|
||||
if(!_cmdline.empty())
|
||||
{
|
||||
for(size_t i = 0; i < _cmdline.size(); ++i)
|
||||
{
|
||||
const auto _l = std::string{(i == 0) ? "" : "_"};
|
||||
auto _v = _cmdline.at(i);
|
||||
_argv_string += _l + _v;
|
||||
if(i > 0)
|
||||
{
|
||||
_argt_string += (i > 1) ? (_l + _v) : _v;
|
||||
_args_string += (i > 1) ? (_l + _v) : _v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* _launch_time = launch_time;
|
||||
auto _time_format = get_env<std::string>("ROCP_TIME_FORMAT", "%F_%H.%M");
|
||||
|
||||
auto _mpi_size = get_env<int>("OMPI_COMM_WORLD_SIZE", get_env<int>("MV2_COMM_WORLD_SIZE", 0));
|
||||
auto _mpi_rank = get_env<int>("OMPI_COMM_WORLD_RANK", get_env<int>("MV2_COMM_WORLD_RANK", -1));
|
||||
|
||||
auto _dmp_size = join("", (_mpi_size) > 0 ? _mpi_size : 1);
|
||||
auto _dmp_rank = join("", (_mpi_rank) > 0 ? _mpi_rank : 0);
|
||||
auto _proc_id = join("", getpid());
|
||||
auto _parent_id = join("", getppid());
|
||||
auto _pgroup_id = join("", getpgid(getpid()));
|
||||
auto _session_id = join("", getsid(getpid()));
|
||||
auto _proc_size = join("", get_num_siblings());
|
||||
auto _pwd_string = get_env<std::string>("PWD", ".");
|
||||
auto _slurm_job_id = get_env<std::string>("SLURM_JOB_ID", "0");
|
||||
auto _slurm_proc_id = get_env("SLURM_PROCID", _dmp_rank);
|
||||
auto _launch_string = get_local_datetime(_time_format.c_str(), _launch_time);
|
||||
|
||||
auto _uniq_id = _proc_id;
|
||||
if(get_env<int32_t>("SLURM_PROCID", -1) >= 0)
|
||||
{
|
||||
_uniq_id = _slurm_proc_id;
|
||||
}
|
||||
else if(_mpi_size > 0 || _mpi_rank >= 0)
|
||||
{
|
||||
_uniq_id = _dmp_rank;
|
||||
}
|
||||
|
||||
for(auto&& itr : std::initializer_list<output_key>{
|
||||
{"%argv%", _argv_string, "Entire command-line condensed into a single string"},
|
||||
{"%argt%",
|
||||
_argt_string,
|
||||
"Similar to `%argv%` except basename of first command line argument"},
|
||||
{"%args%", _args_string, "All command line arguments condensed into a single string"},
|
||||
{"%tag%", _tag0_string, "Basename of first command line argument"}})
|
||||
{
|
||||
_options.emplace_back(itr);
|
||||
}
|
||||
|
||||
if(!_cmdline.empty())
|
||||
{
|
||||
for(size_t i = 0; i < _cmdline.size(); ++i)
|
||||
{
|
||||
auto _v = _cmdline.at(i);
|
||||
_options.emplace_back(join("", "%arg", i, "%"), _v, join("", "Argument #", i));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto&& itr : std::initializer_list<output_key>{
|
||||
{"%pid%", _proc_id, "Process identifier"},
|
||||
{"%ppid%", _parent_id, "Parent process identifier"},
|
||||
{"%pgid%", _pgroup_id, "Process group identifier"},
|
||||
{"%psid%", _session_id, "Process session identifier"},
|
||||
{"%psize%", _proc_size, "Number of sibling process"},
|
||||
{"%job%", _slurm_job_id, "SLURM_JOB_ID env variable"},
|
||||
{"%rank%", _slurm_proc_id, "MPI/UPC++ rank"},
|
||||
{"%size%", _dmp_size, "MPI/UPC++ size"},
|
||||
{"%nid%", _uniq_id, "%rank% if possible, otherwise %pid%"},
|
||||
{"%launch_time%", _launch_string, "Data and/or time of run according to time format"},
|
||||
})
|
||||
{
|
||||
_options.emplace_back(itr);
|
||||
}
|
||||
|
||||
for(auto&& itr : std::initializer_list<output_key>{
|
||||
{"%p", _proc_id, "Shorthand for %pid%"},
|
||||
{"%j", _slurm_job_id, "Shorthand for %job%"},
|
||||
{"%r", _slurm_proc_id, "Shorthand for %rank%"},
|
||||
{"%s", _dmp_size, "Shorthand for %size"},
|
||||
})
|
||||
{
|
||||
_options.emplace_back(itr);
|
||||
}
|
||||
|
||||
return _options;
|
||||
}
|
||||
|
||||
std::string
|
||||
format(std::string _fpath, const std::string& _tag)
|
||||
{
|
||||
if(_fpath.find('%') == std::string::npos && _fpath.find('$') == std::string::npos)
|
||||
return _fpath;
|
||||
|
||||
auto _replace = [](auto& _v, const output_key& pitr) {
|
||||
auto pos = std::string::npos;
|
||||
while((pos = _v.find(pitr.key)) != std::string::npos)
|
||||
_v.replace(pos, pitr.key.length(), pitr.value);
|
||||
};
|
||||
|
||||
for(auto&& itr : output_keys(_tag))
|
||||
_replace(_fpath, itr);
|
||||
|
||||
// environment and configuration variables
|
||||
try
|
||||
{
|
||||
for(const auto& _expr : {std::string{"(.*)%(env|ENV)\\{([A-Z0-9_]+)\\}%(.*)"},
|
||||
std::string{"(.*)\\$(env|ENV)\\{([A-Z0-9_]+)\\}(.*)"}})
|
||||
{
|
||||
std::regex _re{_expr};
|
||||
std::string _cbeg = (_expr.find("(.*)%") == 0) ? "%" : "$";
|
||||
std::string _cend = (_expr.find("(.*)%") == 0) ? "}%" : "}";
|
||||
bool _is_env = (_expr.find("(env|ENV)") != std::string::npos);
|
||||
_cbeg += (_is_env) ? "env{" : "cfg{";
|
||||
while(std::regex_search(_fpath, _re))
|
||||
{
|
||||
auto _var = std::regex_replace(_fpath, _re, "$3");
|
||||
std::string _val = {};
|
||||
if(_is_env)
|
||||
{
|
||||
_val = get_env<std::string>(_var, "");
|
||||
}
|
||||
auto _beg = std::regex_replace(_fpath, _re, "$1");
|
||||
auto _end = std::regex_replace(_fpath, _re, "$4");
|
||||
_fpath = join("", _beg, _val, _end);
|
||||
}
|
||||
}
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s[rocprofiler][%s:%i] %s threw exception :: %s\n%s",
|
||||
log::color::dmesg(),
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
__FUNCTION__,
|
||||
_e.what(),
|
||||
log::color::end());
|
||||
}
|
||||
|
||||
// remove %arg<N>% where N >= argc
|
||||
try
|
||||
{
|
||||
std::regex _re{"(.*)%(arg[0-9]+)%([-/_]*)(.*)"};
|
||||
while(std::regex_search(_fpath, _re))
|
||||
_fpath = std::regex_replace(_fpath, _re, "$1$4");
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s[rocprofiler][%s:%i] %s threw exception :: %s\n%s",
|
||||
log::color::dmesg(),
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
__FUNCTION__,
|
||||
_e.what(),
|
||||
log::color::end());
|
||||
}
|
||||
|
||||
return _fpath;
|
||||
}
|
||||
|
||||
std::string
|
||||
compose_filename(const config& _cfg)
|
||||
{
|
||||
auto _output_path = _cfg.output_path;
|
||||
auto _output_file = _cfg.output_file;
|
||||
auto _output_ext = _cfg.output_ext;
|
||||
|
||||
if(_output_path.empty()) _output_path = ".";
|
||||
if(_cfg.mpi_size > 0)
|
||||
{
|
||||
if(_cfg.mpi_rank >= 0)
|
||||
{
|
||||
_output_file = join('.', _output_file, _cfg.mpi_rank);
|
||||
}
|
||||
else
|
||||
{
|
||||
_output_file = join('.', _output_file, getpid());
|
||||
}
|
||||
}
|
||||
if(!_output_ext.empty())
|
||||
{
|
||||
if(_output_ext.find('.') == std::string::npos) _output_ext.insert(0, ".");
|
||||
if(_output_file.length() < _output_ext.length() ||
|
||||
_output_file.find(_output_ext) != _output_file.length() - _output_ext.length())
|
||||
_output_file += _output_ext;
|
||||
}
|
||||
|
||||
// join <OUTPUT_PATH>/<OUTPUT_FILE> and replace any keys with values
|
||||
auto _prefix = format(std::filesystem::path{_output_path} / _output_file);
|
||||
|
||||
// return on empty
|
||||
if(_prefix.empty()) return std::string{};
|
||||
|
||||
// get the absolute path
|
||||
auto _fname = std::filesystem::absolute(std::filesystem::path{_prefix});
|
||||
|
||||
// create the directory if necessary
|
||||
auto _fname_path = _fname.parent_path();
|
||||
if(!std::filesystem::exists(_fname_path))
|
||||
std::filesystem::create_directories(_fname.parent_path());
|
||||
|
||||
return _fname.string();
|
||||
}
|
||||
|
||||
std::string
|
||||
format_name(std::string_view _name, const config& _cfg)
|
||||
{
|
||||
if(_cfg.demangle && _cfg.truncate)
|
||||
{
|
||||
return truncate_name(cxx_demangle(_name));
|
||||
}
|
||||
|
||||
if(_cfg.demangle)
|
||||
{
|
||||
return cxx_demangle(_name);
|
||||
}
|
||||
|
||||
if(_cfg.truncate)
|
||||
{
|
||||
return truncate_name(_name);
|
||||
}
|
||||
|
||||
return std::string{_name};
|
||||
}
|
||||
|
||||
void
|
||||
initialize()
|
||||
{
|
||||
(void) get_config<config_context::global>();
|
||||
}
|
||||
|
||||
output_key::output_key(std::string _key, std::string _val, std::string _desc)
|
||||
: key{std::move(_key)}
|
||||
, value{std::move(_val)}
|
||||
, description{std::move(_desc)}
|
||||
{}
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lib/common/environment.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
enum class config_context
|
||||
{
|
||||
global = 0,
|
||||
att_plugin,
|
||||
cli_plugin,
|
||||
ctf_plugin,
|
||||
file_plugin,
|
||||
perfetto_plugin,
|
||||
};
|
||||
|
||||
int
|
||||
get_mpi_size();
|
||||
int
|
||||
get_mpi_rank();
|
||||
|
||||
struct config
|
||||
{
|
||||
bool demangle = get_env("ROCP_DEMANGLE_KERNELS", true);
|
||||
bool truncate = get_env("ROCP_TRUNCATE_KERNELS", false);
|
||||
int mpi_size = get_mpi_size();
|
||||
int mpi_rank = get_mpi_rank();
|
||||
std::string output_path = get_env<std::string>("ROCP_OUTPUT_PATH", ".");
|
||||
std::string output_file = get_env<std::string>("ROCP_OUTPUT_FILE", "results");
|
||||
std::string output_ext = {};
|
||||
};
|
||||
|
||||
template <config_context ContextT = config_context::global>
|
||||
config&
|
||||
get_config()
|
||||
{
|
||||
if constexpr(ContextT == config_context::global)
|
||||
{
|
||||
static auto _v = config{};
|
||||
return _v;
|
||||
}
|
||||
else
|
||||
{
|
||||
// context specific config copied from global config
|
||||
static auto _v = get_config<config_context::global>();
|
||||
return _v;
|
||||
}
|
||||
}
|
||||
|
||||
struct output_key
|
||||
{
|
||||
output_key(std::string _key, std::string _val, std::string _desc = {});
|
||||
|
||||
operator std::pair<std::string, std::string>() const;
|
||||
|
||||
std::string key = {};
|
||||
std::string value = {};
|
||||
std::string description = {};
|
||||
};
|
||||
|
||||
std::vector<output_key>
|
||||
output_keys(std::string _tag = {});
|
||||
std::string
|
||||
compose_filename(const config&);
|
||||
std::string
|
||||
format(std::string _fpath, const std::string& _tag = {});
|
||||
std::string
|
||||
format_name(std::string_view _name, const config& = get_config<>());
|
||||
|
||||
void
|
||||
initialize();
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
set(containers_sources)
|
||||
|
||||
set(containers_headers atomic_ring_buffer.hpp c_array.hpp operators.hpp ring_buffer.hpp
|
||||
stable_vector.hpp static_vector.hpp)
|
||||
|
||||
set(containers_sources atomic_ring_buffer.cpp ring_buffer.cpp)
|
||||
|
||||
target_sources(rocprofiler-common-library PRIVATE ${containers_sources}
|
||||
${containers_headers})
|
||||
@@ -0,0 +1,297 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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 "atomic_ring_buffer.hpp"
|
||||
#include "lib/common/units.hpp"
|
||||
#include "lib/common/environment.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
namespace base
|
||||
{
|
||||
atomic_ring_buffer::atomic_ring_buffer(size_t _size, bool _use_mmap)
|
||||
{
|
||||
set_use_mmap(_use_mmap);
|
||||
init(_size);
|
||||
}
|
||||
|
||||
atomic_ring_buffer::~atomic_ring_buffer() { destroy(); }
|
||||
|
||||
atomic_ring_buffer::atomic_ring_buffer(const atomic_ring_buffer& rhs)
|
||||
: m_use_mmap{rhs.m_use_mmap}
|
||||
, m_use_mmap_explicit{rhs.m_use_mmap_explicit}
|
||||
{
|
||||
init(rhs.m_size);
|
||||
}
|
||||
|
||||
atomic_ring_buffer::atomic_ring_buffer(atomic_ring_buffer&& rhs) noexcept
|
||||
: m_init{rhs.m_init}
|
||||
, m_use_mmap{rhs.m_use_mmap}
|
||||
, m_use_mmap_explicit{rhs.m_use_mmap_explicit}
|
||||
, m_ptr{rhs.m_ptr}
|
||||
, m_size{rhs.m_size}
|
||||
, m_read_count{rhs.m_read_count.load()}
|
||||
, m_write_count{rhs.m_write_count.load()}
|
||||
{
|
||||
rhs.reset();
|
||||
}
|
||||
|
||||
atomic_ring_buffer&
|
||||
atomic_ring_buffer::operator=(const atomic_ring_buffer& rhs)
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
destroy();
|
||||
m_use_mmap = rhs.m_use_mmap;
|
||||
m_use_mmap_explicit = rhs.m_use_mmap_explicit;
|
||||
init(rhs.m_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
atomic_ring_buffer&
|
||||
atomic_ring_buffer::operator=(atomic_ring_buffer&& rhs) noexcept
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
destroy();
|
||||
m_init = rhs.m_init;
|
||||
m_use_mmap = rhs.m_use_mmap;
|
||||
m_use_mmap_explicit = rhs.m_use_mmap_explicit;
|
||||
m_ptr = rhs.m_ptr;
|
||||
m_size = rhs.m_size;
|
||||
m_read_count = rhs.m_read_count.load();
|
||||
m_write_count = rhs.m_write_count.load();
|
||||
rhs.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
atomic_ring_buffer::init(size_t _size)
|
||||
{
|
||||
if(m_init)
|
||||
throw std::runtime_error(
|
||||
"tim::base::atomic_ring_buffer::init(size_t) :: already initialized");
|
||||
|
||||
m_init = true;
|
||||
|
||||
// Round up to multiple of page size.
|
||||
_size += units::get_page_size() - ((_size % units::get_page_size() > 0)
|
||||
? (_size % units::get_page_size())
|
||||
: units::get_page_size());
|
||||
|
||||
if((_size % units::get_page_size()) > 0)
|
||||
{
|
||||
std::ostringstream _oss{};
|
||||
_oss << "Error! size is not a multiple of page size: " << _size << " % "
|
||||
<< units::get_page_size() << " = " << (_size % units::get_page_size());
|
||||
throw std::runtime_error(_oss.str());
|
||||
}
|
||||
|
||||
m_size = _size;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
|
||||
if(!m_use_mmap_explicit) m_use_mmap = get_env("ROCPROFILER_USE_MMAP", m_use_mmap);
|
||||
|
||||
if(!m_use_mmap)
|
||||
{
|
||||
m_ptr = malloc(m_size * sizeof(char));
|
||||
return;
|
||||
}
|
||||
|
||||
// Map twice the buffer size.
|
||||
if((m_ptr =
|
||||
mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) ==
|
||||
MAP_FAILED)
|
||||
{
|
||||
destroy();
|
||||
auto _err = errno;
|
||||
// TIMEMORY_PRINTF_FATAL(stderr, "Error using mmap: %s\n", strerror(_err));
|
||||
throw std::runtime_error(strerror(_err));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
atomic_ring_buffer::destroy()
|
||||
{
|
||||
if(m_ptr && m_init)
|
||||
{
|
||||
if(!m_use_mmap)
|
||||
{
|
||||
::free(m_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unmap the mapped virtual memmory.
|
||||
auto ret = munmap(m_ptr, m_size);
|
||||
if(ret != 0) perror("munmap");
|
||||
}
|
||||
}
|
||||
m_init = false;
|
||||
m_size = 0;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
atomic_ring_buffer::set_use_mmap(bool _v)
|
||||
{
|
||||
if(m_init)
|
||||
throw std::runtime_error("tim::base::atomic_ring_buffer::set_use_mmap(bool) cannot be "
|
||||
"called after initialization");
|
||||
m_use_mmap = _v;
|
||||
m_use_mmap_explicit = true;
|
||||
}
|
||||
|
||||
std::string
|
||||
atomic_ring_buffer::as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
ss << std::boolalpha << "is_initialized: " << is_initialized() << ", capacity: " << capacity()
|
||||
<< ", count: " << count() << ", free: " << free() << ", is_empty: " << is_empty()
|
||||
<< ", is_full: " << is_full() << ", pointer: " << m_ptr << ", read count: " << m_read_count
|
||||
<< ", write count: " << m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
atomic_ring_buffer::request(size_t _length)
|
||||
{
|
||||
if(m_ptr == nullptr || m_size == 0) return nullptr;
|
||||
|
||||
if(is_full()) return retrieve(_length);
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
size_t _write_count = 0;
|
||||
size_t _offset = 0;
|
||||
do
|
||||
{
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free()) return nullptr;
|
||||
|
||||
_offset = 0;
|
||||
_write_count = m_write_count.load();
|
||||
auto _modulo = m_size - (_write_count % m_size);
|
||||
if(_modulo < _length) _offset = _modulo;
|
||||
} while(!m_write_count.compare_exchange_strong(
|
||||
_write_count, _write_count + _length + _offset, std::memory_order_seq_cst));
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = write_ptr(_write_count);
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
atomic_ring_buffer::retrieve(size_t _length) const
|
||||
{
|
||||
if(m_ptr == nullptr || m_size == 0) return nullptr;
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
size_t _read_count = 0;
|
||||
size_t _offset = 0;
|
||||
do
|
||||
{
|
||||
if(_length > count()) return nullptr;
|
||||
_offset = 0;
|
||||
_read_count = m_read_count.load();
|
||||
auto _modulo = m_size - (_read_count % m_size);
|
||||
if(_modulo < _length) _offset = _modulo;
|
||||
} while(!m_read_count.compare_exchange_strong(
|
||||
_read_count, _read_count + _length + _offset, std::memory_order_seq_cst));
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = read_ptr(_read_count);
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
atomic_ring_buffer::reset()
|
||||
{
|
||||
m_init = false;
|
||||
m_size = 0;
|
||||
m_ptr = nullptr;
|
||||
m_read_count.store(0);
|
||||
m_write_count.store(0);
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
atomic_ring_buffer::save(std::fstream& _fs)
|
||||
{
|
||||
auto _read_count = m_read_count.load();
|
||||
auto _write_count = m_write_count.load();
|
||||
_fs.write(reinterpret_cast<char*>(&m_use_mmap), sizeof(m_use_mmap));
|
||||
_fs.write(reinterpret_cast<char*>(&m_use_mmap_explicit), sizeof(m_use_mmap_explicit));
|
||||
_fs.write(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
_fs.write(reinterpret_cast<char*>(&_read_count), sizeof(_read_count));
|
||||
_fs.write(reinterpret_cast<char*>(&_write_count), sizeof(_write_count));
|
||||
_fs.write(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
atomic_ring_buffer::load(std::fstream& _fs)
|
||||
{
|
||||
destroy();
|
||||
size_t _read_count = 0;
|
||||
size_t _write_count = 0;
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&m_use_mmap), sizeof(m_use_mmap));
|
||||
_fs.read(reinterpret_cast<char*>(&m_use_mmap_explicit), sizeof(m_use_mmap_explicit));
|
||||
_fs.read(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
|
||||
init(m_size);
|
||||
if(!m_ptr) m_ptr = malloc(m_size);
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&_read_count), sizeof(_read_count));
|
||||
_fs.read(reinterpret_cast<char*>(&_write_count), sizeof(_write_count));
|
||||
_fs.read(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
|
||||
m_read_count.store(_read_count);
|
||||
m_write_count.store(_write_count);
|
||||
}
|
||||
} // namespace base
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,426 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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 "lib/common/units.hpp"
|
||||
#include "lib/common/environment.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp>
|
||||
struct atomic_ring_buffer;
|
||||
//
|
||||
namespace base
|
||||
{
|
||||
/// \struct tim::base::atomic_ring_buffer
|
||||
/// \brief Ring buffer implementation, with support for mmap as backend (Linux only).
|
||||
struct atomic_ring_buffer
|
||||
{
|
||||
template <typename Tp>
|
||||
friend struct container::atomic_ring_buffer;
|
||||
|
||||
atomic_ring_buffer() = default;
|
||||
explicit atomic_ring_buffer(bool _use_mmap) { set_use_mmap(_use_mmap); }
|
||||
explicit atomic_ring_buffer(size_t _size) { init(_size); }
|
||||
atomic_ring_buffer(size_t _size, bool _use_mmap);
|
||||
|
||||
~atomic_ring_buffer();
|
||||
|
||||
atomic_ring_buffer(const atomic_ring_buffer&);
|
||||
atomic_ring_buffer& operator=(const atomic_ring_buffer&);
|
||||
|
||||
atomic_ring_buffer(atomic_ring_buffer&&) noexcept;
|
||||
atomic_ring_buffer& operator=(atomic_ring_buffer&&) noexcept;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return m_init; }
|
||||
|
||||
/// Get the total number of bytes supported
|
||||
size_t capacity() const { return m_size; }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t size);
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy();
|
||||
|
||||
/// Request a pointer for writing at least \param n bytes.
|
||||
void* request(size_t n);
|
||||
|
||||
/// Retrieve a pointer for reading at least \param n bytes.
|
||||
void* retrieve(size_t n) const;
|
||||
|
||||
/// Write class-type data to buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Write non-class-type data to buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Request a pointer to an allocation. This is similar to a "write" except the
|
||||
/// memory is uninitialized. Typically used by allocators. If Tp is a class type,
|
||||
/// be sure to use a placement new instead of a memcpy.
|
||||
template <typename Tp>
|
||||
Tp* request();
|
||||
|
||||
/// Read class-type data from buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* _dest,
|
||||
std::enable_if_t<std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Read non-class-type data from buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* _dest,
|
||||
std::enable_if_t<!std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Retrieve a pointer to the head allocation (read).
|
||||
template <typename Tp>
|
||||
Tp* retrieve() const;
|
||||
|
||||
/// Returns number of bytes currently held by the buffer.
|
||||
size_t count() const { return (m_write_count - m_read_count); }
|
||||
|
||||
/// Returns how many bytes are availiable in the buffer.
|
||||
size_t free() const { return (m_size - count()); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return (count() == 0); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (count() == m_size); }
|
||||
|
||||
/// explicitly configure to use mmap if avail
|
||||
void set_use_mmap(bool);
|
||||
|
||||
/// query whether using mmap
|
||||
bool get_use_mmap() const { return m_use_mmap; }
|
||||
|
||||
std::string as_string() const;
|
||||
|
||||
void save(std::fstream& _fs);
|
||||
void load(std::fstream& _fs);
|
||||
|
||||
private:
|
||||
/// Returns the current write pointer.
|
||||
void* write_ptr(size_t _write_count) const
|
||||
{
|
||||
return static_cast<char*>(m_ptr) + (_write_count % m_size);
|
||||
}
|
||||
|
||||
/// Returns the current read pointer.
|
||||
void* read_ptr(size_t _read_count) const
|
||||
{
|
||||
return static_cast<char*>(m_ptr) + (_read_count % m_size);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
bool m_init = false;
|
||||
bool m_use_mmap = true;
|
||||
bool m_use_mmap_explicit = false;
|
||||
void* m_ptr = nullptr;
|
||||
size_t m_size = 0;
|
||||
mutable std::atomic<size_t> m_read_count = 0;
|
||||
std::atomic<size_t> m_write_count = 0;
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
atomic_ring_buffer::write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
void* _out_p = request(_length);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// Copy in.
|
||||
new(_out_p) Tp{std::move(*in)};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
return {_length, _out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
atomic_ring_buffer::write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
void* _out_p = request(_length);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// Copy in.
|
||||
memcpy(_out_p, in, _length);
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
return {_length, _out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
atomic_ring_buffer::request()
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
return request(sizeof(Tp));
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
atomic_ring_buffer::read(Tp* _dest, std::enable_if_t<std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || _dest == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
void* _out_p = retrieve(_length);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
*_dest = *in;
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
atomic_ring_buffer::read(Tp* _dest, std::enable_if_t<!std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || _dest == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
void* _out_p = retrieve(_length);
|
||||
|
||||
if(_out_p == nullptr) return {0, nullptr};
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(_out_p);
|
||||
|
||||
using Up = typename std::remove_const<Tp>::type;
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
Up* _out = const_cast<Up*>(_dest);
|
||||
memcpy(_out, in, _length);
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
atomic_ring_buffer::retrieve() const
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
return retrieve(sizeof(Tp));
|
||||
}
|
||||
//
|
||||
} // namespace base
|
||||
//
|
||||
/// \struct tim::data_storage::atomic_ring_buffer
|
||||
/// \brief Ring buffer wrapper around \ref tim::base::atomic_ring_buffer for data of type
|
||||
/// Tp. If the data object size is larger than the page size (typically 4KB), behavior is
|
||||
/// undefined. During initialization, one requests a minimum number of objects and the
|
||||
/// buffer will support that number of object + the remainder of the page, e.g. if a page
|
||||
/// is 1000 bytes, the object is 1 byte, and the buffer is requested to support 1500
|
||||
/// objects, then an allocation supporting 2000 objects (i.e. 2 pages) will be created.
|
||||
template <typename Tp>
|
||||
struct atomic_ring_buffer : private base::atomic_ring_buffer
|
||||
{
|
||||
using base_type = base::atomic_ring_buffer;
|
||||
|
||||
static size_t get_items_per_page();
|
||||
|
||||
atomic_ring_buffer() = default;
|
||||
~atomic_ring_buffer() = default;
|
||||
|
||||
explicit atomic_ring_buffer(bool _use_mmap)
|
||||
: base_type{_use_mmap}
|
||||
{}
|
||||
|
||||
explicit atomic_ring_buffer(size_t _size)
|
||||
: base_type{_size * sizeof(Tp)}
|
||||
{}
|
||||
|
||||
atomic_ring_buffer(size_t _size, bool _use_mmap)
|
||||
: base_type{_size * sizeof(Tp), _use_mmap}
|
||||
{}
|
||||
|
||||
atomic_ring_buffer(const atomic_ring_buffer&);
|
||||
atomic_ring_buffer(atomic_ring_buffer&&) noexcept = default;
|
||||
|
||||
atomic_ring_buffer& operator=(const atomic_ring_buffer&);
|
||||
atomic_ring_buffer& operator=(atomic_ring_buffer&&) noexcept = default;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return base_type::is_initialized(); }
|
||||
|
||||
/// Get the total number of Tp instances supported
|
||||
size_t capacity() const { return (base_type::capacity()) / sizeof(Tp); }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t _size) { base_type::init(_size * sizeof(Tp)); }
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy() { base_type::destroy(); }
|
||||
|
||||
/// Write data to buffer.
|
||||
size_t data_size() const { return sizeof(Tp); }
|
||||
|
||||
/// Write data to buffer. Return pointer to location of write
|
||||
Tp* write(Tp* in) { return base_type::write<Tp>(in).second; }
|
||||
|
||||
/// Read data from buffer. Return pointer to location of read
|
||||
Tp* read(Tp* _dest) const { return base_type::read<Tp>(_dest).second; }
|
||||
|
||||
/// Get an uninitialized address at tail of buffer.
|
||||
Tp* request() { return base_type::request<Tp>(); }
|
||||
|
||||
/// Read data from head of buffer.
|
||||
Tp* retrieve() { return base_type::retrieve<Tp>(); }
|
||||
|
||||
/// Returns number of Tp instances currently held by the buffer.
|
||||
size_t count() const { return (base_type::count()) / sizeof(Tp); }
|
||||
|
||||
/// Returns how many Tp instances are availiable in the buffer.
|
||||
size_t free() const { return (base_type::free()) / sizeof(Tp); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return base_type::is_empty(); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (base_type::free() < sizeof(Tp)); }
|
||||
|
||||
template <typename... Args>
|
||||
auto emplace(Args&&... args)
|
||||
{
|
||||
Tp _obj{std::forward<Args>(args)...};
|
||||
return write(&_obj);
|
||||
}
|
||||
|
||||
using base_type::get_use_mmap;
|
||||
using base_type::load;
|
||||
using base_type::save;
|
||||
using base_type::set_use_mmap;
|
||||
|
||||
std::string as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
size_t _w = std::log10(base_type::capacity()) + 1;
|
||||
ss << std::boolalpha << std::right << "data size: " << std::setw(_w) << data_size()
|
||||
<< " B, is_initialized: " << std::setw(5) << is_initialized()
|
||||
<< ", is_empty: " << std::setw(5) << is_empty() << ", is_full: " << std::setw(5)
|
||||
<< is_full() << ", capacity: " << std::setw(_w) << capacity()
|
||||
<< ", count: " << std::setw(_w) << count() << ", free: " << std::setw(_w) << free()
|
||||
<< ", raw capacity: " << std::setw(_w) << base_type::capacity()
|
||||
<< " B, raw count: " << std::setw(_w) << base_type::count()
|
||||
<< " B, raw free: " << std::setw(_w) << base_type::free()
|
||||
<< " B, pointer: " << std::setw(15) << base_type::m_ptr
|
||||
<< ", raw read count: " << std::setw(_w) << base_type::m_read_count
|
||||
<< ", raw write count: " << std::setw(_w) << base_type::m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const atomic_ring_buffer& obj)
|
||||
{
|
||||
return os << obj.as_string();
|
||||
}
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
size_t
|
||||
atomic_ring_buffer<Tp>::get_items_per_page()
|
||||
{
|
||||
return std::max<size_t>(units::get_page_size() / sizeof(Tp), 1);
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
atomic_ring_buffer<Tp>::atomic_ring_buffer(const atomic_ring_buffer<Tp>& rhs)
|
||||
: base_type{rhs}
|
||||
{
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr(m_read_count)) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
atomic_ring_buffer<Tp>&
|
||||
atomic_ring_buffer<Tp>::operator=(const atomic_ring_buffer<Tp>& rhs)
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
|
||||
base_type::operator=(rhs);
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr(m_read_count)) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
//
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,136 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 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 <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp>
|
||||
struct c_array
|
||||
{
|
||||
// Construct an array wrapper from a base pointer and array size
|
||||
c_array(Tp* _base, size_t _size)
|
||||
: m_base{_base}
|
||||
, m_size{_size}
|
||||
{}
|
||||
|
||||
~c_array() = default;
|
||||
c_array(const c_array&) = default;
|
||||
c_array& operator=(const c_array&) = default;
|
||||
c_array& operator=(c_array&&) noexcept = default;
|
||||
|
||||
// Get the size of the wrapped array
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
// Access an element by index
|
||||
Tp& operator[](size_t i) { return m_base[i]; }
|
||||
|
||||
// Access an element by index
|
||||
const Tp& operator[](size_t i) const { return m_base[i]; }
|
||||
|
||||
// Access an element by index with bounds check
|
||||
Tp& at(size_t i)
|
||||
{
|
||||
if(i < m_size) return m_base[i];
|
||||
throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) +
|
||||
" exceeds size " + std::to_string(m_size));
|
||||
}
|
||||
|
||||
// Access an element by index with bounds check
|
||||
const Tp& at(size_t i) const
|
||||
{
|
||||
if(i < m_size) return m_base[i];
|
||||
throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) +
|
||||
" exceeds size " + std::to_string(m_size));
|
||||
}
|
||||
|
||||
// Get a slice of this array, from a start index (inclusive) to end index (exclusive)
|
||||
c_array<Tp> slice(size_t start, size_t end) { return c_array<Tp>(&m_base[start], end - start); }
|
||||
|
||||
void pop_front()
|
||||
{
|
||||
++m_base;
|
||||
--m_size;
|
||||
}
|
||||
|
||||
void pop_back() { --m_size; }
|
||||
|
||||
operator Tp*() const { return m_base; }
|
||||
|
||||
// Iterator class for convenient range-based for loop support
|
||||
template <typename Up>
|
||||
struct iterator
|
||||
{
|
||||
// Start the iterator at a given pointer
|
||||
explicit iterator(Tp* p)
|
||||
: m_ptr{p}
|
||||
{}
|
||||
|
||||
// Advance to the next element
|
||||
void operator++() { ++m_ptr; }
|
||||
void operator++(int) { m_ptr++; }
|
||||
|
||||
// Get the current element
|
||||
Up& operator*() const { return *m_ptr; }
|
||||
|
||||
// Compare iterators
|
||||
bool operator==(const iterator& rhs) const { return m_ptr == rhs.m_ptr; }
|
||||
bool operator!=(const iterator& rhs) const { return m_ptr != rhs.m_ptr; }
|
||||
|
||||
private:
|
||||
Tp* m_ptr = nullptr;
|
||||
};
|
||||
|
||||
// Get an iterator positioned at the beginning of the wrapped array
|
||||
iterator<Tp> begin() { return iterator<Tp>{m_base}; }
|
||||
iterator<const Tp> begin() const { return iterator<const Tp>{m_base}; }
|
||||
|
||||
// Get an iterator positioned at the end of the wrapped array
|
||||
iterator<Tp> end() { return iterator<Tp>{&m_base[m_size]}; }
|
||||
iterator<const Tp> end() const { return iterator<const Tp>{&m_base[m_size]}; }
|
||||
|
||||
private:
|
||||
Tp* m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
// Function for automatic template argument deduction
|
||||
template <typename Tp>
|
||||
c_array<Tp>
|
||||
wrap_c_array(Tp* base, size_t size)
|
||||
{
|
||||
return c_array<Tp>(base, size);
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,239 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 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 <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#define ROCPROFILER_IMPORT_TEMPLATE2(template_name)
|
||||
#define ROCPROFILER_IMPORT_TEMPLATE1(template_name)
|
||||
|
||||
// Import a 2-type-argument operator template into boost (if necessary) and
|
||||
// provide a specialization of 'is_chained_base<>' for it.
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE2(template_name2) \
|
||||
ROCPROFILER_IMPORT_TEMPLATE2(template_name2) \
|
||||
template <typename T, typename U, typename B> \
|
||||
struct is_chained_base<::rocprofiler::container::template_name2<T, U, B>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::container::true_t; \
|
||||
};
|
||||
|
||||
// Import a 1-type-argument operator template into boost (if necessary) and
|
||||
// provide a specialization of 'is_chained_base<>' for it.
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE1(template_name1) \
|
||||
ROCPROFILER_IMPORT_TEMPLATE1(template_name1) \
|
||||
template <typename T, typename B> \
|
||||
struct is_chained_base<::rocprofiler::container::template_name1<T, B>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::container::true_t; \
|
||||
};
|
||||
|
||||
#define ROCPROFILER_OPERATOR_TEMPLATE(template_name) \
|
||||
template <typename T, \
|
||||
typename U = T, \
|
||||
typename B = empty_base<T>, \
|
||||
typename O = typename is_chained_base<U>::value> \
|
||||
struct template_name; \
|
||||
\
|
||||
template <typename T, typename U, typename B> \
|
||||
struct template_name<T, U, B, false_t> : template_name##2 < T \
|
||||
, U \
|
||||
, B > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename U> \
|
||||
struct template_name<T, U, empty_base<T>, true_t> : template_name##1 < T \
|
||||
, U > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename B> \
|
||||
struct template_name<T, T, B, false_t> : template_name##1 < T \
|
||||
, B > \
|
||||
{}; \
|
||||
\
|
||||
template <typename T, typename U, typename B, typename O> \
|
||||
struct is_chained_base<template_name<T, U, B, O>> \
|
||||
{ \
|
||||
using value = ::rocprofiler::container::true_t; \
|
||||
}; \
|
||||
\
|
||||
ROCPROFILER_OPERATOR_TEMPLATE2(template_name##2) \
|
||||
ROCPROFILER_OPERATOR_TEMPLATE1(template_name##1)
|
||||
|
||||
#define ROCPROFILER_BINARY_OPERATOR_COMMUTATIVE(NAME, OP) \
|
||||
template <typename T, typename U, typename B = empty_base<T>> \
|
||||
struct NAME##2 : B{friend T operator OP(T lhs, const U& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
friend T operator OP(const U& lhs, T rhs) { return rhs OP## = lhs; } \
|
||||
} \
|
||||
; \
|
||||
\
|
||||
template <typename T, typename B = empty_base<T>> \
|
||||
struct NAME##1 : B{friend T operator OP(T lhs, const T& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
} \
|
||||
;
|
||||
|
||||
#define ROCPROFILER_BINARY_OPERATOR_NON_COMMUTATIVE(NAME, OP) \
|
||||
template <typename T, typename U, typename B = empty_base<T>> \
|
||||
struct NAME##2 : B{friend T operator OP(T lhs, const U& rhs){return lhs OP## = rhs; \
|
||||
} \
|
||||
} \
|
||||
;
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
struct true_t
|
||||
{};
|
||||
|
||||
struct false_t
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
class empty_base
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct is_chained_base
|
||||
{
|
||||
using value = true_t;
|
||||
};
|
||||
|
||||
ROCPROFILER_BINARY_OPERATOR_COMMUTATIVE(addable, +)
|
||||
ROCPROFILER_BINARY_OPERATOR_NON_COMMUTATIVE(subtractable, -)
|
||||
|
||||
ROCPROFILER_OPERATOR_TEMPLATE(addable)
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct incrementable : B
|
||||
{
|
||||
friend T operator++(T& x, int)
|
||||
{
|
||||
incrementable_type nrv(x);
|
||||
++x;
|
||||
return nrv;
|
||||
}
|
||||
|
||||
private: // The use of this typedef works around a Borland bug
|
||||
typedef T incrementable_type;
|
||||
};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct decrementable : B
|
||||
{
|
||||
friend T operator--(T& x, int)
|
||||
{
|
||||
decrementable_type nrv(x);
|
||||
--x;
|
||||
return nrv;
|
||||
}
|
||||
|
||||
private: // The use of this typedef works around a Borland bug
|
||||
typedef T decrementable_type;
|
||||
};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct dereferenceable : B
|
||||
{
|
||||
P operator->() const { return ::std::addressof(*static_cast<const T&>(*this)); }
|
||||
};
|
||||
|
||||
template <typename T, typename I, typename R, typename B = empty_base<T>>
|
||||
struct indexable : B
|
||||
{
|
||||
R operator[](I n) const { return *(static_cast<const T&>(*this) + n); }
|
||||
};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct equality_comparable1 : B
|
||||
{
|
||||
friend bool operator!=(const T& x, const T& y) { return !static_cast<bool>(x == y); }
|
||||
};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct input_iteratable : equality_comparable1<T, incrementable<T, dereferenceable<T, P, B>>>
|
||||
{};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct output_iteratable : incrementable<T, B>
|
||||
{};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct forward_iteratable : input_iteratable<T, P, B>
|
||||
{};
|
||||
|
||||
template <typename T, typename P, typename B = empty_base<T>>
|
||||
struct bidirectional_iteratable : forward_iteratable<T, P, decrementable<T, B>>
|
||||
{};
|
||||
|
||||
// template <typename T, typename U, typename B = empty_base<T>>
|
||||
// struct subtractable2;
|
||||
|
||||
template <typename T, typename U, typename B = empty_base<T>>
|
||||
struct additive2 : addable2<T, U, subtractable2<T, U, B>>
|
||||
{};
|
||||
|
||||
template <typename T, typename B = empty_base<T>>
|
||||
struct less_than_comparable1 : B
|
||||
{
|
||||
friend bool operator>(const T& x, const T& y) { return y < x; }
|
||||
friend bool operator<=(const T& x, const T& y) { return !static_cast<bool>(y < x); }
|
||||
friend bool operator>=(const T& x, const T& y) { return !static_cast<bool>(x < y); }
|
||||
};
|
||||
|
||||
// To avoid repeated derivation from equality_comparable,
|
||||
// which is an indirect base typename of bidirectional_iterable,
|
||||
// random_access_iteratable must not be derived from totally_ordered1
|
||||
// but from less_than_comparable1 only. (Helmut Zeisel, 02-Dec-2001)
|
||||
template <typename T, typename P, typename D, typename R, typename B = empty_base<T>>
|
||||
struct random_access_iteratable
|
||||
: bidirectional_iteratable<T, P, less_than_comparable1<T, additive2<T, D, indexable<T, D, R, B>>>>
|
||||
{};
|
||||
|
||||
template <typename CategoryT,
|
||||
typename Tp,
|
||||
typename DistanceT = std::ptrdiff_t,
|
||||
typename PointerT = Tp*,
|
||||
typename ReferenceT = Tp&>
|
||||
struct iterator_helper
|
||||
{
|
||||
using iterator_category = CategoryT;
|
||||
using value_type = Tp;
|
||||
using difference_type = DistanceT;
|
||||
using pointer = PointerT;
|
||||
using reference = ReferenceT;
|
||||
};
|
||||
|
||||
template <typename T, typename V, typename D = std::ptrdiff_t, typename P = V*, typename R = V&>
|
||||
struct random_access_iterator_helper
|
||||
: random_access_iteratable<T, P, D, R, iterator_helper<std::random_access_iterator_tag, V, D, P, R>>
|
||||
{
|
||||
friend D requires_difference_operator(const T& x, const T& y) { return x - y; }
|
||||
}; // random_access_iterator_helper
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,289 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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 "ring_buffer.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
namespace base
|
||||
{
|
||||
ring_buffer::ring_buffer(size_t _size, bool _use_mmap)
|
||||
{
|
||||
set_use_mmap(_use_mmap);
|
||||
init(_size);
|
||||
}
|
||||
|
||||
ring_buffer::~ring_buffer() { destroy(); }
|
||||
|
||||
ring_buffer::ring_buffer(const ring_buffer& rhs)
|
||||
: m_use_mmap{rhs.m_use_mmap}
|
||||
, m_use_mmap_explicit{rhs.m_use_mmap_explicit}
|
||||
{
|
||||
init(rhs.m_size);
|
||||
}
|
||||
|
||||
ring_buffer::ring_buffer(ring_buffer&& rhs) noexcept
|
||||
: m_init{rhs.m_init}
|
||||
, m_use_mmap{rhs.m_use_mmap}
|
||||
, m_use_mmap_explicit{rhs.m_use_mmap_explicit}
|
||||
, m_ptr{rhs.m_ptr}
|
||||
, m_size{rhs.m_size}
|
||||
, m_read_count{rhs.m_read_count}
|
||||
, m_write_count{rhs.m_write_count}
|
||||
{
|
||||
rhs.reset();
|
||||
}
|
||||
|
||||
ring_buffer&
|
||||
ring_buffer::operator=(const ring_buffer& rhs)
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
destroy();
|
||||
m_use_mmap = rhs.m_use_mmap;
|
||||
m_use_mmap_explicit = rhs.m_use_mmap_explicit;
|
||||
init(rhs.m_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ring_buffer&
|
||||
ring_buffer::operator=(ring_buffer&& rhs) noexcept
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
destroy();
|
||||
m_init = rhs.m_init;
|
||||
m_use_mmap = rhs.m_use_mmap;
|
||||
m_use_mmap_explicit = rhs.m_use_mmap_explicit;
|
||||
m_ptr = rhs.m_ptr;
|
||||
m_size = rhs.m_size;
|
||||
m_read_count = rhs.m_read_count;
|
||||
m_write_count = rhs.m_write_count;
|
||||
rhs.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
ring_buffer::init(size_t _size)
|
||||
{
|
||||
if(m_init)
|
||||
throw std::runtime_error("tim::base::ring_buffer::init(size_t) :: already initialized");
|
||||
|
||||
m_init = true;
|
||||
|
||||
// Round up to multiple of page size.
|
||||
_size += units::get_page_size() - ((_size % units::get_page_size() > 0)
|
||||
? (_size % units::get_page_size())
|
||||
: units::get_page_size());
|
||||
|
||||
if((_size % units::get_page_size()) > 0)
|
||||
{
|
||||
std::ostringstream _oss{};
|
||||
_oss << "Error! size is not a multiple of page size: " << _size << " % "
|
||||
<< units::get_page_size() << " = " << (_size % units::get_page_size());
|
||||
throw std::runtime_error(_oss.str());
|
||||
}
|
||||
|
||||
m_size = _size;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
|
||||
if(!m_use_mmap_explicit) m_use_mmap = get_env("ROCPROFILER_USE_MMAP", m_use_mmap);
|
||||
|
||||
if(!m_use_mmap)
|
||||
{
|
||||
m_ptr = malloc(m_size * sizeof(char));
|
||||
return;
|
||||
}
|
||||
|
||||
// Map twice the buffer size.
|
||||
if((m_ptr =
|
||||
mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) ==
|
||||
MAP_FAILED)
|
||||
{
|
||||
destroy();
|
||||
auto _err = errno;
|
||||
// TIMEMORY_PRINTF_FATAL(stderr, "Error using mmap: %s\n", strerror(_err));
|
||||
throw std::runtime_error(strerror(_err));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ring_buffer::destroy()
|
||||
{
|
||||
if(m_ptr && m_init)
|
||||
{
|
||||
if(!m_use_mmap)
|
||||
{
|
||||
::free(m_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unmap the mapped virtual memmory.
|
||||
auto ret = munmap(m_ptr, m_size);
|
||||
if(ret != 0) perror("munmap");
|
||||
}
|
||||
}
|
||||
m_init = false;
|
||||
m_size = 0;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ring_buffer::set_use_mmap(bool _v)
|
||||
{
|
||||
if(!m_init)
|
||||
{
|
||||
m_use_mmap = _v;
|
||||
m_use_mmap_explicit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("tim::base::ring_buffer::set_use_mmap(bool) cannot be "
|
||||
"called after initialization");
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
ring_buffer::as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
ss << std::boolalpha << "is_initialized: " << is_initialized() << ", capacity: " << capacity()
|
||||
<< ", count: " << count() << ", free: " << free() << ", is_empty: " << is_empty()
|
||||
<< ", is_full: " << is_full() << ", pointer: " << m_ptr << ", read count: " << m_read_count
|
||||
<< ", write count: " << m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
ring_buffer::request(size_t _length)
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free())
|
||||
throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
|
||||
"to avoid data corruption");
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_write_count % m_size);
|
||||
if(_modulo < _length) m_write_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = write_ptr();
|
||||
|
||||
// Update write count
|
||||
m_write_count += _length;
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
void*
|
||||
ring_buffer::retrieve(size_t _length)
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > count()) throw std::runtime_error("ring buffer is empty");
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_read_count % m_size);
|
||||
if(_modulo < _length) m_read_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
void* _out = read_ptr();
|
||||
|
||||
// Update write count
|
||||
m_read_count += _length;
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
|
||||
size_t
|
||||
ring_buffer::rewind(size_t n) const
|
||||
{
|
||||
if(n > m_read_count) n = m_read_count;
|
||||
m_read_count -= n;
|
||||
return n;
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::reset()
|
||||
{
|
||||
m_init = false;
|
||||
m_ptr = nullptr;
|
||||
m_size = 0;
|
||||
m_read_count = 0;
|
||||
m_write_count = 0;
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::save(std::fstream& _fs)
|
||||
{
|
||||
_fs.write(reinterpret_cast<char*>(&m_use_mmap), sizeof(m_use_mmap));
|
||||
_fs.write(reinterpret_cast<char*>(&m_use_mmap_explicit), sizeof(m_use_mmap_explicit));
|
||||
_fs.write(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
_fs.write(reinterpret_cast<char*>(&m_read_count), sizeof(m_read_count));
|
||||
_fs.write(reinterpret_cast<char*>(&m_write_count), sizeof(m_write_count));
|
||||
_fs.write(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
}
|
||||
//
|
||||
|
||||
void
|
||||
ring_buffer::load(std::fstream& _fs)
|
||||
{
|
||||
destroy();
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&m_use_mmap), sizeof(m_use_mmap));
|
||||
_fs.read(reinterpret_cast<char*>(&m_use_mmap_explicit), sizeof(m_use_mmap_explicit));
|
||||
_fs.read(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
|
||||
init(m_size);
|
||||
if(!m_ptr) m_ptr = malloc(m_size);
|
||||
|
||||
_fs.read(reinterpret_cast<char*>(&m_read_count), sizeof(m_read_count));
|
||||
_fs.read(reinterpret_cast<char*>(&m_write_count), sizeof(m_write_count));
|
||||
_fs.read(reinterpret_cast<char*>(m_ptr), m_size * sizeof(char));
|
||||
}
|
||||
} // namespace base
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,495 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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 "lib/common/environment.hpp"
|
||||
#include "lib/common/units.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp>
|
||||
struct ring_buffer;
|
||||
//
|
||||
namespace base
|
||||
{
|
||||
/// \struct tim::base::ring_buffer
|
||||
/// \brief Ring buffer implementation, with support for mmap as backend (Linux only).
|
||||
struct ring_buffer
|
||||
{
|
||||
template <typename Tp>
|
||||
friend struct container::ring_buffer;
|
||||
|
||||
ring_buffer() = default;
|
||||
explicit ring_buffer(bool _use_mmap) { set_use_mmap(_use_mmap); }
|
||||
explicit ring_buffer(size_t _size) { init(_size); }
|
||||
ring_buffer(size_t _size, bool _use_mmap);
|
||||
|
||||
~ring_buffer();
|
||||
|
||||
ring_buffer(const ring_buffer&);
|
||||
ring_buffer& operator=(const ring_buffer&);
|
||||
|
||||
ring_buffer(ring_buffer&&) noexcept;
|
||||
ring_buffer& operator=(ring_buffer&&) noexcept;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return m_init; }
|
||||
|
||||
/// Get the total number of bytes supported
|
||||
size_t capacity() const { return m_size; }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t size);
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy();
|
||||
|
||||
/// Write class-type data to buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Write non-class-type data to buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int> = 0);
|
||||
|
||||
/// Request a pointer to an allocation. This is similar to a "write" except the
|
||||
/// memory is uninitialized. Typically used by allocators. If Tp is a class type,
|
||||
/// be sure to use a placement new instead of a memcpy.
|
||||
template <typename Tp>
|
||||
Tp* request();
|
||||
|
||||
/// Request a pointer to an allocation for at least \param n bytes.
|
||||
void* request(size_t n);
|
||||
|
||||
/// Read class-type data from buffer (uses placement new).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* out, std::enable_if_t<std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Read non-class-type data from buffer (uses memcpy).
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*> read(Tp* out,
|
||||
std::enable_if_t<!std::is_class<Tp>::value, int> = 0) const;
|
||||
|
||||
/// Retrieve a pointer to the head allocation (read).
|
||||
template <typename Tp>
|
||||
Tp* retrieve();
|
||||
|
||||
/// Retrieve a pointer to the head allocation of at least \param n bytes (read).
|
||||
void* retrieve(size_t n);
|
||||
|
||||
/// Returns number of bytes currently held by the buffer.
|
||||
size_t count() const { return (m_write_count - m_read_count); }
|
||||
|
||||
/// Returns how many bytes are availiable in the buffer.
|
||||
size_t free() const { return (m_size - count()); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return (count() == 0); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (count() == m_size); }
|
||||
|
||||
/// Rewind the read position n bytes
|
||||
size_t rewind(size_t n) const;
|
||||
|
||||
/// explicitly configure to use mmap if avail
|
||||
void set_use_mmap(bool);
|
||||
|
||||
/// query whether using mmap
|
||||
bool get_use_mmap() const { return m_use_mmap; }
|
||||
|
||||
std::string as_string() const;
|
||||
|
||||
void save(std::fstream& _fs);
|
||||
void load(std::fstream& _fs);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const ring_buffer& obj)
|
||||
{
|
||||
return os << obj.as_string();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Returns the current write pointer.
|
||||
void* write_ptr() const { return static_cast<char*>(m_ptr) + (m_write_count % m_size); }
|
||||
|
||||
/// Returns the current read pointer.
|
||||
void* read_ptr() const { return static_cast<char*>(m_ptr) + (m_read_count % m_size); }
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
bool m_init = false;
|
||||
bool m_use_mmap = true;
|
||||
bool m_use_mmap_explicit = false;
|
||||
void* m_ptr = nullptr;
|
||||
size_t m_size = 0;
|
||||
mutable size_t m_read_count = 0;
|
||||
size_t m_write_count = 0;
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::write(Tp* in, std::enable_if_t<std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free())
|
||||
throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
|
||||
"to avoid data corruption");
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_write_count % m_size);
|
||||
if(_modulo < _length) m_write_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* out = reinterpret_cast<Tp*>(write_ptr());
|
||||
|
||||
// Copy in.
|
||||
new((void*) out) Tp{std::move(*in)};
|
||||
|
||||
// Update write count
|
||||
m_write_count += _length;
|
||||
|
||||
return {_length, out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::write(Tp* in, std::enable_if_t<!std::is_class<Tp>::value, int>)
|
||||
{
|
||||
if(in == nullptr || m_ptr == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free())
|
||||
throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
|
||||
"to avoid data corruption");
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_write_count % m_size);
|
||||
if(_modulo < _length) m_write_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* out = reinterpret_cast<Tp*>(write_ptr());
|
||||
|
||||
// Copy in.
|
||||
memcpy((void*) out, in, _length);
|
||||
|
||||
// Update write count
|
||||
m_write_count += _length;
|
||||
|
||||
return {_length, out};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
ring_buffer::request()
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > free())
|
||||
throw std::runtime_error("heap-buffer-overflow :: ring buffer is full. read data "
|
||||
"to avoid data corruption");
|
||||
|
||||
// if write count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_write_count % m_size);
|
||||
if(_modulo < _length) m_write_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(write_ptr());
|
||||
|
||||
// Update write count
|
||||
m_write_count += _length;
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::read(Tp* out, std::enable_if_t<std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || out == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
// Make sure we do not read out more than there is actually in the buffer.
|
||||
if(_length > count()) throw std::runtime_error("ring buffer is empty");
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_read_count % m_size);
|
||||
if(_modulo < _length) m_read_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(read_ptr());
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
*out = *in;
|
||||
|
||||
// Update read count.
|
||||
m_read_count += _length;
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
std::pair<size_t, Tp*>
|
||||
ring_buffer::read(Tp* out, std::enable_if_t<!std::is_class<Tp>::value, int>) const
|
||||
{
|
||||
if(is_empty() || out == nullptr) return {0, nullptr};
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
using Up = typename std::remove_const<Tp>::type;
|
||||
|
||||
// Make sure we do not read out more than there is actually in the buffer.
|
||||
if(_length > count()) throw std::runtime_error("ring buffer is empty");
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_read_count % m_size);
|
||||
if(_modulo < _length) m_read_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* in = reinterpret_cast<Tp*>(read_ptr());
|
||||
|
||||
// Copy out for BYTE, nothing magic here.
|
||||
Up* _out = const_cast<Up*>(out);
|
||||
memcpy(_out, in, _length);
|
||||
|
||||
// Update read count.
|
||||
m_read_count += _length;
|
||||
|
||||
return {_length, in};
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
Tp*
|
||||
ring_buffer::retrieve()
|
||||
{
|
||||
if(m_ptr == nullptr) return nullptr;
|
||||
|
||||
auto _length = sizeof(Tp);
|
||||
|
||||
// Make sure we don't put in more than there's room for, by writing no
|
||||
// more than there is free.
|
||||
if(_length > count()) throw std::runtime_error("ring buffer is empty");
|
||||
|
||||
// if read count is at the tail of buffer, bump to the end of buffer
|
||||
auto _modulo = m_size - (m_read_count % m_size);
|
||||
if(_modulo < _length) m_read_count += _modulo;
|
||||
|
||||
// pointer in buffer
|
||||
Tp* _out = reinterpret_cast<Tp*>(read_ptr());
|
||||
|
||||
// Update write count
|
||||
m_read_count += _length;
|
||||
|
||||
return _out;
|
||||
}
|
||||
//
|
||||
} // namespace base
|
||||
///
|
||||
/// \struct rocprofiler::container::ring_buffer
|
||||
/// \brief Ring buffer wrapper around \ref tim::base::ring_buffer for data of type Tp. If
|
||||
/// the data object size is larger than the page size (typically 4KB), behavior is
|
||||
/// undefined. During initialization, one requests a minimum number of objects and the
|
||||
/// buffer will support that number of object + the remainder of the page, e.g. if a page
|
||||
/// is 1000 bytes, the object is 1 byte, and the buffer is requested to support 1500
|
||||
/// objects, then an allocation supporting 2000 objects (i.e. 2 pages) will be created.
|
||||
template <typename Tp>
|
||||
struct ring_buffer : private base::ring_buffer
|
||||
{
|
||||
using base_type = base::ring_buffer;
|
||||
|
||||
static size_t get_items_per_page();
|
||||
|
||||
ring_buffer() = default;
|
||||
~ring_buffer() = default;
|
||||
|
||||
explicit ring_buffer(bool _use_mmap)
|
||||
: base_type{_use_mmap}
|
||||
{}
|
||||
|
||||
explicit ring_buffer(size_t _size)
|
||||
: base_type{_size * sizeof(Tp)}
|
||||
{}
|
||||
|
||||
ring_buffer(size_t _size, bool _use_mmap)
|
||||
: base_type{_size * sizeof(Tp), _use_mmap}
|
||||
{}
|
||||
|
||||
ring_buffer(const ring_buffer&);
|
||||
ring_buffer(ring_buffer&&) noexcept = default;
|
||||
|
||||
ring_buffer& operator=(const ring_buffer&);
|
||||
ring_buffer& operator=(ring_buffer&&) noexcept = default;
|
||||
|
||||
/// Returns whether the buffer has been allocated
|
||||
bool is_initialized() const { return base_type::is_initialized(); }
|
||||
|
||||
/// Get the total number of Tp instances supported
|
||||
size_t capacity() const { return (base_type::capacity()) / sizeof(Tp); }
|
||||
|
||||
/// Creates new ring buffer.
|
||||
void init(size_t _size) { base_type::init(_size * sizeof(Tp)); }
|
||||
|
||||
/// Destroy ring buffer.
|
||||
void destroy() { base_type::destroy(); }
|
||||
|
||||
/// Write data to buffer.
|
||||
size_t data_size() const { return sizeof(Tp); }
|
||||
|
||||
/// Write data to buffer. Return pointer to location of write
|
||||
Tp* write(Tp* in) { return base_type::write<Tp>(in).second; }
|
||||
|
||||
/// Read data from buffer. Return pointer to location of read
|
||||
Tp* read(Tp* out) const { return base_type::read<Tp>(out).second; }
|
||||
|
||||
/// Get an uninitialized address at tail of buffer.
|
||||
Tp* request() { return base_type::request<Tp>(); }
|
||||
|
||||
/// Read data from head of buffer.
|
||||
Tp* retrieve() { return base_type::retrieve<Tp>(); }
|
||||
|
||||
/// Returns number of Tp instances currently held by the buffer.
|
||||
size_t count() const { return (base_type::count()) / sizeof(Tp); }
|
||||
|
||||
/// Returns how many Tp instances are availiable in the buffer.
|
||||
size_t free() const { return (base_type::free()) / sizeof(Tp); }
|
||||
|
||||
/// Returns if the buffer is empty.
|
||||
bool is_empty() const { return base_type::is_empty(); }
|
||||
|
||||
/// Returns if the buffer is full.
|
||||
bool is_full() const { return (base_type::free() < sizeof(Tp)); }
|
||||
|
||||
/// Rewinds the read pointer
|
||||
size_t rewind(size_t n) const { return base_type::rewind(n); }
|
||||
|
||||
template <typename... Args>
|
||||
auto emplace(Args&&... args)
|
||||
{
|
||||
Tp _obj{std::forward<Args>(args)...};
|
||||
return write(&_obj);
|
||||
}
|
||||
|
||||
using base_type::get_use_mmap;
|
||||
using base_type::load;
|
||||
using base_type::save;
|
||||
using base_type::set_use_mmap;
|
||||
|
||||
std::string as_string() const
|
||||
{
|
||||
std::ostringstream ss{};
|
||||
size_t _w = std::log10(base_type::capacity()) + 1;
|
||||
ss << std::boolalpha << std::right << "data size: " << std::setw(_w) << data_size()
|
||||
<< " B, is_initialized: " << std::setw(5) << is_initialized()
|
||||
<< ", is_empty: " << std::setw(5) << is_empty() << ", is_full: " << std::setw(5)
|
||||
<< is_full() << ", capacity: " << std::setw(_w) << capacity()
|
||||
<< ", count: " << std::setw(_w) << count() << ", free: " << std::setw(_w) << free()
|
||||
<< ", raw capacity: " << std::setw(_w) << base_type::capacity()
|
||||
<< " B, raw count: " << std::setw(_w) << base_type::count()
|
||||
<< " B, raw free: " << std::setw(_w) << base_type::free()
|
||||
<< " B, pointer: " << std::setw(15) << base_type::m_ptr
|
||||
<< ", raw read count: " << std::setw(_w) << base_type::m_read_count
|
||||
<< ", raw write count: " << std::setw(_w) << base_type::m_write_count;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const ring_buffer& obj)
|
||||
{
|
||||
return os << obj.as_string();
|
||||
}
|
||||
};
|
||||
//
|
||||
template <typename Tp>
|
||||
size_t
|
||||
ring_buffer<Tp>::get_items_per_page()
|
||||
{
|
||||
return std::max<size_t>(units::get_page_size() / sizeof(Tp), 1);
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
ring_buffer<Tp>::ring_buffer(const ring_buffer<Tp>& rhs)
|
||||
: base_type{rhs}
|
||||
{
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr()) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
}
|
||||
//
|
||||
template <typename Tp>
|
||||
ring_buffer<Tp>&
|
||||
ring_buffer<Tp>::operator=(const ring_buffer<Tp>& rhs)
|
||||
{
|
||||
if(this == &rhs) return *this;
|
||||
|
||||
base_type::operator=(rhs);
|
||||
size_t _n = rhs.count();
|
||||
char* _end = static_cast<char*>(rhs.m_ptr) + rhs.m_size;
|
||||
for(size_t i = 0; i < _n; ++i)
|
||||
{
|
||||
char* _addr = static_cast<char*>(rhs.read_ptr()) + (i * sizeof(Tp));
|
||||
if((_addr + sizeof(Tp)) > _end) _addr = static_cast<char*>(rhs.m_ptr);
|
||||
Tp* _in = static_cast<Tp*>(static_cast<void*>(_addr));
|
||||
write(_in);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
//
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,389 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 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 "lib/common/container/operators.hpp"
|
||||
#include "lib/common/container/static_vector.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp, size_t ChunkSizeV = 64>
|
||||
class stable_vector
|
||||
{
|
||||
public:
|
||||
using value_type = Tp;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
static constexpr const size_t chunk_size = ChunkSizeV;
|
||||
|
||||
private:
|
||||
template <size_t N>
|
||||
struct is_pow2
|
||||
{
|
||||
static constexpr bool value = (N & (N - 1)) == 0;
|
||||
};
|
||||
|
||||
static_assert(ChunkSizeV > 0, "ChunkSize needs to be greater than zero");
|
||||
static_assert(is_pow2<ChunkSizeV>::value, "ChunkSize needs to be a power of 2");
|
||||
|
||||
using this_type = stable_vector<Tp, ChunkSizeV>;
|
||||
using const_this_type = const stable_vector<Tp, ChunkSizeV>;
|
||||
|
||||
template <typename ContainerT>
|
||||
struct iterator_base
|
||||
{
|
||||
iterator_base(ContainerT* c = nullptr, size_type i = 0)
|
||||
: m_container(c)
|
||||
, m_index(i)
|
||||
{}
|
||||
|
||||
iterator_base& operator+=(size_type i)
|
||||
{
|
||||
m_index += i;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator-=(size_type i)
|
||||
{
|
||||
m_index -= i;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator++()
|
||||
{
|
||||
++m_index;
|
||||
return *this;
|
||||
}
|
||||
iterator_base& operator--()
|
||||
{
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator_base& it)
|
||||
{
|
||||
assert(m_container == it.m_container);
|
||||
return m_index - it.m_index;
|
||||
}
|
||||
|
||||
bool operator<(const iterator_base& it) const
|
||||
{
|
||||
assert(m_container == it.m_container);
|
||||
return m_index < it.m_index;
|
||||
}
|
||||
bool operator==(const iterator_base& it) const
|
||||
{
|
||||
return m_container == it.m_container && m_index == it.m_index;
|
||||
}
|
||||
|
||||
protected:
|
||||
ContainerT* m_container;
|
||||
size_type m_index;
|
||||
};
|
||||
|
||||
public:
|
||||
struct const_iterator;
|
||||
|
||||
struct iterator
|
||||
: public iterator_base<this_type>
|
||||
//, std::iterator<std::random_access_iterator_tag, value_type>
|
||||
, public random_access_iterator_helper<iterator, value_type>
|
||||
{
|
||||
using iterator_base<this_type>::iterator_base;
|
||||
friend struct const_iterator;
|
||||
|
||||
reference operator*() { return (*this->m_container)[this->m_index]; }
|
||||
};
|
||||
|
||||
struct const_iterator
|
||||
: public iterator_base<const_this_type>
|
||||
//, std::iterator<std::random_access_iterator_tag, const value_type>
|
||||
, public random_access_iterator_helper<const_iterator, const value_type>
|
||||
{
|
||||
using iterator_base<const_this_type>::iterator_base;
|
||||
|
||||
explicit const_iterator(const iterator& it)
|
||||
: iterator_base<const_this_type>(it.m_container, it.m_index)
|
||||
{}
|
||||
|
||||
const_reference operator*() const { return (*this->m_container)[this->m_index]; }
|
||||
|
||||
bool operator==(const const_iterator& it) const
|
||||
{
|
||||
return iterator_base<const_this_type>::operator==(it);
|
||||
}
|
||||
|
||||
friend bool operator==(const iterator& l, const const_iterator& r) { return r == l; }
|
||||
};
|
||||
|
||||
stable_vector() = default;
|
||||
explicit stable_vector(size_type count, const Tp& value);
|
||||
explicit stable_vector(size_type count);
|
||||
|
||||
template <typename InputItrT,
|
||||
typename = std::enable_if_t<
|
||||
std::is_convertible<typename std::iterator_traits<InputItrT>::iterator_category,
|
||||
std::input_iterator_tag>::value>>
|
||||
stable_vector(InputItrT first, InputItrT last);
|
||||
|
||||
explicit stable_vector(std::initializer_list<Tp>);
|
||||
|
||||
stable_vector(const stable_vector& other);
|
||||
stable_vector(stable_vector&& other) noexcept;
|
||||
|
||||
stable_vector& operator=(stable_vector v);
|
||||
|
||||
iterator begin() noexcept { return {this, 0}; }
|
||||
const_iterator begin() const noexcept { return {this, 0}; }
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
|
||||
iterator end() noexcept { return {this, size()}; }
|
||||
const_iterator end() const noexcept { return {this, size()}; }
|
||||
const_iterator cend() const noexcept { return end(); }
|
||||
|
||||
size_type size() const noexcept
|
||||
{
|
||||
return empty() ? 0 : (m_chunks.size() - 1) * ChunkSizeV + m_chunks.back()->size();
|
||||
}
|
||||
size_type max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
|
||||
size_type capacity() const noexcept { return m_chunks.size() * ChunkSizeV; }
|
||||
|
||||
bool empty() const noexcept { return m_chunks.size() == 0; }
|
||||
|
||||
void reserve(size_type new_capacity);
|
||||
void shrink_to_fit() noexcept {}
|
||||
|
||||
bool operator==(const this_type& c) const
|
||||
{
|
||||
return size() == c.size() && std::equal(cbegin(), cend(), c.cbegin());
|
||||
}
|
||||
bool operator!=(const this_type& c) const { return !operator==(c); }
|
||||
|
||||
void swap(this_type& v) { std::swap(m_chunks, v.m_chunks); }
|
||||
|
||||
friend void swap(this_type& l, this_type& r) { l.swap(r); }
|
||||
|
||||
reference front() { return m_chunks.front()->front(); }
|
||||
const_reference front() const { return front(); }
|
||||
|
||||
reference back() { return m_chunks.back()->back(); }
|
||||
const_reference back() const { return back(); }
|
||||
|
||||
void push_back(const Tp& t);
|
||||
void push_back(Tp&& t);
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args);
|
||||
|
||||
reference operator[](size_type i);
|
||||
|
||||
const_reference operator[](size_type i) const;
|
||||
|
||||
reference at(size_type i);
|
||||
|
||||
const_reference at(size_type i) const;
|
||||
|
||||
private:
|
||||
using chunk_type = container::static_vector<Tp, ChunkSizeV, true>;
|
||||
using storage_type = std::vector<std::unique_ptr<chunk_type>>;
|
||||
|
||||
void add_chunk();
|
||||
chunk_type& last_chunk();
|
||||
|
||||
storage_type m_chunks;
|
||||
};
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(size_type count, const Tp& value)
|
||||
{
|
||||
for(size_type i = 0; i < count; ++i)
|
||||
{
|
||||
push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(size_type count)
|
||||
{
|
||||
for(size_type i = 0; i < count; ++i)
|
||||
{
|
||||
emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
template <typename InputItrT, typename>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(InputItrT first, InputItrT last)
|
||||
{
|
||||
for(; first != last; ++first)
|
||||
{
|
||||
push_back(*first);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(const stable_vector& other)
|
||||
{
|
||||
for(const auto& chunk : other.m_chunks)
|
||||
{
|
||||
m_chunks.emplace_back(std::make_unique<chunk_type>(*chunk));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(stable_vector&& other) noexcept
|
||||
: m_chunks(std::move(other.m_chunks))
|
||||
{}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>::stable_vector(std::initializer_list<Tp> ilist)
|
||||
{
|
||||
for(const auto& t : ilist)
|
||||
{
|
||||
push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
stable_vector<Tp, ChunkSizeV>&
|
||||
stable_vector<Tp, ChunkSizeV>::operator=(stable_vector v)
|
||||
{
|
||||
swap(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::add_chunk()
|
||||
{
|
||||
m_chunks.emplace_back(std::make_unique<chunk_type>());
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::chunk_type&
|
||||
stable_vector<Tp, ChunkSizeV>::last_chunk()
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(m_chunks.empty() || m_chunks.back()->size() == ChunkSizeV))
|
||||
{
|
||||
add_chunk();
|
||||
}
|
||||
|
||||
return *m_chunks.back();
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::reserve(size_type new_capacity)
|
||||
{
|
||||
const size_t initial_capacity = capacity();
|
||||
for(difference_type i = new_capacity - initial_capacity; i > 0; i -= ChunkSizeV)
|
||||
{
|
||||
add_chunk();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::push_back(const Tp& t)
|
||||
{
|
||||
last_chunk().push_back(t);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::push_back(Tp&& t)
|
||||
{
|
||||
last_chunk().push_back(std::move(t));
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
template <typename... Args>
|
||||
void
|
||||
stable_vector<Tp, ChunkSizeV>::emplace_back(Args&&... args)
|
||||
{
|
||||
last_chunk().emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::reference
|
||||
stable_vector<Tp, ChunkSizeV>::operator[](size_type i)
|
||||
{
|
||||
return (*m_chunks[i / ChunkSizeV])[i % ChunkSizeV];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::const_reference
|
||||
stable_vector<Tp, ChunkSizeV>::operator[](size_type i) const
|
||||
{
|
||||
return const_cast<this_type&>(*this)[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::reference
|
||||
stable_vector<Tp, ChunkSizeV>::at(size_type i)
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(i >= size()))
|
||||
{
|
||||
throw ::rocprofiler::exception<std::out_of_range>("stable_vector::at(" + std::to_string(i) +
|
||||
"). size is " + std::to_string(size()));
|
||||
}
|
||||
|
||||
return operator[](i);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV>
|
||||
typename stable_vector<Tp, ChunkSizeV>::const_reference
|
||||
stable_vector<Tp, ChunkSizeV>::at(size_type i) const
|
||||
{
|
||||
return const_cast<this_type&>(*this).at(i);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t ChunkSizeV, typename... Args>
|
||||
auto
|
||||
resize(stable_vector<Tp, ChunkSizeV>& _v, size_t _n, Args&&... args)
|
||||
{
|
||||
if(_n > _v.capacity()) _v.reserve(_n);
|
||||
|
||||
while(_v.size() < _n)
|
||||
_v.emplace_back(std::forward<Args>(args)...);
|
||||
|
||||
return _v.size();
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,221 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 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 "lib/common/container/c_array.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <cstddef>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace container
|
||||
{
|
||||
template <typename Tp, size_t N, bool AtomicSizeV = false>
|
||||
struct static_vector
|
||||
{
|
||||
using count_type = std::conditional_t<AtomicSizeV, std::atomic<size_t>, size_t>;
|
||||
using this_type = static_vector<Tp, N>;
|
||||
using value_type = Tp;
|
||||
|
||||
static_vector() = default;
|
||||
static_vector(const static_vector&) = default;
|
||||
static_vector(static_vector&&) noexcept = default;
|
||||
static_vector& operator=(const static_vector&) = default;
|
||||
static_vector& operator=(static_vector&&) noexcept = default;
|
||||
|
||||
explicit static_vector(size_t _n, Tp _v = {});
|
||||
explicit static_vector(c_array<Tp>&&);
|
||||
|
||||
template <size_t M>
|
||||
explicit static_vector(std::array<Tp, M>&&);
|
||||
|
||||
static_vector& operator=(std::initializer_list<Tp>&& _v);
|
||||
static_vector& operator=(std::pair<std::array<Tp, N>, size_t>&&);
|
||||
|
||||
template <typename... Args>
|
||||
value_type& emplace_back(Args&&... _v);
|
||||
|
||||
template <typename Up>
|
||||
decltype(auto) push_back(Up&& _v)
|
||||
{
|
||||
return emplace_back(Tp{std::forward<Up>(_v)});
|
||||
}
|
||||
|
||||
void pop_back() { --m_size; }
|
||||
|
||||
void clear();
|
||||
void reserve(size_t) noexcept {}
|
||||
void shrink_to_fit() noexcept {}
|
||||
auto capacity() noexcept { return N; }
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
bool empty() const { return (size() == 0); }
|
||||
|
||||
auto begin() { return m_data.begin(); }
|
||||
auto begin() const { return m_data.begin(); }
|
||||
auto cbegin() const { return m_data.cbegin(); }
|
||||
|
||||
auto end() { return m_data.begin() + size(); }
|
||||
auto end() const { return m_data.begin() + size(); }
|
||||
auto cend() const { return m_data.cbegin() + size(); }
|
||||
|
||||
decltype(auto) operator[](size_t _idx) { return m_data[_idx]; }
|
||||
decltype(auto) operator[](size_t _idx) const { return m_data[_idx]; }
|
||||
|
||||
decltype(auto) at(size_t _idx) { return m_data.at(_idx); }
|
||||
decltype(auto) at(size_t _idx) const { return m_data.at(_idx); }
|
||||
|
||||
decltype(auto) front() { return m_data.front(); }
|
||||
decltype(auto) front() const { return m_data.front(); }
|
||||
decltype(auto) back() { return *(m_data.begin() + size() - 1); }
|
||||
decltype(auto) back() const { return *(m_data.begin() + size() - 1); }
|
||||
|
||||
auto* data() { return m_data.data(); }
|
||||
const auto* data() const { return m_data.data(); }
|
||||
|
||||
void swap(this_type& _v);
|
||||
|
||||
friend void swap(this_type& _lhs, this_type& _rhs) { _lhs.swap(_rhs); }
|
||||
|
||||
private:
|
||||
void update_size(size_t);
|
||||
|
||||
private:
|
||||
count_type m_size = count_type{0};
|
||||
std::array<Tp, N> m_data = {};
|
||||
};
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(size_t _n, Tp _v)
|
||||
{
|
||||
m_data.fill(_v);
|
||||
update_size(_n);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(c_array<Tp>&& _v)
|
||||
{
|
||||
auto _n = std::min<size_t>(N, _v.size());
|
||||
for(size_t i = 0; i < _n; ++i, ++m_size)
|
||||
m_data[i] = _v[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
template <size_t M>
|
||||
static_vector<Tp, N, AtomicSizeV>::static_vector(std::array<Tp, M>&& _v)
|
||||
{
|
||||
auto _n = std::min<size_t>(N, M);
|
||||
for(size_t i = 0; i < _n; ++i, ++m_size)
|
||||
m_data[i] = _v[i];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>&
|
||||
static_vector<Tp, N, AtomicSizeV>::operator=(std::initializer_list<Tp>&& _v)
|
||||
{
|
||||
if(ROCPROFILER_UNLIKELY(_v.size() > N))
|
||||
{
|
||||
throw exception<std::out_of_range>(
|
||||
std::string{"static_vector::operator=(initializer_list) size > "} + std::to_string(N));
|
||||
}
|
||||
|
||||
clear();
|
||||
for(auto&& itr : _v)
|
||||
m_data[m_size++] = itr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
static_vector<Tp, N, AtomicSizeV>&
|
||||
static_vector<Tp, N, AtomicSizeV>::operator=(std::pair<std::array<Tp, N>, size_t>&& _v)
|
||||
{
|
||||
update_size(0);
|
||||
m_data = std::move(_v.first);
|
||||
update_size(_v.second);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::clear()
|
||||
{
|
||||
update_size(0);
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::swap(this_type& _v)
|
||||
{
|
||||
if constexpr(AtomicSizeV)
|
||||
{
|
||||
auto _t_size = m_size;
|
||||
auto _v_size = _v.m_size;
|
||||
std::swap(m_data, _v.m_data);
|
||||
update_size(_v_size);
|
||||
_v.update_size(_t_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(m_size, _v.m_size);
|
||||
std::swap(m_data, _v.m_data);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
template <typename... Args>
|
||||
Tp&
|
||||
static_vector<Tp, N, AtomicSizeV>::emplace_back(Args&&... _v)
|
||||
{
|
||||
auto _idx = m_size++;
|
||||
if(_idx >= N)
|
||||
{
|
||||
throw exception<std::out_of_range>(
|
||||
std::string{"static_vector::emplace_back - reached capacity "} + std::to_string(N));
|
||||
}
|
||||
|
||||
if constexpr(std::is_assignable<Tp, decltype(std::forward<Args>(_v))...>::value)
|
||||
m_data[_idx] = {std::forward<Args>(_v)...};
|
||||
else
|
||||
m_data[_idx] = Tp{std::forward<Args>(_v)...};
|
||||
return m_data[_idx];
|
||||
}
|
||||
|
||||
template <typename Tp, size_t N, bool AtomicSizeV>
|
||||
void
|
||||
static_vector<Tp, N, AtomicSizeV>::update_size(size_t _n)
|
||||
{
|
||||
if constexpr(AtomicSizeV)
|
||||
m_size.store(_n);
|
||||
else
|
||||
m_size = _n;
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2018-2023 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ROCPROFILER_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
|
||||
#define ROCPROFILER_VISIBILITY(MODE) ROCPROFILER_ATTRIBUTE(visibility(MODE))
|
||||
#define ROCPROFILER_PUBLIC_API ROCPROFILER_VISIBILITY("default")
|
||||
#define ROCPROFILER_HIDDEN_API ROCPROFILER_VISIBILITY("hidden")
|
||||
#define ROCPROFILER_INTERNAL_API ROCPROFILER_VISIBILITY("internal")
|
||||
#define ROCPROFILER_INLINE ROCPROFILER_ATTRIBUTE(always_inline) inline
|
||||
#define ROCPROFILER_NOINLINE ROCPROFILER_ATTRIBUTE(noinline)
|
||||
#define ROCPROFILER_HOT ROCPROFILER_ATTRIBUTE(hot)
|
||||
#define ROCPROFILER_COLD ROCPROFILER_ATTRIBUTE(cold)
|
||||
#define ROCPROFILER_CONST ROCPROFILER_ATTRIBUTE(const)
|
||||
#define ROCPROFILER_PURE ROCPROFILER_ATTRIBUTE(pure)
|
||||
#define ROCPROFILER_WEAK ROCPROFILER_ATTRIBUTE(weak)
|
||||
#define ROCPROFILER_PACKED ROCPROFILER_ATTRIBUTE(__packed__)
|
||||
#define ROCPROFILER_PACKED_ALIGN(VAL) ROCPROFILER_PACKED ROCPROFILER_ATTRIBUTE(__aligned__(VAL))
|
||||
#define ROCPROFILER_LIKELY(...) __builtin_expect((__VA_ARGS__), 1)
|
||||
#define ROCPROFILER_UNLIKELY(...) __builtin_expect((__VA_ARGS__), 0)
|
||||
|
||||
#if defined(ROCPROFILER_CI) && ROCPROFILER_CI > 0
|
||||
# if defined(NDEBUG)
|
||||
# undef NDEBUG
|
||||
# endif
|
||||
# if !defined(DEBUG)
|
||||
# define DEBUG 1
|
||||
# endif
|
||||
# if defined(__cplusplus)
|
||||
# include <cassert>
|
||||
# else
|
||||
# include <assert.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ROCPROFILER_STRINGIZE(X) ROCPROFILER_STRINGIZE2(X)
|
||||
#define ROCPROFILER_STRINGIZE2(X) #X
|
||||
#define ROCPROFILER_VAR_NAME_COMBINE(X, Y) X##Y
|
||||
#define ROCPROFILER_VARIABLE(X, Y) ROCPROFILER_VAR_NAME_COMBINE(X, Y)
|
||||
#define ROCPROFILER_LINESTR ROCPROFILER_STRINGIZE(__LINE__)
|
||||
#define ROCPROFILER_ESC(...) __VA_ARGS__
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# if !defined(ROCPROFILER_FOLD_EXPRESSION)
|
||||
# define ROCPROFILER_FOLD_EXPRESSION(...) ((__VA_ARGS__), ...)
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lib/common/log.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(ROCPROFILER_ENVIRON_LOG_NAME)
|
||||
# if defined(ROCPROFILER_COMMON_LIBRARY_NAME)
|
||||
# define ROCPROFILER_ENVIRON_LOG_NAME "[" ROCPROFILER_COMMON_LIBRARY_NAME "]"
|
||||
# else
|
||||
# define ROCPROFILER_ENVIRON_LOG_NAME "[environ]"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFILER_ENVIRON_LOG_START)
|
||||
# if defined(ROCPROFILER_COMMON_LIBRARY_LOG_START)
|
||||
# define ROCPROFILER_ENVIRON_LOG_START ROCPROFILER_COMMON_LIBRARY_LOG_START
|
||||
# elif defined(ROCPROFILER_LOG_COLORS_AVAILABLE)
|
||||
# define ROCPROFILER_ENVIRON_LOG_START \
|
||||
fprintf(stderr, "%s", ::rocprofiler::common::log::color::dmesg());
|
||||
# else
|
||||
# define ROCPROFILER_ENVIRON_LOG_START
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(ROCPROFILER_ENVIRON_LOG_END)
|
||||
# if defined(ROCPROFILER_COMMON_LIBRARY_LOG_END)
|
||||
# define ROCPROFILER_ENVIRON_LOG_END ROCPROFILER_COMMON_LIBRARY_LOG_END
|
||||
# elif defined(ROCPROFILER_LOG_COLORS_AVAILABLE)
|
||||
# define ROCPROFILER_ENVIRON_LOG_END \
|
||||
fprintf(stderr, "%s", ::rocprofiler::common::log::color::dmesg());
|
||||
# else
|
||||
# define ROCPROFILER_ENVIRON_LOG_END
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ROCPROFILER_ENVIRON_LOG(CONDITION, ...) \
|
||||
if(CONDITION) \
|
||||
{ \
|
||||
fflush(stderr); \
|
||||
ROCPROFILER_ENVIRON_LOG_START \
|
||||
fprintf(stderr, "[rocprofiler]" ROCPROFILER_ENVIRON_LOG_NAME "[%i] ", getpid()); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
ROCPROFILER_ENVIRON_LOG_END \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline std::string
|
||||
get_env_impl(std::string_view env_id, std::string_view _default)
|
||||
{
|
||||
if(env_id.empty()) return std::string{_default};
|
||||
char* env_var = ::std::getenv(env_id.data());
|
||||
if(env_var) return std::string{env_var};
|
||||
return std::string{_default};
|
||||
}
|
||||
|
||||
inline std::string
|
||||
get_env_impl(std::string_view env_id, const char* _default)
|
||||
{
|
||||
return get_env_impl(env_id, std::string_view{_default});
|
||||
}
|
||||
|
||||
inline int
|
||||
get_env_impl(std::string_view env_id, int _default)
|
||||
{
|
||||
if(env_id.empty()) return _default;
|
||||
char* env_var = ::std::getenv(env_id.data());
|
||||
if(env_var)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::stoi(env_var);
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[rocprofiler][get_env] Exception thrown converting getenv(\"%s\") = "
|
||||
"%s to integer :: %s. Using default value of %i\n",
|
||||
env_id.data(),
|
||||
env_var,
|
||||
_e.what(),
|
||||
_default);
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
|
||||
inline bool
|
||||
get_env_impl(std::string_view env_id, bool _default)
|
||||
{
|
||||
if(env_id.empty()) return _default;
|
||||
char* env_var = ::std::getenv(env_id.data());
|
||||
if(env_var)
|
||||
{
|
||||
if(std::string_view{env_var}.empty())
|
||||
{
|
||||
throw std::runtime_error(std::string{"No boolean value provided for "} +
|
||||
std::string{env_id});
|
||||
}
|
||||
|
||||
if(std::string_view{env_var}.find_first_not_of("0123456789") == std::string_view::npos)
|
||||
{
|
||||
return static_cast<bool>(std::stoi(env_var));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < strlen(env_var); ++i)
|
||||
env_var[i] = tolower(env_var[i]);
|
||||
for(const auto& itr : {"off", "false", "no", "n", "f", "0"})
|
||||
if(strcmp(env_var, itr) == 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename Tp>
|
||||
inline auto
|
||||
get_env(std::string_view env_id, Tp&& _default)
|
||||
{
|
||||
if constexpr(std::is_enum<Tp>::value)
|
||||
{
|
||||
using Up = std::underlying_type_t<Tp>;
|
||||
// cast to underlying type -> get_env -> cast to enum type
|
||||
return static_cast<Tp>(get_env_impl(env_id, static_cast<Up>(_default)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return get_env_impl(env_id, std::forward<Tp>(_default));
|
||||
}
|
||||
}
|
||||
|
||||
struct env_config
|
||||
{
|
||||
std::string env_name = {};
|
||||
std::string env_value = {};
|
||||
int override = 0;
|
||||
|
||||
auto operator()(bool _verbose = false) const
|
||||
{
|
||||
if(env_name.empty()) return -1;
|
||||
ROCPROFILER_ENVIRON_LOG(_verbose,
|
||||
"setenv(\"%s\", \"%s\", %i)\n",
|
||||
env_name.c_str(),
|
||||
env_value.c_str(),
|
||||
override);
|
||||
return setenv(env_name.c_str(), env_value.c_str(), override);
|
||||
}
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,373 @@
|
||||
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE. */
|
||||
|
||||
#include "lib/common/helper.hpp"
|
||||
|
||||
#include <amd_comgr/amd_comgr.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <cxxabi.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#define ENABLE_BACKTRACE
|
||||
#if defined(ENABLE_BACKTRACE)
|
||||
# include <backtrace.h>
|
||||
#endif
|
||||
|
||||
#define amd_comgr_(call) \
|
||||
do \
|
||||
{ \
|
||||
if(amd_comgr_status_t status = amd_comgr_##call; status != AMD_COMGR_STATUS_SUCCESS) \
|
||||
{ \
|
||||
const char* reason = ""; \
|
||||
amd_comgr_status_string(status, &reason); \
|
||||
fprintf(stderr, #call " failed: %s\n", reason); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
std::string
|
||||
cxa_demangle(std::string_view _mangled_name, int* _status)
|
||||
{
|
||||
constexpr size_t buffer_len = 4096;
|
||||
// return the mangled since there is no buffer
|
||||
if(_mangled_name.empty())
|
||||
{
|
||||
*_status = -2;
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
auto _demangled_name = std::string{_mangled_name};
|
||||
|
||||
// PARAMETERS to __cxa_demangle
|
||||
// mangled_name:
|
||||
// A NULL-terminated character string containing the name to be demangled.
|
||||
// buffer:
|
||||
// A region of memory, allocated with malloc, of *length bytes, into which the
|
||||
// demangled name is stored. If output_buffer is not long enough, it is expanded
|
||||
// using realloc. output_buffer may instead be NULL; in that case, the demangled
|
||||
// name is placed in a region of memory allocated with malloc.
|
||||
// _buflen:
|
||||
// If length is non-NULL, the length of the buffer containing the demangled name
|
||||
// is placed in *length.
|
||||
// status:
|
||||
// *status is set to one of the following values
|
||||
size_t _demang_len = 0;
|
||||
char* _demang = abi::__cxa_demangle(_demangled_name.c_str(), nullptr, &_demang_len, _status);
|
||||
switch(*_status)
|
||||
{
|
||||
// 0 : The demangling operation succeeded.
|
||||
// -1 : A memory allocation failure occurred.
|
||||
// -2 : mangled_name is not a valid name under the C++ ABI mangling rules.
|
||||
// -3 : One of the arguments is invalid.
|
||||
case 0:
|
||||
{
|
||||
if(_demang) _demangled_name = std::string{_demang};
|
||||
break;
|
||||
}
|
||||
case -1:
|
||||
{
|
||||
char _msg[buffer_len];
|
||||
::memset(_msg, '\0', buffer_len * sizeof(char));
|
||||
::snprintf(_msg,
|
||||
buffer_len,
|
||||
"memory allocation failure occurred demangling %s",
|
||||
_demangled_name.c_str());
|
||||
::perror(_msg);
|
||||
break;
|
||||
}
|
||||
case -2: break;
|
||||
case -3:
|
||||
{
|
||||
char _msg[buffer_len];
|
||||
::memset(_msg, '\0', buffer_len * sizeof(char));
|
||||
::snprintf(_msg,
|
||||
buffer_len,
|
||||
"Invalid argument in: (\"%s\", nullptr, nullptr, %p)",
|
||||
_demangled_name.c_str(),
|
||||
(void*) _status);
|
||||
::perror(_msg);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
};
|
||||
|
||||
// if it "demangled" but the length is zero, set the status to -2
|
||||
if(_demang_len == 0 && *_status == 0) *_status = -2;
|
||||
|
||||
// free allocated buffer
|
||||
::free(_demang);
|
||||
return _demangled_name;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
#if defined(ENABLE_BACKTRACE)
|
||||
|
||||
// struct BackTraceInfo
|
||||
// {
|
||||
// struct ::backtrace_state* state = nullptr;
|
||||
// std::stringstream sstream{};
|
||||
// int depth = 0;
|
||||
// int error = 0;
|
||||
// };
|
||||
|
||||
// void
|
||||
// errorCallback(void* data, const char* message, int errnum)
|
||||
// {
|
||||
// BackTraceInfo* info = static_cast<BackTraceInfo*>(data);
|
||||
// info->sstream << "ROCProfiler: error: " << message << '(' << errnum << ')';
|
||||
// info->error = 1;
|
||||
// }
|
||||
|
||||
// void
|
||||
// syminfoCallback(void* data,
|
||||
// uintptr_t /* pc */,
|
||||
// const char* symname,
|
||||
// uintptr_t /* symval */,
|
||||
// uintptr_t /* symsize */)
|
||||
// {
|
||||
// BackTraceInfo* info = static_cast<BackTraceInfo*>(data);
|
||||
|
||||
// if(symname == nullptr) return;
|
||||
|
||||
// int status = 0;
|
||||
// auto&& _demangled = cxa_demangle(symname, &status);
|
||||
// info->sstream << ' '
|
||||
// << (status == 0 ? std::string_view{_demangled} : std::string_view{symname});
|
||||
// }
|
||||
|
||||
// int
|
||||
// fullCallback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function)
|
||||
// {
|
||||
// BackTraceInfo* info = static_cast<BackTraceInfo*>(data);
|
||||
|
||||
// info->sstream << std::endl
|
||||
// << " #" << std::dec << info->depth++ << ' ' << "0x" << std::noshowbase
|
||||
// << std::hex << std::setfill('0') << std::setw(sizeof(pc) * 2) << pc;
|
||||
// if(function == nullptr)
|
||||
// {
|
||||
// backtrace_syminfo(info->state, pc, syminfoCallback, errorCallback, data);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// int status = 0;
|
||||
// auto&& _demangled = cxa_demangle(function, &status);
|
||||
// info->sstream << ' '
|
||||
// << (status == 0 ? std::string_view{_demangled} :
|
||||
// std::string_view{function});
|
||||
|
||||
// if(filename != nullptr)
|
||||
// {
|
||||
// info->sstream << " in " << filename;
|
||||
// if(lineno != 0) info->sstream << ':' << std::dec << lineno;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return info->error;
|
||||
// }
|
||||
#endif // defined (ENABLE_BACKTRACE)
|
||||
} // namespace
|
||||
|
||||
/* The function extracts the kernel name from
|
||||
input string. By using the iterators it finds the
|
||||
window in the string which contains only the kernel name.
|
||||
For example 'Foo<int, float>::foo(a[], int (int))' -> 'foo'*/
|
||||
std::string
|
||||
truncate_name(std::string_view name)
|
||||
{
|
||||
auto rit = name.rbegin();
|
||||
auto rend = name.rend();
|
||||
uint32_t counter = 0;
|
||||
char open_token = 0;
|
||||
char close_token = 0;
|
||||
while(rit != rend)
|
||||
{
|
||||
if(counter == 0)
|
||||
{
|
||||
switch(*rit)
|
||||
{
|
||||
case ')':
|
||||
counter = 1;
|
||||
open_token = ')';
|
||||
close_token = '(';
|
||||
break;
|
||||
case '>':
|
||||
counter = 1;
|
||||
open_token = '>';
|
||||
close_token = '<';
|
||||
break;
|
||||
case ']':
|
||||
counter = 1;
|
||||
open_token = ']';
|
||||
close_token = '[';
|
||||
break;
|
||||
case ' ': ++rit; continue;
|
||||
}
|
||||
if(counter == 0) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*rit == open_token) counter++;
|
||||
if(*rit == close_token) counter--;
|
||||
}
|
||||
++rit;
|
||||
}
|
||||
auto rbeg = rit;
|
||||
while((rit != rend) && (*rit != ' ') && (*rit != ':'))
|
||||
rit++;
|
||||
return std::string{name.substr(rend - rit, rit - rbeg)};
|
||||
}
|
||||
|
||||
// C++ symbol demangle
|
||||
std::string
|
||||
cxx_demangle(std::string_view symbol)
|
||||
{
|
||||
int _status = 0;
|
||||
auto demangled_str = cxa_demangle(symbol, &_status);
|
||||
if(_status == 0)
|
||||
{
|
||||
return demangled_str;
|
||||
}
|
||||
|
||||
amd_comgr_data_t mangled_data;
|
||||
amd_comgr_(create_data(AMD_COMGR_DATA_KIND_BYTES, &mangled_data));
|
||||
amd_comgr_(set_data(mangled_data, symbol.size(), symbol.data()));
|
||||
|
||||
amd_comgr_data_t demangled_data;
|
||||
amd_comgr_(demangle_symbol_name(mangled_data, &demangled_data));
|
||||
|
||||
size_t demangled_size = 0;
|
||||
amd_comgr_(get_data(demangled_data, &demangled_size, nullptr));
|
||||
|
||||
demangled_str.resize(demangled_size);
|
||||
amd_comgr_(get_data(demangled_data, &demangled_size, demangled_str.data()));
|
||||
|
||||
amd_comgr_(release_data(mangled_data));
|
||||
amd_comgr_(release_data(demangled_data));
|
||||
return demangled_str;
|
||||
}
|
||||
|
||||
// check if string has special char
|
||||
bool
|
||||
has_special_char(std::string_view str)
|
||||
{
|
||||
return std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return !((isalnum(ch) != 0) || ch == '_' || ch == ':' || ch == ' ');
|
||||
}) != str.end();
|
||||
}
|
||||
|
||||
// check if string has correct counter format
|
||||
bool
|
||||
has_counter_format(std::string_view str)
|
||||
{
|
||||
return std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return ((isalnum(ch) != 0) || ch == '_');
|
||||
}) != str.end();
|
||||
}
|
||||
|
||||
// trims the begining of the line for spaces
|
||||
std::string
|
||||
left_trim(std::string_view s)
|
||||
{
|
||||
constexpr std::string_view WHITESPACE = " \n\r\t\f\v";
|
||||
size_t start = s.find_first_not_of(WHITESPACE);
|
||||
if(start == std::string_view::npos) return std::string{};
|
||||
return std::string{s.substr(start)};
|
||||
}
|
||||
|
||||
// trims begining and end of input line in place
|
||||
void
|
||||
trim(std::string& str)
|
||||
{
|
||||
// Remove leading spaces.
|
||||
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return std::isspace(ch) == 0;
|
||||
}));
|
||||
// Remove trailing spaces.
|
||||
str.erase(std::find_if(
|
||||
str.rbegin(), str.rend(), [](unsigned char ch) { return std::isspace(ch) == 0; })
|
||||
.base(),
|
||||
str.end());
|
||||
}
|
||||
|
||||
// replace unsuported specail chars with space
|
||||
static void
|
||||
handle_special_chars(std::string& str)
|
||||
{
|
||||
std::set<char> specialChars = {'!', '@', '#', '$', '%', '&', '(', ')', ',',
|
||||
'*', '+', '-', '.', '/', ';', '<', '=', '>',
|
||||
'?', '@', '{', '}', '^', '`', '~', '|', ':'};
|
||||
|
||||
// Iterate over the string and replace any special characters with a space.
|
||||
for(char& i : str)
|
||||
{
|
||||
if(specialChars.find(i) != specialChars.end())
|
||||
{
|
||||
i = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate input coutners and correct format if needed
|
||||
void
|
||||
validate_counters_format(std::vector<std::string>& counters, std::string line)
|
||||
{
|
||||
// trim line for any white spaces
|
||||
trim(line);
|
||||
|
||||
if(!(line[0] == '#' || line.find("pmc") == std::string::npos))
|
||||
{
|
||||
handle_special_chars(line);
|
||||
|
||||
std::stringstream input_line(line);
|
||||
std::string counter;
|
||||
while(getline(input_line, counter, ' '))
|
||||
{
|
||||
if(counter.substr(0, 3) != "pmc" && has_counter_format(counter))
|
||||
{
|
||||
counters.push_back(counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// raise exception with correct usage if user still managed to corrupt input
|
||||
for(const auto& itr : counters)
|
||||
{
|
||||
if(!has_counter_format(itr))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[rocprofiler] Bad input metric. usage --> pmc: <counter1> <counter2>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cxxabi.h>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
[[nodiscard]] std::string
|
||||
cxa_demangle(std::string_view _mangled_name, int* _status) __attribute__((nonnull(2)));
|
||||
|
||||
/* The function extracts the kernel name from
|
||||
input string. By using the iterators it finds the
|
||||
window in the string which contains only the kernel name.
|
||||
For example 'Foo<int, float>::foo(a[], int (int))' -> 'foo'*/
|
||||
std::string
|
||||
truncate_name(std::string_view name);
|
||||
|
||||
// C++ symbol demangle
|
||||
std::string
|
||||
cxx_demangle(std::string_view symbol);
|
||||
|
||||
// check if string has special char
|
||||
bool
|
||||
has_special_char(std::string_view str);
|
||||
|
||||
// check if string has correct counter format
|
||||
bool
|
||||
has_counter_format(std::string_view str);
|
||||
|
||||
// trims the begining of the line for spaces
|
||||
std::string
|
||||
left_trim(std::string_view s);
|
||||
|
||||
// validates pmc user input format
|
||||
void
|
||||
validate_counters_format(std::vector<std::string>& counters, std::string line);
|
||||
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(ROCPROFILER_FOLD_EXPRESSION)
|
||||
# define ROCPROFILER_FOLD_EXPRESSION(...) ((__VA_ARGS__), ...)
|
||||
#endif
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename Tp>
|
||||
struct is_string_impl : std::false_type
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct is_string_impl<std::string> : std::true_type
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct is_string_impl<std::string_view> : std::true_type
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct is_string_impl<const char*> : std::true_type
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct is_string_impl<char*> : std::true_type
|
||||
{};
|
||||
|
||||
template <typename Tp>
|
||||
struct is_string : is_string_impl<std::remove_cv_t<std::decay_t<Tp>>>
|
||||
{};
|
||||
|
||||
template <typename ArgT>
|
||||
auto
|
||||
as_string(ArgT&& _v, std::enable_if_t<is_string<ArgT>::value, int> = 0)
|
||||
{
|
||||
if constexpr(std::is_pointer<std::decay_t<ArgT>>::value)
|
||||
{
|
||||
return (_v == nullptr) ? std::string{"\"\""} : (std::string{"\""} + _v + std::string{"\""});
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string{"\""} + _v + std::string{"\""};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ArgT>
|
||||
auto
|
||||
as_string(ArgT&& _v, std::enable_if_t<!is_string<ArgT>::value, long> = 0)
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
|
||||
template <typename DelimT, typename... Args>
|
||||
auto
|
||||
join(DelimT&& _delim, Args&&... _args)
|
||||
{
|
||||
using delim_type = std::remove_cv_t<std::remove_reference_t<DelimT>>;
|
||||
|
||||
std::stringstream _ss{};
|
||||
_ss << std::boolalpha;
|
||||
|
||||
if constexpr(std::is_same<delim_type, char>::value)
|
||||
{
|
||||
const char _delim_c[2] = {_delim, '\0'};
|
||||
ROCPROFILER_FOLD_EXPRESSION(_ss << _delim_c << _args);
|
||||
auto _ret = _ss.str();
|
||||
return (_ret.length() > 1) ? _ret.substr(1) : std::string{};
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFILER_FOLD_EXPRESSION(_ss << _delim << _args);
|
||||
auto _ret = _ss.str();
|
||||
auto&& _len = std::string{_delim}.length();
|
||||
return (_ret.length() > _len) ? _ret.substr(_len) : std::string{};
|
||||
}
|
||||
}
|
||||
|
||||
struct QuoteStrings
|
||||
{};
|
||||
|
||||
template <typename DelimT, typename... Args>
|
||||
auto
|
||||
join(QuoteStrings&&, DelimT&& _delim, Args&&... _args)
|
||||
{
|
||||
using delim_type = std::remove_cv_t<std::remove_reference_t<DelimT>>;
|
||||
|
||||
std::stringstream _ss{};
|
||||
_ss << std::boolalpha;
|
||||
|
||||
if constexpr(std::is_same<delim_type, char>::value)
|
||||
{
|
||||
const char _delim_c[2] = {_delim, '\0'};
|
||||
ROCPROFILER_FOLD_EXPRESSION(_ss << _delim_c << as_string(_args));
|
||||
auto _ret = _ss.str();
|
||||
return (_ret.length() > 1) ? _ret.substr(1) : std::string{};
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCPROFILER_FOLD_EXPRESSION(_ss << _delim << as_string(_args));
|
||||
auto _ret = _ss.str();
|
||||
auto&& _len = std::string{_delim}.length();
|
||||
return (_ret.length() > _len) ? _ret.substr(_len) : std::string{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto
|
||||
join(std::array<std::string_view, 3>&& _delim, Args&&... _args)
|
||||
{
|
||||
return join("",
|
||||
std::get<0>(_delim),
|
||||
join(std::get<1>(_delim), std::forward<Args>(_args)...),
|
||||
std::get<2>(_delim));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto
|
||||
join(QuoteStrings&&, std::array<std::string_view, 3>&& _delim, Args&&... _args)
|
||||
{
|
||||
return join(QuoteStrings{},
|
||||
"",
|
||||
std::get<0>(_delim),
|
||||
join(std::get<1>(_delim), std::forward<Args>(_args)...),
|
||||
std::get<2>(_delim));
|
||||
}
|
||||
|
||||
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
|
||||
auto
|
||||
join(std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
|
||||
{
|
||||
return join("",
|
||||
std::get<0>(_delim),
|
||||
join(std::get<1>(_delim), std::forward<Args>(_args)...),
|
||||
std::get<2>(_delim));
|
||||
}
|
||||
|
||||
template <typename DelimB, typename DelimT, typename DelimE, typename... Args>
|
||||
auto
|
||||
join(QuoteStrings&&, std::tuple<DelimB, DelimT, DelimE>&& _delim, Args&&... _args)
|
||||
{
|
||||
return join(QuoteStrings{},
|
||||
"",
|
||||
std::get<0>(_delim),
|
||||
join(std::get<1>(_delim), std::forward<Args>(_args)...),
|
||||
std::get<2>(_delim));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,140 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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 rhs
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR rhsWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR rhs DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCPROFILER_LOG_COLORS_AVAILABLE
|
||||
# define ROCPROFILER_LOG_COLORS_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace log
|
||||
{
|
||||
bool&
|
||||
monochrome();
|
||||
|
||||
inline bool&
|
||||
monochrome()
|
||||
{
|
||||
static bool _v = []() {
|
||||
auto _val = false;
|
||||
const char* _env_cstr = nullptr;
|
||||
#if defined(ROCPROFILER_LOG_COLORS_ENV)
|
||||
_env_cstr = std::getenv(ROCPROFILER_LOG_COLORS_ENV);
|
||||
#elif defined(ROCPROFILER_PROJECT_NAME)
|
||||
auto _env_name = std::string{ROCPROFILER_PROJECT_NAME} + "_MONOCHROME";
|
||||
for(auto& itr : _env_name)
|
||||
itr = toupper(itr);
|
||||
_env_cstr = std::getenv(_env_name.c_str());
|
||||
#else
|
||||
_env_cstr = std::getenv("ROCPROFILER_MONOCHROME");
|
||||
#endif
|
||||
|
||||
if(!_env_cstr) _env_cstr = std::getenv("MONOCHROME");
|
||||
|
||||
if(_env_cstr)
|
||||
{
|
||||
auto _env = std::string{_env_cstr};
|
||||
|
||||
// check if numeric
|
||||
if(_env.find_first_not_of("0123456789") == std::string::npos)
|
||||
{
|
||||
return _env.length() > 1 || _env[0] != '0';
|
||||
}
|
||||
|
||||
for(auto& itr : _env)
|
||||
itr = tolower(itr);
|
||||
|
||||
// check for matches to acceptable forms of false
|
||||
for(const auto& itr : {"off", "false", "no", "n", "f"})
|
||||
{
|
||||
if(_env == itr) return false;
|
||||
}
|
||||
|
||||
// check for matches to acceptable forms of true
|
||||
for(const auto& itr : {"on", "true", "yes", "y", "t"})
|
||||
{
|
||||
if(_env == itr) return true;
|
||||
}
|
||||
}
|
||||
return _val;
|
||||
}();
|
||||
return _v;
|
||||
}
|
||||
|
||||
namespace color
|
||||
{
|
||||
static constexpr auto info_value = "\033[01;34m";
|
||||
static constexpr auto warning_value = "\033[01;33m";
|
||||
static constexpr auto fatal_value = "\033[01;31m";
|
||||
static constexpr auto source_value = "\033[01;32m";
|
||||
static constexpr auto dmesg_value = "\033[01;37m";
|
||||
static constexpr auto end_value = "\033[0m";
|
||||
|
||||
inline const char*
|
||||
info()
|
||||
{
|
||||
return (log::monochrome()) ? "" : info_value;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
warning()
|
||||
{
|
||||
return (log::monochrome()) ? "" : warning_value;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
fatal()
|
||||
{
|
||||
return (log::monochrome()) ? "" : fatal_value;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
source()
|
||||
{
|
||||
return (log::monochrome()) ? "" : source_value;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
dmesg()
|
||||
{
|
||||
return (log::monochrome()) ? "" : dmesg_value;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
end()
|
||||
{
|
||||
return (log::monochrome()) ? "" : end_value;
|
||||
}
|
||||
} // namespace color
|
||||
} // namespace log
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,376 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020, The Regents of the University of California,
|
||||
// through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||
// required approvals from the U.S. Dept. of Energy). 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
|
||||
// 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 "lib/common/environment.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace units
|
||||
{
|
||||
static constexpr int64_t nsec = 1;
|
||||
static constexpr int64_t usec = 1000 * nsec;
|
||||
static constexpr int64_t msec = 1000 * usec;
|
||||
static constexpr int64_t csec = 10 * msec;
|
||||
static constexpr int64_t dsec = 10 * csec;
|
||||
static constexpr int64_t sec = 10 * dsec;
|
||||
static constexpr int64_t minute = 60 * sec;
|
||||
static constexpr int64_t hour = 60 * minute;
|
||||
|
||||
static constexpr int64_t byte = 1;
|
||||
static constexpr int64_t kilobyte = 1000 * byte;
|
||||
static constexpr int64_t megabyte = 1000 * kilobyte;
|
||||
static constexpr int64_t gigabyte = 1000 * megabyte;
|
||||
static constexpr int64_t terabyte = 1000 * gigabyte;
|
||||
static constexpr int64_t petabyte = 1000 * terabyte;
|
||||
|
||||
static constexpr int64_t kibibyte = 1024 * byte;
|
||||
static constexpr int64_t mebibyte = 1024 * kibibyte;
|
||||
static constexpr int64_t gibibyte = 1024 * mebibyte;
|
||||
static constexpr int64_t tebibyte = 1024 * gibibyte;
|
||||
static constexpr int64_t pebibyte = 1024 * tebibyte;
|
||||
|
||||
static constexpr int64_t B = 1;
|
||||
static constexpr int64_t KB = 1000 * B;
|
||||
static constexpr int64_t MB = 1000 * KB;
|
||||
static constexpr int64_t GB = 1000 * MB;
|
||||
static constexpr int64_t TB = 1000 * GB;
|
||||
static constexpr int64_t PB = 1000 * TB;
|
||||
|
||||
static constexpr int64_t Bi = 1;
|
||||
static constexpr int64_t KiB = 1024 * Bi;
|
||||
static constexpr int64_t MiB = 1024 * KiB;
|
||||
static constexpr int64_t GiB = 1024 * MiB;
|
||||
static constexpr int64_t TiB = 1024 * GiB;
|
||||
static constexpr int64_t PiB = 1024 * TiB;
|
||||
|
||||
static constexpr int64_t nanowatt = 1;
|
||||
static constexpr int64_t microwatt = 1000 * nanowatt;
|
||||
static constexpr int64_t milliwatt = 1000 * microwatt;
|
||||
static constexpr int64_t watt = 1000 * milliwatt;
|
||||
static constexpr int64_t kilowatt = 1000 * watt;
|
||||
static constexpr int64_t megawatt = 1000 * kilowatt;
|
||||
static constexpr int64_t gigawatt = 1000 * megawatt;
|
||||
|
||||
static constexpr int64_t hertz = 1;
|
||||
static constexpr int64_t kilohertz = 1000 * hertz;
|
||||
static constexpr int64_t megahertz = 1000 * kilohertz;
|
||||
static constexpr int64_t gigahertz = 1000 * megahertz;
|
||||
|
||||
static constexpr int64_t Hz = 1;
|
||||
static constexpr int64_t KHz = 1000 * Hz;
|
||||
static constexpr int64_t MHz = 1000 * KHz;
|
||||
static constexpr int64_t GHz = 1000 * MHz;
|
||||
|
||||
inline int64_t
|
||||
get_page_size()
|
||||
{
|
||||
static auto _pagesz = sysconf(_SC_PAGESIZE);
|
||||
return _pagesz;
|
||||
}
|
||||
|
||||
const int64_t clocks_per_sec = sysconf(_SC_CLK_TCK);
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
time_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case nsec: return "nsec"; break;
|
||||
case usec: return "usec"; break;
|
||||
case msec: return "msec"; break;
|
||||
case csec: return "csec"; break;
|
||||
case dsec: return "dsec"; break;
|
||||
case sec: return "sec"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
mem_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case byte: return "B"; break;
|
||||
case kilobyte: return "KB"; break;
|
||||
case megabyte: return "MB"; break;
|
||||
case gigabyte: return "GB"; break;
|
||||
case terabyte: return "TB"; break;
|
||||
case petabyte: return "PB"; break;
|
||||
case kibibyte: return "KiB"; break;
|
||||
case mebibyte: return "MiB"; break;
|
||||
case gibibyte: return "GiB"; break;
|
||||
case tebibyte: return "TiB"; break;
|
||||
case pebibyte: return "PiB"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
freq_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case hertz: return "Hz"; break;
|
||||
case kilohertz: return "KHz"; break;
|
||||
case megahertz: return "MHz"; break;
|
||||
case gigahertz: return "GHz"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::string
|
||||
power_repr(int64_t _unit)
|
||||
{
|
||||
switch(_unit)
|
||||
{
|
||||
case nanowatt: return "nanowatts"; break;
|
||||
case microwatt: return "microwatts"; break;
|
||||
case milliwatt: return "milliwatts"; break;
|
||||
case watt: return "watts"; break;
|
||||
case kilowatt: return "kilowatts"; break;
|
||||
case megawatt: return "megawatts"; break;
|
||||
case gigawatt: return "gigawatts"; break;
|
||||
default: return "UNK"; break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_memory_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.length() == 0) return return_type{"MB", units::megabyte};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"byte", "b", units::byte},
|
||||
inner_t{"kilobyte", "kb", units::kilobyte},
|
||||
inner_t{"megabyte", "mb", units::megabyte},
|
||||
inner_t{"gigabyte", "gb", units::gigabyte},
|
||||
inner_t{"terabyte", "tb", units::terabyte},
|
||||
inner_t{"petabyte", "pb", units::petabyte},
|
||||
inner_t{"kibibyte", "kib", units::KiB},
|
||||
inner_t{"mebibyte", "mib", units::MiB},
|
||||
inner_t{"gibibyte", "gib", units::GiB},
|
||||
inner_t{"tebibyte", "tib", units::TiB},
|
||||
inner_t{"pebibyte", "pib", units::PiB}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || _unit == std::get<1>(itr))
|
||||
{
|
||||
if(std::get<2>(itr) == units::byte)
|
||||
return return_type{std::get<0>(itr), std::get<2>(itr)};
|
||||
return return_type{mem_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Warning!! No memory unit matching \"" << _unit << "\". Using default..."
|
||||
<< std::endl;
|
||||
|
||||
return return_type{"MB", units::megabyte};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_timing_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using strset_t = std::unordered_set<string_t>;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, strset_t, int64_t>;
|
||||
|
||||
if(_unit.length() == 0) return return_type{"sec", units::sec};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr :
|
||||
{inner_t{"nsec", strset_t{"ns", "nanosecond", "nanoseconds"}, units::nsec},
|
||||
inner_t{"usec", strset_t{"us", "microsecond", "microseconds"}, units::usec},
|
||||
inner_t{"msec", strset_t{"ms", "millisecond", "milliseconds"}, units::msec},
|
||||
inner_t{"csec", strset_t{"cs", "centisecond", "centiseconds"}, units::csec},
|
||||
inner_t{"dsec", strset_t{"ds", "decisecond", "deciseconds"}, units::dsec},
|
||||
inner_t{"sec", strset_t{"s", "second", "seconds"}, units::sec},
|
||||
inner_t{"min", strset_t{"minute", "minutes"}, units::minute},
|
||||
inner_t{"hr", strset_t{"hr", "hour", "hours"}, units::hour}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || std::get<1>(itr).find(_unit) != std::get<1>(itr).end())
|
||||
{
|
||||
return return_type{time_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Warning!! No timing unit matching \"" << _unit << "\". Using default..."
|
||||
<< std::endl;
|
||||
|
||||
return return_type{"sec", units::sec};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_frequncy_unit(std::string _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.length() == 0) return return_type{"MHz", units::megahertz};
|
||||
|
||||
for(auto& itr : _unit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"hertz", "hz", units::hertz},
|
||||
inner_t{"kilohertz", "khz", units::kilohertz},
|
||||
inner_t{"megahertz", "mhz", units::megahertz},
|
||||
inner_t{"gigahertz", "ghz", units::gigahertz}})
|
||||
{
|
||||
if(_unit == std::get<0>(itr) || _unit == std::get<1>(itr))
|
||||
{
|
||||
return return_type{freq_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Warning!! No frequency unit matching \"" << _unit << "\". Using default..."
|
||||
<< std::endl;
|
||||
|
||||
return return_type{"MHz", units::megahertz};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
inline std::tuple<std::string, int64_t>
|
||||
get_power_unit(const std::string& _unit)
|
||||
{
|
||||
using string_t = std::string;
|
||||
using return_type = std::tuple<string_t, int64_t>;
|
||||
using inner_t = std::tuple<string_t, string_t, int64_t>;
|
||||
|
||||
if(_unit.length() == 0) return return_type{"watts", units::watt};
|
||||
|
||||
auto _lunit = _unit;
|
||||
for(auto& itr : _lunit)
|
||||
itr = tolower(itr);
|
||||
|
||||
for(const auto& itr : {inner_t{"nanowatt", "nW", units::nanowatt},
|
||||
inner_t{"microwatt", "uW", units::microwatt},
|
||||
inner_t{"milliwatt", "mW", units::milliwatt},
|
||||
inner_t{"watt", "W", units::watt},
|
||||
inner_t{"kilowatt", "KW", units::kilowatt},
|
||||
inner_t{"megawatt", "MW", units::megawatt},
|
||||
inner_t{"gigawatt", "GW", units::gigawatt}})
|
||||
{
|
||||
if(_lunit == std::get<0>(itr) || _lunit + "s" == std::get<0>(itr) ||
|
||||
_unit == std::get<1>(itr))
|
||||
{
|
||||
return return_type{power_repr(std::get<2>(itr)), std::get<2>(itr)};
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Warning!! No power unit matching \"" << _unit << "\". Using default..."
|
||||
<< std::endl;
|
||||
|
||||
return return_type{"watts", units::watt};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
namespace temperature
|
||||
{
|
||||
enum unit_system : int8_t
|
||||
{
|
||||
Celsius = 0,
|
||||
Fahrenheit,
|
||||
Kelvin
|
||||
};
|
||||
|
||||
template <typename Tp>
|
||||
Tp
|
||||
convert(Tp _v, unit_system _from, unit_system _to)
|
||||
{
|
||||
switch(_from)
|
||||
{
|
||||
case Celsius:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return _v;
|
||||
case Fahrenheit: return static_cast<Tp>((_v * 1.8) + 32);
|
||||
case Kelvin: return (_v - 273);
|
||||
}
|
||||
}
|
||||
case Fahrenheit:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return static_cast<Tp>((_v - 32) / 1.8);
|
||||
case Fahrenheit: return _v;
|
||||
case Kelvin: return (_v - 273);
|
||||
}
|
||||
}
|
||||
case Kelvin:
|
||||
{
|
||||
switch(_to)
|
||||
{
|
||||
case Celsius: return (_v + 273);
|
||||
case Fahrenheit: return static_cast<Tp>(((_v + 273) * 1.8) + 32);
|
||||
case Kelvin: return _v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace temperature
|
||||
} // namespace units
|
||||
} // namespace common
|
||||
} // namespace rocprofiler
|
||||
Reference in New Issue
Block a user