Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Additional configuration options to control the tracing behavior include:
- ``ROCPROFSYS_TRACE_DELAY`` (``--trace-wait``): Delay before starting trace collection (in seconds).
- ``ROCPROFSYS_TRACE_DURATION`` (``--trace-duration``): Duration of trace collection (in seconds).
- ``ROCPROFSYS_TRACE_PERIODS`` (``--trace-periods``): Specifies multiple delay/duration periods in the format ``<DELAY>:<DURATION>``, ``<DELAY>:<DURATION>:<REPEAT>``, or ``<DELAY>:<DURATION>:<REPEAT>:<CLOCK_ID>``.
- ``ROCPROFSYS_TRACE_PERIOD_CLOCK_ID`` (``--trace-clock-id``): Clock type for timing, such as ``realtime``, ``monotonic``, ``cputime``.
- ``ROCPROFSYS_TRACE_PERIOD_CLOCK_ID`` (``--trace-clock-id``): Clock type for timing, either ``realtime`` (default) or ``cputime``.

Profile mode
^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
33 changes: 2 additions & 31 deletions projects/rocprofiler-systems/source/lib/core/argparse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "gpu.hpp"
#include "state.hpp"
#include <cstdint>
#include <tuple>

#include <timemory/settings/types.hpp>

Expand All @@ -26,32 +25,6 @@ namespace argparse
{
namespace
{
auto
get_clock_id_choices()
{
#define ROCPROFSYS_CLOCK_IDENTIFIER(VAL) \
std::make_tuple(utility::string::clock_name(#VAL), VAL, std::string_view{ #VAL })

auto _choices = strvec_t{};
auto _aliases = std::map<std::string, strvec_t>{};
for(auto itr : { ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_REALTIME),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_PROCESS_CPUTIME_ID),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC_RAW),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_REALTIME_COARSE),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_MONOTONIC_COARSE),
ROCPROFSYS_CLOCK_IDENTIFIER(CLOCK_BOOTTIME) })
{
auto _choice = std::to_string(std::get<1>(itr));
_choices.emplace_back(_choice);
_aliases[_choice] = { std::get<0>(itr), std::string{ std::get<2>(itr) } };
}

#undef ROCPROFSYS_CLOCK_IDENTIFIER

return std::make_pair(_choices, _aliases);
}

using rocprofsys::common::update_mode;

// NOLINTBEGIN - ignore argument number issue
Expand Down Expand Up @@ -774,7 +747,6 @@ add_core_arguments(parser_t& _parser, parser_data& _data)

if(_data.reg.environ_filter("trace_clock_id", _data))
{
auto _clock_id_choices = get_clock_id_choices();
_parser
.add_argument(
{ "--trace-clock-id" },
Expand All @@ -791,10 +763,9 @@ add_core_arguments(parser_t& _parser, parser_data& _data)
.dtype("clock-id")
.action([&](parser_t& p) {
update_env(_data, env_vars::TRACE_PERIOD_CLOCK_ID,
p.get<double>("trace-clock-id"));
p.get<std::string>("trace-clock-id"));
})
.choices(_clock_id_choices.first)
.choice_aliases(_clock_id_choices.second);
.choices({ "realtime", "cputime" });

_data.reg.processed_environs.emplace("trace_clock_id");
_data.reg.processed_environs.emplace("trace_period_clock_id");
Expand Down
42 changes: 26 additions & 16 deletions projects/rocprofiler-systems/source/lib/core/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,29 @@ struct config_value_validation
// ROCPROFSYS_TRACE_DURATION=abc -> 0.0. If timemory callbacks become
// unconditional and available before constructor parsing, the explicit env and
// config-file validation paths can be removed.
const auto strict_config_value_validations = std::array<config_value_validation, 5>{ {
{ env_vars::MODE, config_value_rule::choice, "one of the registered choices" },
{ env_vars::PERFETTO_BACKEND, config_value_rule::choice,
"one of the registered choices" },
{ env_vars::TRACE, config_value_rule::boolean,
"a boolean value (0, non-zero integer, true, false, on, off, yes, no, "
"y, n, t, f)" },
{ env_vars::TRACE_DURATION, config_value_rule::floating_point,
"a finite floating-point value" },
const auto k_strict_config_value_validations = std::array<config_value_validation, 6>{ {
{ .name = env_vars::MODE,
.rule = config_value_rule::choice,
.expectation = "one of the registered choices" },
{ .name = env_vars::PERFETTO_BACKEND,
.rule = config_value_rule::choice,
.expectation = "one of the registered choices" },
{ .name = env_vars::TRACE,
.rule = config_value_rule::boolean,
.expectation =
"a boolean value (0, non-zero integer, true, false, on, off, yes, no, "
"y, n, t, f)" },
{ .name = env_vars::TRACE_DURATION,
.rule = config_value_rule::floating_point,
.expectation = "a finite floating-point value" },
// Only validate positive ranges for settings without sentinel values.
// CPUTIME/REALTIME sampling frequencies intentionally default to -1.0.
{ env_vars::SAMPLING_FREQ, config_value_rule::positive_floating_point,
"a positive finite floating-point value" },
{ .name = env_vars::SAMPLING_FREQ,
.rule = config_value_rule::positive_floating_point,
.expectation = "a positive finite floating-point value" },
{ .name = env_vars::TRACE_PERIOD_CLOCK_ID,
.rule = config_value_rule::choice,
.expectation = "one of the registered choices" },
} };

[[nodiscard]] bool
Expand Down Expand Up @@ -237,10 +247,10 @@ get_setting_choices(const std::shared_ptr<settings>& _config, std::string_view n
[[nodiscard]] const config_value_validation*
find_config_value_validation(std::string_view name)
{
auto itr = std::find_if(
strict_config_value_validations.begin(), strict_config_value_validations.end(),
auto itr = std::ranges::find_if(
k_strict_config_value_validations,
[name](const auto& validation) { return validation.name == name; });
return (itr != strict_config_value_validations.end()) ? &*itr : nullptr;
return (itr != k_strict_config_value_validations.end()) ? &*itr : nullptr;
}

void
Expand Down Expand Up @@ -294,7 +304,7 @@ validate_config_setting_value(std::string_view name, std::string_view raw_value,
void
validate_environment_config_values(const std::shared_ptr<settings>& _config)
{
for(const auto& validation : strict_config_value_validations)
for(const auto& validation : k_strict_config_value_validations)
{
if(auto* raw_value = std::getenv(std::string{ validation.name }.c_str()))
validate_config_setting_value(validation.name, raw_value,
Expand Down Expand Up @@ -362,7 +372,7 @@ validate_config_file_values(const std::string& config_file, const std::string& t
void
install_strict_config_value_callbacks(const std::shared_ptr<settings>& _config)
{
for(const auto& validation : strict_config_value_validations)
for(const auto& validation : k_strict_config_value_validations)
{
auto itr = _config->find(std::string{ validation.name });
if(itr == _config->end() || !itr->second) continue;
Expand Down
10 changes: 10 additions & 0 deletions projects/rocprofiler-systems/tests/pytest/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
r"[Ii]nvalid value.*ROCPROFSYS_SAMPLING_FREQ",
id="numeric-range",
),
pytest.param(
{"ROCPROFSYS_TRACE_PERIOD_CLOCK_ID": "boottime"},
r"[Ii]nvalid value.*ROCPROFSYS_TRACE_PERIOD_CLOCK_ID",
id="trace-period-clock-id",
),
]

VALID_BOOLEAN_VALUES = [
Expand Down Expand Up @@ -90,6 +95,11 @@
r"[Ii]nvalid value.*ROCPROFSYS_SAMPLING_FREQ",
id="numeric-range",
),
pytest.param(
{"ROCPROFSYS_TRACE_PERIOD_CLOCK_ID": "cputime"},
r"[Ii]nvalid value.*ROCPROFSYS_TRACE_PERIOD_CLOCK_ID",
id="trace-period-clock-id",
),
]


Expand Down
28 changes: 28 additions & 0 deletions projects/rocprofiler-systems/tests/pytest/test_time_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,31 @@ def test_delay(self, mode, time_window_env):
counts=[1, 1],
depths=[0, 0],
)

def test_delay_cputime_clock(self, mode, time_window_env):
"""Regression test: --trace-clock-id used to crash the profiled process
for every value (numeric clock id written to the settings string that
only ever matched "realtime"/"cputime").

Uses a small process-CPU-time budget rather than test_delay's
wall-clock values: this app is dominated by sleep_for() and only
generates ~500ms of real CPU time total across its 5 calls, so a
larger cputime budget can starve indefinitely (clock_nanosleep on
CLOCK_PROCESS_CPUTIME_ID never returns once nothing in the process
is left consuming CPU)."""
env = time_window_env.copy()
env.update(
{
"ROCPROFSYS_TRACE_DELAY": "0.05",
"ROCPROFSYS_TRACE_DURATION": "0.1",
"ROCPROFSYS_TRACE_PERIOD_CLOCK_ID": "cputime",
}
)
result = self.run_test(
mode,
"trace-time-window",
env=env,
binary_rewrite_args=self.BINARY_REWRITE_ARGS,
runtime_instrument_args=self.RUNTIME_INSTRUMENT_ARGS,
)
self.assert_regex(result)
Loading