Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 13 additions & 4 deletions src/runtime_src/core/tools/common/SmiWatchMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ------ I N C L U D E F I L E S -------------------------------------------
// Local - Include Files
#include "SmiWatchMode.h"
#include "XBUtilitiesCore.h"
#include "core/common/query_requests.h"
#include "core/common/time.h"

Expand Down Expand Up @@ -104,10 +105,12 @@ void
smi_watch_mode::
run_watch_mode(const xrt_core::device* device,
std::ostream& output,
const ReportGenerator& report_generator)
const ReportGenerator& report_generator,
unsigned refresh_interval_seconds,
bool refresh_terminal)
{
if (!device || !report_generator) {
output << "Error: Invalid device or report generator provided to watch mode\n";
if (!report_generator) {
output << "Error: Invalid report generator provided to watch mode\n";
return;
}

Expand All @@ -116,9 +119,15 @@ run_watch_mode(const xrt_core::device* device,

signal_handler::reset_interrupt();

bool first_iteration = true;
while (signal_handler::active()) {
if (!first_iteration && refresh_interval_seconds > 0)
std::this_thread::sleep_for(std::chrono::seconds(refresh_interval_seconds));
first_iteration = false;

try {
// Generate current report
if (refresh_terminal && !XBUtilities::is_escape_codes_disabled())
output << "\033[2J\033[H";
output << report_generator(device);
output.flush();
}
Expand Down
38 changes: 13 additions & 25 deletions src/runtime_src/core/tools/common/SmiWatchMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,14 @@ class smi_debug_buffer {
* by any XRT-SMI report. It handles:
* - Element filter parsing for watch mode options
* - Signal handling (Ctrl+C interruption) with graceful cleanup
* - Screen clearing with ANSI escape codes for real-time updates
* - Timing and interval management (1-second intervals)
* - Optional terminal clear between updates (examine --watch only)
* - Optional refresh interval (seconds; 0 = no delay between updates)
* - Cross-platform compatibility (Windows/POSIX)
*
* Usage Example:
* @code
* if (smi_watch_mode::parse_watch_mode_options(elements_filter)) {
* auto generator = [](const xrt_core::device* dev, const std::vector<std::string>& filters) {
* return my_report_function(dev, filters);
* };
* smi_watch_mode::run_watch_mode(device, elements_filter, std::cout, generator, "My Report");
* }
* smi_watch_mode::run_watch_mode(device, std::cout, report_generator); // no delay
* smi_watch_mode::run_watch_mode(device, std::cout, report_generator, interval); // seconds between updates
* @endcode
*/
class smi_watch_mode {
Expand Down Expand Up @@ -118,27 +114,19 @@ class smi_watch_mode {

/**
* @brief Run watch mode with the provided report generator
*
* @param device The XRT device to query for real-time data
* @param elements_filter Element filters (watch-specific filters will be filtered out automatically)
*
* @param device May be nullptr for host-only report generators.
* @param output Output stream for the report (typically std::cout)
* @param report_generator Function to generate report content for each iteration
* @param report_title Title to display in watch mode header (e.g., "Context Health")
*
* This function implements the complete watch mode workflow:
* - Sets up SIGINT (Ctrl+C) signal handling for graceful interruption
* - Runs an infinite loop with 1-second intervals until interrupted
* - Clears screen using ANSI escape codes for real-time updates
* - Only updates display when report content actually changes (efficiency)
* - Shows timestamp using XRT's native timestamp format (GMT)
* - Restores original signal handler on exit
* - Handles all exceptions internally with error reporting
*
* @note This function blocks until user interrupts with Ctrl+C
* @note Thread-safe signal handling using atomic variables
* @param refresh_interval_seconds Seconds to sleep after each update before the next (default 0 = no sleep)
* @param refresh_terminal If true, clear the terminal before each update after the first (examine reports only).
*
* @note Blocks until user interrupts with Ctrl+C
*/
static void
run_watch_mode(const xrt_core::device* device,
std::ostream& output,
const ReportGenerator& report_generator);
const ReportGenerator& report_generator,
unsigned refresh_interval_seconds = 0,
bool refresh_terminal = false);
};
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ OO_ContextHealth::execute(const SubCmdOptions& _options) const
};

if (m_watch) {
// Watch mode: continuously monitor
smi_watch_mode::run_watch_mode(device.get(), std::cout, report_generator);
} else {
// Single report
Expand Down
32 changes: 30 additions & 2 deletions src/runtime_src/core/tools/xbutil2/SubCmdExamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include "tools/common/XBHelpMenusCore.h"
#include "tools/common/XBUtilitiesCore.h"
#include "tools/common/XBUtilities.h"
#include "tools/common/SmiWatchMode.h"

#include <boost/algorithm/string.hpp>

// ---- OptionOptions ------
#include "tools/common/OptionOptions.h"
Expand Down Expand Up @@ -45,6 +48,7 @@

#include <filesystem>
#include <fstream>
#include <sstream>

SubCmdExamine::SubCmdExamine(bool _isHidden, bool _isDepricated, bool _isPreliminary)
: SubCmd("examine", "Status of the system and device")
Expand Down Expand Up @@ -107,7 +111,12 @@ void SubCmdExamine::fill_option_values(const po::variables_map& vm, SubCmdExamin
options.m_output = vm.count("output") ? vm["output"].as<std::string>() : "";
options.m_reportNames = vm.count("report") ? vm["report"].as<std::vector<std::string>>() : std::vector<std::string>();
options.m_help = vm.count("help") ? vm["help"].as<bool>() : false;
options.m_elementsFilter = vm.count("element") ? vm["element"].as<std::vector<std::string>>() : std::vector<std::string>();
options.m_elementsFilter = vm.count("element") ? vm["element"].as<std::vector<std::string>>() : std::vector<std::string>();
options.m_watchIntervalSec.reset();
if (vm.count("watch")) {
const auto& s = vm["watch"].as<std::string>();
options.m_watchIntervalSec = static_cast<unsigned>(std::stoul(s.empty() ? "0" : s));
}
}

void
Expand Down Expand Up @@ -226,6 +235,13 @@ SubCmdExamine::execute(const SubCmdOptions& _options) const
if (!options.m_output.empty() && std::filesystem::exists(options.m_output) && !XBU::getForce())
throw xrt_core::error((boost::format("The output file '%s' already exists. Please either remove it or execute this command again with the '--force' option to overwrite it") % options.m_output).str());

if (options.m_watchIntervalSec) {
if (vm.count("format"))
throw xrt_core::error("Watch mode cannot be used with --format; output is text only.");
if (!options.m_output.empty())
throw xrt_core::error("Watch mode cannot be used with --output.");
}

} catch (const xrt_core::error& e) {
// Catch only the exceptions that we have generated earlier
std::cerr << boost::format("ERROR: %s\n") % e.what();
Expand Down Expand Up @@ -316,7 +332,19 @@ SubCmdExamine::execute(const SubCmdOptions& _options) const
// Create the report
std::ostringstream oSchemaOutput;
try {
XBU::produce_reports(device, reportsToProcess, schemaVersion, options.m_elementsFilter, std::cout, oSchemaOutput);
if (options.m_watchIntervalSec) {
/* Bundle produce_reports() into a lamda and pass it to run_watch_mode() */
const auto examine_watch_snapshot =
[&](const xrt_core::device*) {
std::ostringstream console;
XBU::produce_reports(device, reportsToProcess, schemaVersion, {}, console, oSchemaOutput);
return console.str();
};
smi_watch_mode::run_watch_mode(device.get(), std::cout, examine_watch_snapshot,
*options.m_watchIntervalSec, true);
} else {
XBU::produce_reports(device, reportsToProcess, schemaVersion, {}, std::cout, oSchemaOutput);
}
} catch (const std::exception&) {
// Exception is thrown at the end of this function to allow for report writing
is_report_output_valid = false;
Expand Down
2 changes: 2 additions & 0 deletions src/runtime_src/core/tools/xbutil2/SubCmdExamine.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

// System - Include Files
#include <memory>
#include <optional>

namespace XBU = XBUtilities;
namespace po = boost::program_options;
Expand All @@ -26,6 +27,7 @@ struct SubCmdExamineOptions {
std::string m_format;
std::string m_output;
bool m_help;
std::optional<unsigned> m_watchIntervalSec;
};
class SubCmdExamine : public SubCmd {
ReportCollection uniqueReportCollection;
Expand Down
Loading