refactor: centralize update_env across binaries with unit test added … (#2029)

* refactor: centralize update_env across binaries with unit test added for testing

* removed unused includes suggested by clangd and small cleanup

* use centralized update_env in argparse as well

* review comments incorporated

* move update_env tests closer to common library

* fix: missing common:: prefix in rocprof-sys-sample

* cmake formatting
This commit is contained in:
habajpai-amd
2025-12-04 19:24:27 +05:30
committed by GitHub
parent 16b9160034
commit 30161885e2
11 changed files with 798 additions and 362 deletions
@@ -24,7 +24,6 @@
#include "common/defines.h"
#include "common/environment.hpp"
#include "common/join.hpp"
#include "common/path.hpp"
#include "core/mproc.hpp"
#include "core/utility.hpp"
@@ -37,6 +36,7 @@
#include <timemory/utility/filepath.hpp>
#include <timemory/utility/join.hpp>
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdio>
@@ -58,6 +58,7 @@ namespace console = ::tim::utility::console;
namespace argparse = ::tim::argparse;
namespace path = rocprofsys::common::path;
using namespace ::timemory::join;
using rocprofsys::common::update_mode;
using ::rocprofsys::utility::parse_numeric_range;
using ::tim::get_env;
using ::tim::log::monochrome;
@@ -301,61 +302,28 @@ void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
bool _append, std::string_view _join_delim)
{
updated_envs.emplace(_env_var);
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0)
{
if(_append)
{
if(std::string_view{ itr }.find(join("", _env_val)) ==
std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
if(_env_var == "LD_PRELOAD")
{
itr =
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
.c_str());
}
else
{
itr =
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
.c_str());
}
}
}
else
{
free(itr);
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
}
return;
}
}
_environ.emplace_back(
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
auto _mode = _append ? update_mode::APPEND : update_mode::REPLACE;
rocprofsys::common::update_env(_environ, _env_var, std::forward<Tp>(_env_val), _mode,
_join_delim, updated_envs, original_envs);
}
template <typename Tp>
void
add_default_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val)
{
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0) return;
}
// Check if already exists
auto _key = join("", _env_var, "=");
const auto exists =
std::any_of(_environ.begin(), _environ.end(), [&_key](const char* itr) {
return itr && std::string_view{ itr }.find(_key) == 0;
});
updated_envs.emplace(_env_var);
_environ.emplace_back(
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
if(exists) return;
// If not exists, use common::update_env
rocprofsys::common::update_env(_environ, _env_var, std::forward<Tp>(_env_val),
update_mode::REPLACE, ":", updated_envs,
original_envs);
}
std::vector<char*>
@@ -32,6 +32,11 @@
#include <string_view>
#include <vector>
template <typename Tp>
void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
bool _append = false, std::string_view _join_delim = ":");
int
get_verbose();
@@ -49,15 +54,6 @@ prepare_command_for_run(char*, std::vector<char*>&);
void
prepare_environment_for_run(std::vector<char*>&);
template <typename Tp>
void
update_env(std::vector<char*>&, std::string_view, Tp&&, bool _append = false,
std::string_view _join_delim = ":");
template <typename Tp>
void
add_default_env(std::vector<char*>&, std::string_view, Tp&&);
std::vector<char*>
parse_args(int argc, char** argv, std::vector<char*>&,
std::vector<std::map<std::string_view, std::string>>&);
@@ -24,7 +24,6 @@
#include "common/defines.h"
#include "common/environment.hpp"
#include "common/join.hpp"
#include "common/path.hpp"
#include "core/argparse.hpp"
#include "core/timemory.hpp"
@@ -76,71 +75,9 @@ to_string(bool _v)
namespace
{
using rocprofsys::common::update_mode;
auto original_envs = std::unordered_set<std::string>{};
enum update_mode : int
{
UPD_REPLACE = 0, // no PREPEND/APPEND bits set
UPD_PREPEND = 1 << 0, // 0x01
UPD_APPEND = 1 << 1, // 0x02
UPD_WEAK = 1 << 2, // 0x04
};
template <typename Tp>
void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
update_mode&& _mode, std::string_view _join_delim = ":")
{
auto _prepend = (_mode & UPD_PREPEND) != 0;
auto _append = (_mode & UPD_APPEND) != 0;
auto _weak_upd = (_mode & UPD_WEAK) != 0;
// if both flags are set, prefer append
if(_prepend && _append)
{
_prepend = false;
}
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0)
{
if(_weak_upd)
{
// if the value has changed, do not update but allow overridding the value
// inherited from the initial env
if(original_envs.find(std::string{ itr }) == original_envs.end()) return;
}
if(_prepend || _append)
{
if(std::string_view{ itr }.find(join("", _env_val)) ==
std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
if(_prepend)
itr =
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
.c_str());
else
itr =
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
.c_str());
}
}
else
{
free(itr);
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
}
return;
}
}
_environ.emplace_back(
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
}
int
get_verbose(parser_data_t& _data)
@@ -172,16 +109,18 @@ get_initial_environment(parser_data_t& _data)
auto _libexecpath = path::realpath(path::get_internal_script_path());
if(!_libexecpath.empty())
{
update_env(_data.current, "ROCPROFSYS_SCRIPT_PATH", _libexecpath, UPD_REPLACE);
_data.updated.emplace("ROCPROFSYS_SCRIPT_PATH");
rocprofsys::common::update_env(_data.current, "ROCPROFSYS_SCRIPT_PATH",
_libexecpath, update_mode::REPLACE, ":",
_data.updated, original_envs);
}
const bool verbose = (get_verbose(_data) > 0);
if(auto llvm_dir = rocprofsys::common::discover_llvm_libdir_for_ompt(verbose);
!llvm_dir.empty())
{
update_env(_data.current, "LD_LIBRARY_PATH", llvm_dir, UPD_APPEND);
_data.updated.emplace("LD_LIBRARY_PATH");
rocprofsys::common::update_env(_data.current, "LD_LIBRARY_PATH", llvm_dir,
update_mode::APPEND, ":", _data.updated,
original_envs);
auto current_ld = getenv("LD_LIBRARY_PATH");
std::string new_ld = current_ld ? (llvm_dir + ":" + current_ld) : llvm_dir;
setenv("LD_LIBRARY_PATH", new_ld.c_str(), 1);
@@ -23,7 +23,6 @@
#include "rocprof-sys-sample.hpp"
#include "common/environment.hpp"
#include "common/join.hpp"
#include "common/path.hpp"
#include <timemory/environment.hpp>
@@ -46,6 +45,7 @@ namespace color = tim::log::color;
namespace path = rocprofsys::common::path;
using namespace timemory::join;
using rocprofsys::common::remove_env;
using rocprofsys::common::update_mode;
using tim::get_env;
using tim::log::monochrome;
using tim::log::stream;
@@ -118,21 +118,32 @@ get_initial_environment()
auto _libexecpath = path::realpath(path::get_internal_script_path());
auto _rootpath = path::realpath(path::get_rocprofsys_root());
update_env(_env, "ROCPROFSYS_ROOT", _rootpath, UPD_REPLACE);
update_env(_env, "LD_PRELOAD", _dl_libpath, UPD_APPEND);
update_env(_env, "LD_LIBRARY_PATH", tim::filepath::dirname(_dl_libpath), UPD_APPEND);
update_env(_env, "ROCPROFSYS_SCRIPT_PATH", _libexecpath, UPD_REPLACE);
rocprofsys::common::update_env(_env, "ROCPROFSYS_ROOT", _rootpath,
update_mode::REPLACE, ":", updated_envs,
original_envs);
rocprofsys::common::update_env(_env, "LD_PRELOAD", _dl_libpath, update_mode::APPEND,
":", updated_envs, original_envs);
rocprofsys::common::update_env(_env, "LD_LIBRARY_PATH",
tim::filepath::dirname(_dl_libpath),
update_mode::APPEND, ":", updated_envs, original_envs);
rocprofsys::common::update_env(_env, "ROCPROFSYS_SCRIPT_PATH", _libexecpath,
update_mode::REPLACE, ":", updated_envs,
original_envs);
// Discover LLVM libdir containing libomptarget.so and append to LD_LIBRARY_PATH
if(auto llvm_dir = rocprofsys::common::discover_llvm_libdir_for_ompt(verbose > 0);
!llvm_dir.empty())
{
update_env(_env, "LD_LIBRARY_PATH", llvm_dir, UPD_APPEND);
rocprofsys::common::update_env(_env, "LD_LIBRARY_PATH", llvm_dir,
update_mode::APPEND, ":", updated_envs,
original_envs);
}
auto _mode = get_env<std::string>("ROCPROFSYS_MODE", "sampling", false);
update_env(_env, "ROCPROFSYS_USE_SAMPLING", (_mode != "causal"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_USE_SAMPLING", (_mode != "causal"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
return _env;
}
@@ -184,65 +195,6 @@ print_updated_environment(std::vector<char*> _env)
std::cerr << std::endl;
}
template <typename Tp>
void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
update_mode&& _mode, std::string_view _join_delim)
{
updated_envs.emplace(_env_var);
auto _prepend = (_mode & UPD_PREPEND) != 0;
auto _append = (_mode & UPD_APPEND) != 0;
auto _weak_upd = (_mode & UPD_WEAK) != 0;
// if both flags are set, prefer append
if(_prepend && _append)
{
_prepend = false;
}
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0)
{
if(_weak_upd)
{
// if the value has changed, do not update but allow overridding the value
// inherited from the initial env
if(original_envs.find(std::string{ itr }) == original_envs.end()) return;
}
if(_prepend || _append)
{
if(std::string_view{ itr }.find(join("", _env_val)) ==
std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
if(_prepend)
itr =
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
.c_str());
else
itr =
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
.c_str());
}
}
else
{
free(itr);
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
}
return;
}
}
_environ.emplace_back(
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
}
std::vector<char*>
parse_args(int argc, char** argv, std::vector<char*>& _env)
{
@@ -335,20 +287,28 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
auto _monochrome = p.get<bool>("monochrome");
monochrome() = _monochrome;
p.set_use_color(!_monochrome);
update_env(_env, "ROCPROFSYS_MONOCHROME", (_monochrome) ? "1" : "0");
update_env(_env, "MONOCHROME", (_monochrome) ? "1" : "0");
rocprofsys::common::update_env(
_env, "ROCPROFSYS_MONOCHROME", (_monochrome) ? "1" : "0",
update_mode::REPLACE, ":", updated_envs, original_envs);
rocprofsys::common::update_env(_env, "MONOCHROME", (_monochrome) ? "1" : "0",
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.add_argument({ "--debug" }, "Debug output")
.max_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_DEBUG", p.get<bool>("debug"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_DEBUG", p.get<bool>("debug"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.add_argument({ "-v", "--verbose" }, "Verbose output")
.count(1)
.action([&](parser_t& p) {
auto _v = p.get<int>("verbose");
verbose = _v;
update_env(_env, "ROCPROFSYS_VERBOSE", _v);
rocprofsys::common::update_env(_env, "ROCPROFSYS_VERBOSE", _v,
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.start_group("GENERAL OPTIONS",
@@ -357,9 +317,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.min_count(0)
.dtype("filepath")
.action([&](parser_t& p) {
update_env(
rocprofsys::common::update_env(
_env, "ROCPROFSYS_CONFIG_FILE",
join(array_config{ ":" }, p.get<std::vector<std::string>>("config")));
join(array_config{ ":" }, p.get<std::vector<std::string>>("config")),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "-o", "--output" },
@@ -369,21 +330,30 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.max_count(2)
.action([&](parser_t& p) {
auto _v = p.get<std::vector<std::string>>("output");
update_env(_env, "ROCPROFSYS_OUTPUT_PATH", _v.at(0));
if(_v.size() > 1) update_env(_env, "ROCPROFSYS_OUTPUT_PREFIX", _v.at(1));
rocprofsys::common::update_env(_env, "ROCPROFSYS_OUTPUT_PATH", _v.at(0),
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(_v.size() > 1)
rocprofsys::common::update_env(_env, "ROCPROFSYS_OUTPUT_PREFIX", _v.at(1),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "-T", "--trace" }, "Generate a detailed trace (perfetto output)")
.max_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE", p.get<bool>("trace"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_TRACE", p.get<bool>("trace"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "--trace-cached" },
"Generate a detailed trace (perfetto output) from cached data ")
.max_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_CACHED", p.get<bool>("trace-cached"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_CACHED", p.get<bool>("trace-cached"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -392,7 +362,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.max_count(1)
.conflicts({ "flat-profile" })
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PROFILE", p.get<bool>("profile"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_PROFILE",
p.get<bool>("profile"), update_mode::REPLACE,
":", updated_envs, original_envs);
});
parser
.add_argument({ "-F", "--flat-profile" },
@@ -400,8 +372,12 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.max_count(1)
.conflicts({ "profile" })
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PROFILE", p.get<bool>("flat-profile"));
update_env(_env, "ROCPROFSYS_FLAT_PROFILE", p.get<bool>("flat-profile"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_PROFILE", p.get<bool>("flat-profile"),
update_mode::REPLACE, ":", updated_envs, original_envs);
rocprofsys::common::update_env(
_env, "ROCPROFSYS_FLAT_PROFILE", p.get<bool>("flat-profile"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "-H", "--host" },
@@ -411,9 +387,16 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.action([&](parser_t& p) {
auto _h = p.get<bool>("host");
auto _d = p.get<bool>("device");
update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING", _h || _d);
update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h);
if(_h) update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d);
rocprofsys::common::update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING",
_h || _d, update_mode::REPLACE, ":",
updated_envs, original_envs);
rocprofsys::common::update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(_h)
rocprofsys::common::update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d,
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "-D", "--device" },
@@ -423,9 +406,16 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.action([&](parser_t& p) {
auto _h = p.get<bool>("host");
auto _d = p.get<bool>("device");
update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING", _h || _d);
update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d);
if(_d) update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h);
rocprofsys::common::update_env(_env, "ROCPROFSYS_USE_PROCESS_SAMPLING",
_h || _d, update_mode::REPLACE, ":",
updated_envs, original_envs);
rocprofsys::common::update_env(_env, "ROCPROFSYS_USE_AMD_SMI", _d,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(_d)
rocprofsys::common::update_env(_env, "ROCPROFSYS_CPU_FREQ_ENABLED", _h,
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "-w", "--wait" },
@@ -433,8 +423,12 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"'--sampling-wait'. See the descriptions for those two options.")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_DELAY", p.get<double>("wait"));
update_env(_env, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("wait"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_TRACE_DELAY",
p.get<double>("wait"), update_mode::REPLACE,
":", updated_envs, original_envs);
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_DELAY",
p.get<double>("wait"), update_mode::REPLACE,
":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -443,8 +437,12 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"'--sampling-duration'. See the descriptions for those two options.")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_DURATION", p.get<double>("duration"));
update_env(_env, "ROCPROFSYS_SAMPLING_DURATION", p.get<double>("duration"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_DURATION", p.get<double>("duration"),
update_mode::REPLACE, ":", updated_envs, original_envs);
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_DURATION", p.get<double>("duration"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser.start_group("TRACING OPTIONS", "Specific options controlling tracing (i.e. "
@@ -456,8 +454,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.count(1)
.dtype("filepath")
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PERFETTO_FILE",
p.get<std::string>("trace-file"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_PERFETTO_FILE", p.get<std::string>("trace-file"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "--trace-buffer-size" },
@@ -465,15 +464,19 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.count(1)
.dtype("KB")
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PERFETTO_BUFFER_SIZE_KB",
p.get<int64_t>("trace-buffer-size"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_PERFETTO_BUFFER_SIZE_KB",
p.get<int64_t>("trace-buffer-size"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.add_argument({ "--trace-fill-policy" }, _trace_policy_desc)
.count(1)
.choices({ "discard", "ring_buffer" })
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PERFETTO_FILL_POLICY",
p.get<std::string>("trace-fill-policy"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_PERFETTO_FILL_POLICY",
p.get<std::string>("trace-fill-policy"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "--trace-wait" },
@@ -483,7 +486,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"but that can changed via --trace-clock-id.")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_DELAY", p.get<double>("trace-wait"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_DELAY", p.get<double>("trace-wait"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "--trace-duration" },
@@ -492,8 +497,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"that can changed via --trace-clock-id.")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_DURATION",
p.get<double>("trace-duration"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_DURATION", p.get<double>("trace-duration"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -503,9 +509,11 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"and/or <DELAY>:<DURATION>:<REPEAT>:<CLOCK_ID>.")
.min_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_PERIODS",
join(array_config{ ",", "", "" },
p.get<std::vector<std::string>>("trace-periods")));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_PERIODS",
join(array_config{ ",", "", "" },
p.get<std::vector<std::string>>("trace-periods")),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -518,8 +526,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"rocprof-sys to auto-scale based on the number of threads.")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_TRACE_PERIOD_CLOCK_ID",
p.get<double>("trace-clock-id"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TRACE_PERIOD_CLOCK_ID", p.get<double>("trace-clock-id"),
update_mode::REPLACE, ":", updated_envs, original_envs);
})
.choices(clock_id_choices.first)
.choice_aliases(clock_id_choices.second);
@@ -534,12 +543,20 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.choices({ "text", "json", "console" })
.action([&](parser_t& p) {
auto _v = p.get<std::set<std::string>>("profile");
update_env(_env, "ROCPROFSYS_PROFILE", true);
rocprofsys::common::update_env(_env, "ROCPROFSYS_PROFILE", true,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_TEXT_OUTPUT", _v.count("text") != 0);
update_env(_env, "ROCPROFSYS_JSON_OUTPUT", _v.count("json") != 0);
update_env(_env, "ROCPROFSYS_COUT_OUTPUT", _v.count("console") != 0);
rocprofsys::common::update_env(
_env, "ROCPROFSYS_TEXT_OUTPUT", _v.count("text") != 0,
update_mode::REPLACE, ":", updated_envs, original_envs);
rocprofsys::common::update_env(
_env, "ROCPROFSYS_JSON_OUTPUT", _v.count("json") != 0,
update_mode::REPLACE, ":", updated_envs, original_envs);
rocprofsys::common::update_env(
_env, "ROCPROFSYS_COUT_OUTPUT", _v.count("console") != 0,
update_mode::REPLACE, ":", updated_envs, original_envs);
}
});
@@ -552,9 +569,16 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.max_count(2)
.action([&](parser_t& p) {
auto _v = p.get<std::vector<std::string>>("profile-diff");
update_env(_env, "ROCPROFSYS_DIFF_OUTPUT", true);
update_env(_env, "ROCPROFSYS_INPUT_PATH", _v.at(0));
if(_v.size() > 1) update_env(_env, "ROCPROFSYS_INPUT_PREFIX", _v.at(1));
rocprofsys::common::update_env(_env, "ROCPROFSYS_DIFF_OUTPUT", true,
update_mode::REPLACE, ":", updated_envs,
original_envs);
rocprofsys::common::update_env(_env, "ROCPROFSYS_INPUT_PATH", _v.at(0),
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(_v.size() > 1)
rocprofsys::common::update_env(_env, "ROCPROFSYS_INPUT_PREFIX", _v.at(1),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.start_group(
@@ -567,8 +591,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"(number of interrupts per second)")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PROCESS_SAMPLING_FREQ",
p.get<double>("process-freq"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_PROCESS_SAMPLING_FREQ", p.get<double>("process-freq"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "--process-wait" }, "Set the default wait time (i.e. delay) "
@@ -576,8 +601,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"(in seconds of realtime)")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_PROCESS_SAMPLING_DELAY",
p.get<double>("process-wait"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_PROCESS_SAMPLING_DELAY", p.get<double>("process-wait"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -585,8 +611,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"Set the duration of the host/device sampling (in seconds of realtime)")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_PROCESS_DURATION",
p.get<double>("process-duration"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_PROCESS_DURATION",
p.get<double>("process-duration"),
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser
.add_argument({ "--cpus" },
@@ -594,9 +622,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.dtype("int or range")
.required({ "host" })
.action([&](parser_t& p) {
update_env(
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_CPUS",
join(array_config{ "," }, p.get<std::vector<std::string>>("cpus")));
join(array_config{ "," }, p.get<std::vector<std::string>>("cpus")),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "--gpus" },
@@ -604,9 +633,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.dtype("int or range")
.required({ "device" })
.action([&](parser_t& p) {
update_env(
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_GPUS",
join(array_config{ "," }, p.get<std::vector<std::string>>("gpus")));
join(array_config{ "," }, p.get<std::vector<std::string>>("gpus")),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser.start_group("GENERAL SAMPLING OPTIONS",
@@ -616,7 +646,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"(number of interrupts per second)")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_FREQ", p.get<double>("freq"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_FREQ",
p.get<double>("freq"), update_mode::REPLACE,
":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -626,7 +658,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"delay of 1 second for CPU-clock sampler may not equal 1 second of realtime")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("sampling-wait"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("sampling-wait"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument(
@@ -636,8 +670,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"real-time duration... resulting in zero samples being taken")
.count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_DURATION",
p.get<double>("sampling-duration"));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_DURATION", p.get<double>("sampling-duration"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser
.add_argument({ "-t", "--tids" },
@@ -646,8 +681,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"is assigned an atomically incrementing value.")
.min_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_TIDS",
join(array_config{ ", " }, p.get<std::vector<int64_t>>("tids")));
rocprofsys::common::update_env(
_env, "ROCPROFSYS_SAMPLING_TIDS",
join(array_config{ ", " }, p.get<std::vector<int64_t>>("tids")),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser.start_group(
@@ -657,21 +694,29 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.min_count(0)
.action([&](parser_t& p) {
auto _v = p.get<std::deque<std::string>>("cputime");
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", true);
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", true,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_FREQ", _v.front());
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_FREQ",
_v.front(), update_mode::REPLACE, ":",
updated_envs, original_envs);
_v.pop_front();
}
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_DELAY", _v.front());
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_DELAY",
_v.front(), update_mode::REPLACE, ":",
updated_envs, original_envs);
_v.pop_front();
}
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_TIDS",
join(array_config{ "," }, _v));
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME_TIDS",
join(array_config{ "," }, _v),
update_mode::REPLACE, ":", updated_envs,
original_envs);
}
});
@@ -679,21 +724,29 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.min_count(0)
.action([&](parser_t& p) {
auto _v = p.get<std::deque<std::string>>("realtime");
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME", true);
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME", true,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_FREQ", _v.front());
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_FREQ",
_v.front(), update_mode::REPLACE, ":",
updated_envs, original_envs);
_v.pop_front();
}
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_DELAY", _v.front());
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_DELAY",
_v.front(), update_mode::REPLACE, ":",
updated_envs, original_envs);
_v.pop_front();
}
if(!_v.empty())
{
update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_TIDS",
join(array_config{ "," }, _v));
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_REALTIME_TIDS",
join(array_config{ "," }, _v),
update_mode::REPLACE, ":", updated_envs,
original_envs);
}
});
@@ -726,7 +779,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.action([&](parser_t& p) {
auto _v = p.get<std::set<std::string>>("include");
auto _update = [&](const auto& _opt, bool _cond) {
if(_cond || _v.count("all") > 0) update_env(_env, _opt, true);
if(_cond || _v.count("all") > 0)
rocprofsys::common::update_env(_env, _opt, true, update_mode::REPLACE,
":", updated_envs, original_envs);
};
_update("ROCPROFSYS_USE_KOKKOSP", _v.count("kokkosp") > 0);
_update("ROCPROFSYS_USE_MPIP", _v.count("mpip") > 0);
@@ -739,7 +794,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
_update("ROCPROFSYS_TRACE_THREAD_SPIN_LOCKS", _v.count("spin-locks") > 0);
if(_v.count("all") > 0 || _v.count("kokkosp") > 0)
update_env(_env, "KOKKOS_TOOLS_LIBS", _omni_libpath, UPD_APPEND);
rocprofsys::common::update_env(_env, "KOKKOS_TOOLS_LIBS", _omni_libpath,
update_mode::APPEND, ":", updated_envs,
original_envs);
});
parser.add_argument({ "-E", "--exclude" }, "Exclude data from these backends")
@@ -747,7 +804,10 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.action([&](parser_t& p) {
auto _v = p.get<std::set<std::string>>("exclude");
auto _update = [&](const auto& _opt, bool _cond) {
if(_cond || _v.count("all") > 0) update_env(_env, _opt, false);
if(_cond || _v.count("all") > 0)
rocprofsys::common::update_env(_env, _opt, false,
update_mode::REPLACE, ":",
updated_envs, original_envs);
};
_update("ROCPROFSYS_USE_KOKKOSP", _v.count("kokkosp") > 0);
_update("ROCPROFSYS_USE_MPIP", _v.count("mpip") > 0);
@@ -771,7 +831,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.action([&](parser_t& p) {
auto _events =
join(array_config{ "," }, p.get<std::vector<std::string>>("cpu-events"));
update_env(_env, "ROCPROFSYS_PAPI_EVENTS", _events);
rocprofsys::common::update_env(_env, "ROCPROFSYS_PAPI_EVENTS", _events,
update_mode::REPLACE, ":", updated_envs,
original_envs);
});
parser.start_group("MISCELLANEOUS OPTIONS", "");
@@ -780,8 +842,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
"Include inline info in output when available")
.max_count(1)
.action([&](parser_t& p) {
update_env(_env, "ROCPROFSYS_SAMPLING_INCLUDE_INLINES",
p.get<bool>("inlines"));
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_INCLUDE_INLINES",
p.get<bool>("inlines"), update_mode::REPLACE,
":", updated_envs, original_envs);
});
parser.add_argument({ "--hsa-interrupt" }, _hsa_interrupt_desc)
@@ -789,7 +852,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
.dtype("int")
.choices({ 0, 1 })
.action([&](parser_t& p) {
update_env(_env, "HSA_ENABLE_INTERRUPT", p.get<int>("hsa-interrupt"));
rocprofsys::common::update_env(
_env, "HSA_ENABLE_INTERRUPT", p.get<int>("hsa-interrupt"),
update_mode::REPLACE, ":", updated_envs, original_envs);
});
parser.end_group();
@@ -820,7 +885,9 @@ parse_args(int argc, char** argv, std::vector<char*>& _env)
throw std::runtime_error(_cerr.what());
if(parser.exists("realtime") && !parser.exists("cputime"))
update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", false);
rocprofsys::common::update_env(_env, "ROCPROFSYS_SAMPLING_CPUTIME", false,
update_mode::REPLACE, ":", updated_envs,
original_envs);
if(parser.exists("profile") && parser.exists("flat-profile"))
throw std::runtime_error(
"Error! '--profile' argument conflicts with '--flat-profile' argument");
@@ -22,17 +22,8 @@
#pragma once
#include <string_view>
#include <vector>
enum update_mode : int
{
UPD_REPLACE = 0, // no PREPEND/APPEND bits set
UPD_PREPEND = 1 << 0, // 0x01
UPD_APPEND = 1 << 1, // 0x02
UPD_WEAK = 1 << 2, // 0x04
};
void
print_command(const std::vector<char*>& _argv);
@@ -42,10 +33,5 @@ print_updated_environment(std::vector<char*> _env);
std::vector<char*>
get_initial_environment();
template <typename Tp>
void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
update_mode&& _mode = UPD_REPLACE, std::string_view _join_delim = ":");
std::vector<char*>
parse_args(int argc, char** argv, std::vector<char*>& envp);
@@ -48,3 +48,7 @@ target_compile_definitions(
rocprofiler-systems-common-library
INTERFACE $<BUILD_INTERFACE:ROCPROFSYS_INTERNAL_BUILD=1>
)
if(ROCPROFSYS_BUILD_TESTING)
add_subdirectory(tests)
endif()
@@ -265,5 +265,82 @@ discover_llvm_libdir_for_ompt(bool verbose = false)
return {};
}
enum class update_mode : uint8_t
{
REPLACE = 0,
PREPEND,
APPEND,
WEAK,
};
template <typename Tp>
inline std::string
to_env_string(Tp&& val)
{
using T = std::decay_t<Tp>;
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, const char*> ||
std::is_same_v<T, bool> || std::is_arithmetic_v<T>,
"to_env_string: unsupported type. Use string, bool, or numeric types.");
if constexpr(std::is_same_v<T, std::string> || std::is_same_v<T, const char*>)
return std::string{ val };
else if constexpr(std::is_same_v<T, bool>)
return val ? "true" : "false";
else
return std::to_string(val);
}
template <typename Tp>
inline void
update_env(std::vector<char*>& _environ, std::string_view _env_var, Tp&& _env_val,
update_mode _mode, std::string_view _join_delim,
std::unordered_set<std::string_view>& _updated_envs,
const std::unordered_set<std::string>& _original_envs)
{
_updated_envs.emplace(_env_var);
const bool _prepend = (_mode == update_mode::PREPEND);
const bool _append = (_mode == update_mode::APPEND);
const bool _weak_upd = (_mode == update_mode::WEAK);
auto _env_val_str = to_env_string(std::forward<Tp>(_env_val));
auto _key = join("", _env_var, "=");
for(auto& itr : _environ)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) != 0) continue;
if(_weak_upd)
{
if(_original_envs.find(std::string{ itr }) == _original_envs.end()) return;
}
if(_prepend || _append)
{
if(std::string_view{ itr }.find(_env_val_str) == std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
if(_prepend)
itr =
strdup(join('=', _env_var, join(_join_delim, _env_val_str, _val))
.c_str());
else
itr =
strdup(join('=', _env_var, join(_join_delim, _val, _env_val_str))
.c_str());
}
}
else
{
free(itr);
itr = strdup(join('=', _env_var, _env_val_str).c_str());
}
return;
}
_environ.emplace_back(strdup(join('=', _env_var, _env_val_str).c_str()));
}
} // namespace common
} // namespace rocprofsys
@@ -0,0 +1,28 @@
# MIT License
#
# Copyright (c) 2025 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.
add_library(lib-common-tests OBJECT test_update_env.cpp)
target_link_libraries(
lib-common-tests
PUBLIC rocprofiler-systems-common-library rocprofiler-systems-googletest-library
)
@@ -0,0 +1,420 @@
// MIT License
//
// Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "common/environment.hpp"
#include <cstring>
#include <gtest/gtest.h>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>
using namespace rocprofsys::common;
static std::string
find_env_var(const std::vector<char*>& env, std::string_view var_name)
{
std::string prefix = std::string(var_name) + "=";
for(auto* itr : env)
{
if(!itr) continue;
if(std::string_view{ itr }.find(prefix) == 0)
{
return std::string{ itr };
}
}
return "";
}
class UpdateEnvTest : public ::testing::Test
{
protected:
void SetUp() override
{
env_vars.clear();
updated_envs.clear();
original_envs.clear();
}
void TearDown() override
{
for(auto* ptr : env_vars)
{
if(ptr) free(ptr);
}
}
std::vector<char*> env_vars;
std::unordered_set<std::string_view> updated_envs;
std::unordered_set<std::string> original_envs;
};
TEST_F(UpdateEnvTest, ReplaceMode_NewVariable)
{
update_env(env_vars, "TEST_VAR", "test_value", update_mode::REPLACE, ":",
updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "TEST_VAR=test_value");
EXPECT_EQ(updated_envs.count("TEST_VAR"), 1);
}
TEST_F(UpdateEnvTest, ReplaceMode_ExistingVariable)
{
env_vars.push_back(strdup("TEST_VAR=old_value"));
original_envs.insert("TEST_VAR=old_value");
update_env(env_vars, "TEST_VAR", "new_value", update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "TEST_VAR=new_value");
EXPECT_EQ(updated_envs.count("TEST_VAR"), 1);
}
TEST_F(UpdateEnvTest, AppendMode_NewVariable)
{
update_env(env_vars, "PATH", "/new/path", update_mode::APPEND, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "PATH=/new/path");
}
TEST_F(UpdateEnvTest, AppendMode_ExistingVariable)
{
env_vars.push_back(strdup("PATH=/old/path"));
original_envs.insert("PATH=/old/path");
update_env(env_vars, "PATH", "/new/path", update_mode::APPEND, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "PATH=/old/path:/new/path");
EXPECT_EQ(updated_envs.count("PATH"), 1);
}
TEST_F(UpdateEnvTest, PrependMode_ExistingVariable)
{
env_vars.push_back(strdup("LD_LIBRARY_PATH=/old/lib"));
original_envs.insert("LD_LIBRARY_PATH=/old/lib");
update_env(env_vars, "LD_LIBRARY_PATH", "/new/lib", update_mode::PREPEND, ":",
updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "LD_LIBRARY_PATH=/new/lib:/old/lib");
}
TEST_F(UpdateEnvTest, WeakMode_OriginalValue)
{
env_vars.push_back(strdup("WEAK_VAR=original"));
original_envs.insert("WEAK_VAR=original");
update_env(env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "WEAK_VAR=new_value");
}
TEST_F(UpdateEnvTest, WeakMode_ModifiedValue)
{
env_vars.push_back(strdup("WEAK_VAR=original"));
original_envs.insert("WEAK_VAR=original");
free(env_vars[0]);
env_vars[0] = strdup("WEAK_VAR=modified");
update_env(env_vars, "WEAK_VAR", "new_value", update_mode::WEAK, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "WEAK_VAR=modified");
}
TEST_F(UpdateEnvTest, BooleanValue_True)
{
update_env(env_vars, "BOOL_VAR", true, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "BOOL_VAR=true");
}
TEST_F(UpdateEnvTest, BooleanValue_False)
{
update_env(env_vars, "BOOL_VAR", false, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "BOOL_VAR=false");
}
TEST_F(UpdateEnvTest, NumericValue)
{
update_env(env_vars, "NUM_VAR", 42, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "NUM_VAR=42");
}
TEST_F(UpdateEnvTest, AppendMode_AvoidsDuplicates)
{
env_vars.push_back(strdup("PATH=/existing/path"));
original_envs.insert("PATH=/existing/path");
update_env(env_vars, "PATH", "/existing/path", update_mode::APPEND, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "PATH=/existing/path");
}
TEST_F(UpdateEnvTest, CustomDelimiter)
{
env_vars.push_back(strdup("VAR=a"));
original_envs.insert("VAR=a");
update_env(env_vars, "VAR", "b", update_mode::APPEND, ",", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "VAR=a,b");
}
TEST_F(UpdateEnvTest, RealWorld_LD_LIBRARY_PATH_Append)
{
env_vars.push_back(strdup("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib"));
original_envs.insert("LD_LIBRARY_PATH=/usr/lib:/usr/local/lib");
update_env(env_vars, "LD_LIBRARY_PATH", "/opt/rocm/lib", update_mode::APPEND, ":",
updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/opt/rocm/lib");
}
TEST_F(UpdateEnvTest, RealWorld_LD_PRELOAD_Prepend)
{
env_vars.push_back(strdup("LD_PRELOAD=/lib/existing.so"));
original_envs.insert("LD_PRELOAD=/lib/existing.so");
update_env(env_vars, "LD_PRELOAD", "/opt/rocm/librocprof-sys-dl.so",
update_mode::PREPEND, ":", updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0],
"LD_PRELOAD=/opt/rocm/librocprof-sys-dl.so:/lib/existing.so");
}
TEST_F(UpdateEnvTest, RealWorld_ROCPROFSYS_Environment_Variables)
{
update_env(env_vars, "ROCPROFSYS_TRACE", true, update_mode::REPLACE, ":",
updated_envs, original_envs);
update_env(env_vars, "ROCPROFSYS_PROFILE", false, update_mode::REPLACE, ":",
updated_envs, original_envs);
update_env(env_vars, "ROCPROFSYS_USE_SAMPLING", true, update_mode::REPLACE, ":",
updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 3);
EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_TRACE").c_str(),
"ROCPROFSYS_TRACE=true");
EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_PROFILE").c_str(),
"ROCPROFSYS_PROFILE=false");
EXPECT_STREQ(find_env_var(env_vars, "ROCPROFSYS_USE_SAMPLING").c_str(),
"ROCPROFSYS_USE_SAMPLING=true");
}
TEST_F(UpdateEnvTest, RealWorld_Timing_DoubleValues)
{
update_env(env_vars, "ROCPROFSYS_TRACE_DELAY", 1.5, update_mode::REPLACE, ":",
updated_envs, original_envs);
update_env(env_vars, "ROCPROFSYS_SAMPLING_FREQ", 100.0, update_mode::REPLACE, ":",
updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 2);
std::string delay_var = find_env_var(env_vars, "ROCPROFSYS_TRACE_DELAY");
std::string freq_var = find_env_var(env_vars, "ROCPROFSYS_SAMPLING_FREQ");
EXPECT_TRUE(delay_var.find("ROCPROFSYS_TRACE_DELAY=") == 0);
EXPECT_TRUE(freq_var.find("ROCPROFSYS_SAMPLING_FREQ=") == 0);
}
TEST_F(UpdateEnvTest, StringTypes_StdString)
{
std::string value = "test_string_value";
update_env(env_vars, "STRING_VAR", value, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "STRING_VAR=test_string_value");
}
TEST_F(UpdateEnvTest, StringTypes_ConstCharPtr)
{
const char* value = "const_char_value";
update_env(env_vars, "CHAR_VAR", value, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "CHAR_VAR=const_char_value");
}
TEST_F(UpdateEnvTest, EmptyStringValue)
{
update_env(env_vars, "EMPTY_VAR", "", update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "EMPTY_VAR=");
}
TEST_F(UpdateEnvTest, MultipleVariables_DifferentNames)
{
update_env(env_vars, "VAR1", "value1", update_mode::REPLACE, ":", updated_envs,
original_envs);
update_env(env_vars, "VAR2", "value2", update_mode::REPLACE, ":", updated_envs,
original_envs);
update_env(env_vars, "VAR3", "value3", update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 3);
EXPECT_EQ(updated_envs.size(), 3);
EXPECT_STREQ(find_env_var(env_vars, "VAR1").c_str(), "VAR1=value1");
EXPECT_STREQ(find_env_var(env_vars, "VAR2").c_str(), "VAR2=value2");
EXPECT_STREQ(find_env_var(env_vars, "VAR3").c_str(), "VAR3=value3");
}
TEST_F(UpdateEnvTest, NullPointer_InEnvironmentVector)
{
env_vars.push_back(strdup("VAR1=value1"));
env_vars.push_back(nullptr);
env_vars.push_back(strdup("VAR2=value2"));
original_envs.insert("VAR1=value1");
original_envs.insert("VAR2=value2");
update_env(env_vars, "VAR2", "new_value2", update_mode::REPLACE, ":", updated_envs,
original_envs);
EXPECT_STREQ(find_env_var(env_vars, "VAR2").c_str(), "VAR2=new_value2");
}
TEST_F(UpdateEnvTest, LongPath_Append)
{
std::string long_path = "/very/long/path/to/some/directory/with/many/subdirectories/"
"that/might/be/used/in/real/world";
env_vars.push_back(strdup("PATH=/usr/bin:/bin"));
original_envs.insert("PATH=/usr/bin:/bin");
update_env(env_vars, "PATH", long_path, update_mode::APPEND, ":", updated_envs,
original_envs);
std::string expected = "PATH=/usr/bin:/bin:" + long_path;
EXPECT_STREQ(env_vars[0], expected.c_str());
}
TEST_F(UpdateEnvTest, SpecialCharacters_InValue)
{
update_env(env_vars, "SPECIAL_VAR", "value-with_special.chars:123",
update_mode::REPLACE, ":", updated_envs, original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "SPECIAL_VAR=value-with_special.chars:123");
}
TEST_F(UpdateEnvTest, IntegerValues_Positive)
{
update_env(env_vars, "INT_VAR", 12345, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "INT_VAR=12345");
}
TEST_F(UpdateEnvTest, IntegerValues_Negative)
{
update_env(env_vars, "NEGATIVE_VAR", -999, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "NEGATIVE_VAR=-999");
}
TEST_F(UpdateEnvTest, IntegerValues_Zero)
{
update_env(env_vars, "ZERO_VAR", 0, update_mode::REPLACE, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "ZERO_VAR=0");
}
TEST_F(UpdateEnvTest, UpdateTracking_MultipleUpdates)
{
update_env(env_vars, "VAR1", "val1", update_mode::REPLACE, ":", updated_envs,
original_envs);
update_env(env_vars, "VAR2", "val2", update_mode::REPLACE, ":", updated_envs,
original_envs);
update_env(env_vars, "VAR1", "val1_updated", update_mode::REPLACE, ":", updated_envs,
original_envs);
EXPECT_EQ(updated_envs.count("VAR1"), 1);
EXPECT_EQ(updated_envs.count("VAR2"), 1);
EXPECT_EQ(updated_envs.size(), 2);
}
TEST_F(UpdateEnvTest, WeakMode_SequentialUpdates)
{
env_vars.push_back(strdup("CONFIG_VAR=initial"));
original_envs.insert("CONFIG_VAR=initial");
update_env(env_vars, "CONFIG_VAR", "weak_update", update_mode::WEAK, ":",
updated_envs, original_envs);
EXPECT_STREQ(env_vars[0], "CONFIG_VAR=weak_update");
free(env_vars[0]);
env_vars[0] = strdup("CONFIG_VAR=user_modified");
update_env(env_vars, "CONFIG_VAR", "another_weak_update", update_mode::WEAK, ":",
updated_envs, original_envs);
EXPECT_STREQ(env_vars[0], "CONFIG_VAR=user_modified");
}
TEST_F(UpdateEnvTest, Append_MultiplePathsInSequence)
{
update_env(env_vars, "BUILD_PATH", "/path1", update_mode::REPLACE, ":", updated_envs,
original_envs);
update_env(env_vars, "BUILD_PATH", "/path2", update_mode::APPEND, ":", updated_envs,
original_envs);
update_env(env_vars, "BUILD_PATH", "/path3", update_mode::APPEND, ":", updated_envs,
original_envs);
update_env(env_vars, "BUILD_PATH", "/path4", update_mode::APPEND, ":", updated_envs,
original_envs);
ASSERT_EQ(env_vars.size(), 1);
EXPECT_STREQ(env_vars[0], "BUILD_PATH=/path1:/path2:/path3:/path4");
}
@@ -22,7 +22,6 @@
#include "argparse.hpp"
#include "common/environment.hpp"
#include "common/join.hpp"
#include "common/path.hpp"
#include "config.hpp"
#include "exception.hpp"
@@ -81,71 +80,15 @@ get_clock_id_choices()
return std::make_pair(_choices, _aliases);
}
enum update_mode : int
{
UPD_REPLACE = 0, // no PREPEND/APPEND bits set
UPD_PREPEND = 1 << 0, // 0x01
UPD_APPEND = 1 << 1, // 0x02
UPD_WEAK = 1 << 2, // 0x04
};
using rocprofsys::common::update_mode;
template <typename Tp>
void
update_env(parser_data& _data, std::string_view _env_var, Tp&& _env_val,
update_mode&& _mode = UPD_REPLACE, std::string_view _join_delim = ":")
update_mode _mode = update_mode::REPLACE, std::string_view _join_delim = ":")
{
_data.updated.emplace(_env_var);
auto _prepend = (_mode & UPD_PREPEND) != 0;
auto _append = (_mode & UPD_APPEND) != 0;
auto _weak_upd = (_mode & UPD_WEAK) != 0;
// if both flags are set, prefer append
if(_prepend && _append)
{
_prepend = false;
}
auto _key = join("", _env_var, "=");
for(auto& itr : _data.current)
{
if(!itr) continue;
if(std::string_view{ itr }.find(_key) == 0)
{
if(_weak_upd)
{
// if the value has changed, do not update but allow overridding the value
// inherited from the initial env
if(_data.initial.find(std::string{ itr }) == _data.initial.end()) return;
}
if(_prepend || _append)
{
if(std::string_view{ itr }.find(join("", _env_val)) ==
std::string_view::npos)
{
auto _val = std::string{ itr }.substr(_key.length());
free(itr);
if(_prepend)
itr =
strdup(join('=', _env_var, join(_join_delim, _env_val, _val))
.c_str());
else
itr =
strdup(join('=', _env_var, join(_join_delim, _val, _env_val))
.c_str());
}
}
else
{
free(itr);
itr = strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str());
}
return;
}
}
_data.current.emplace_back(
strdup(rocprofsys::common::join('=', _env_var, _env_val).c_str()));
rocprofsys::common::update_env(_data.current, _env_var, std::forward<Tp>(_env_val),
_mode, _join_delim, _data.updated, _data.initial);
}
} // namespace
@@ -199,10 +142,10 @@ init_parser(parser_data& _data)
path::realpath(path::get_internal_libpath("librocprof-sys.so").c_str());
auto _libexecpath = path::realpath(path::get_internal_script_path());
update_env(_data, "ROCPROFSYS_SCRIPT_PATH", _libexecpath, UPD_REPLACE);
update_env(_data, "ROCPROFSYS_SCRIPT_PATH", _libexecpath, update_mode::REPLACE);
auto _rootpath = path::realpath(path::get_rocprofsys_root());
update_env(_data, "ROCPROFSYS_ROOT", _rootpath, UPD_REPLACE);
update_env(_data, "ROCPROFSYS_ROOT", _rootpath, update_mode::REPLACE);
return _data;
}
@@ -210,7 +153,7 @@ init_parser(parser_data& _data)
parser_data&
add_ld_preload(parser_data& _data)
{
update_env(_data, "LD_PRELOAD", _data.dl_libpath, UPD_APPEND);
update_env(_data, "LD_PRELOAD", _data.dl_libpath, update_mode::APPEND);
return _data;
}
@@ -219,7 +162,7 @@ add_ld_library_path(parser_data& _data)
{
auto _libdir = filepath::dirname(_data.dl_libpath);
if(filepath::exists(_libdir))
update_env(_data, "LD_LIBRARY_PATH", _libdir, UPD_APPEND);
update_env(_data, "LD_LIBRARY_PATH", _libdir, update_mode::APPEND);
return _data;
}
@@ -413,9 +356,9 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
if(!_modes.empty())
{
update_env(_data, "ROCPROFSYS_SAMPLING_CPUTIME",
_modes.count("cputime") > 0, UPD_WEAK);
_modes.count("cputime") > 0, update_mode::WEAK);
update_env(_data, "ROCPROFSYS_SAMPLING_REALTIME",
_modes.count("realtime") > 0, UPD_WEAK);
_modes.count("realtime") > 0, update_mode::WEAK);
}
});
@@ -472,11 +415,11 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
.dtype("seconds")
.action([&](parser_t& p) {
update_env(_data, "ROCPROFSYS_TRACE_DELAY", p.get<double>("wait"),
UPD_WEAK);
update_mode::WEAK);
update_env(_data, "ROCPROFSYS_SAMPLING_DELAY", p.get<double>("wait"),
UPD_WEAK);
update_mode::WEAK);
update_env(_data, "ROCPROFSYS_CAUSAL_DELAY", p.get<double>("wait"),
UPD_WEAK);
update_mode::WEAK);
});
_data.processed_environs.emplace("wait");
@@ -493,11 +436,11 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
.dtype("seconds")
.action([&](parser_t& p) {
update_env(_data, "ROCPROFSYS_TRACE_DURATION", p.get<double>("duration"),
UPD_WEAK);
update_mode::WEAK);
update_env(_data, "ROCPROFSYS_SAMPLING_DURATION",
p.get<double>("duration"), UPD_WEAK);
p.get<double>("duration"), update_mode::WEAK);
update_env(_data, "ROCPROFSYS_CAUSAL_DURATION", p.get<double>("duration"),
UPD_WEAK);
update_mode::WEAK);
});
_data.processed_environs.emplace("duration");
@@ -516,7 +459,7 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
update_env(
_data, "ROCPROFSYS_TRACE_PERIODS",
join(array_config_t{ " ", "", "" }, p.get<strvec_t>("periods")),
UPD_WEAK);
update_mode::WEAK);
});
_data.processed_environs.emplace("periods");
@@ -584,7 +527,7 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
if(_v.count("all") > 0 || _v.count("kokkosp") > 0)
update_env(_data, "KOKKOS_TOOLS_LIBS", _data.omni_libpath,
UPD_PREPEND);
update_mode::PREPEND);
});
_data.processed_environs.emplace("include");
@@ -20,9 +20,17 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
add_executable(rocprof-sys-unit-tests dummy.cpp $<TARGET_OBJECTS:trace-cache-tests>)
add_executable(
rocprof-sys-unit-tests
dummy.cpp
$<TARGET_OBJECTS:lib-common-tests>
$<TARGET_OBJECTS:trace-cache-tests>
)
target_link_libraries(
rocprof-sys-unit-tests
PRIVATE rocprofiler-systems-googletest-library rocprofiler-systems-core-library
PRIVATE
rocprofiler-systems-googletest-library
rocprofiler-systems-common-library
rocprofiler-systems-core-library
)