// 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. #include "avail.hpp" #include "library/api.hpp" #include "library/components/backtrace.hpp" #include "library/components/fork_gotcha.hpp" #include "library/components/mpi_gotcha.hpp" #include "library/components/omnitrace.hpp" #include "library/components/pthread_gotcha.hpp" #include "library/components/roctracer.hpp" #include "library/components/user_region.hpp" #include "library/config.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(TIMEMORY_UNIX) # include // ioctl() and TIOCGWINSZ # include // for STDOUT_FILENO #elif defined(TIMEMORY_WINDOWS) # include #endif using namespace tim; template using array_t = std::array; using string_t = std::string; using stringstream_t = std::stringstream; using str_vec_t = std::vector; using str_set_t = std::set; using info_type_base = std::tuple; using parser_t = tim::argparse::argument_parser; struct info_type : info_type_base { TIMEMORY_DEFAULT_OBJECT(info_type) template info_type(Args&&... _args) : info_type_base{ std::forward(_args)... } {} const auto& name() const { return std::get<0>(*this); } auto is_available() const { return std::get<1>(*this); } const auto& info() const { return std::get<2>(*this); } const auto& data_type() const { return info().at(0); } const auto& enum_type() const { return info().at(1); } const auto& id_type() const { return info().at(2); } const auto& id_strings() const { return info().at(3); } const auto& label() const { return info().at(4); } const auto& description() const { return info().at(5); } const auto& categories() const { return info().at(6); } bool valid() const { return !name().empty() && info().size() >= 6; } bool operator<(const info_type& rhs) const { return name() < rhs.name(); } bool operator!=(const info_type& rhs) const { return !(*this == rhs); } bool operator==(const info_type& rhs) const { if(info().size() != rhs.info().size()) return false; for(size_t i = 0; i < info().size(); ++i) { if(info().at(i) != rhs.info().at(i)) return false; } return name() == rhs.name() && is_available() == rhs.is_available(); } }; //--------------------------------------------------------------------------------------// enum { VAL = 0, ENUM = 1, LANG = 2, CID = 3, FNAME = 4, DESC = 5, CATEGORY = 6, TOTAL = 7 }; //--------------------------------------------------------------------------------------// namespace { auto global_delim = std::string{ "|" }; bool csv = false; bool markdown = false; bool alphabetical = false; bool available_only = false; bool all_info = false; bool force_brief = false; bool debug_msg = false; bool case_insensitive = false; bool regex_hl = false; int32_t max_width = 0; int32_t num_cols = 0; int32_t min_width = 40; int32_t padding = 4; str_vec_t regex_keys = {}; str_vec_t category_regex_keys = {}; str_set_t category_view = {}; constexpr size_t num_component_options = 7; constexpr size_t num_settings_options = 4; constexpr size_t num_hw_counter_options = 4; auto lerr = std::stringstream{}; // explicit setting names to exclude std::set settings_exclude = { "OMNITRACE_ENVIRONMENT", "OMNITRACE_COMMAND_LINE", "cereal_class_version", "settings", #if !defined(TIMEMORY_USE_CRAYPAT) "OMNITRACE_CRAYPAT" #endif }; // exclude some timemory settings which are not relevant to omnitrace // exact matches, e.g. OMNITRACE_BANNER std::string settings_rexclude_exact = "^OMNITRACE_(BANNER|DESTRUCTOR_REPORT|COMPONENTS|(GLOBAL|MPIP|NCCLP|OMPT|" "PROFILER|TRACE|KOKKOS)_COMPONENTS|PYTHON_EXE|PAPI_ATTACH|PLOT_OUTPUT|SEPARATOR_" "FREQ|" "STACK_CLEARING|TARGET_PID|THROTTLE_(COUNT|VALUE)|(AUTO|FLAMEGRAPH)_OUTPUT|" "(ENABLE|DISABLE)_ALL_SIGNALS|ALLOW_SIGNAL_HANDLER|CTEST_NOTES|INSTRUCTION_" "ROOFLINE|ADD_SECONDARY)$"; // leading matches, e.g. OMNITRACE_MPI_[A-Z_]+ std::string settings_rexclude_begin = "^OMNITRACE_(ERT|DART|MPI|UPCXX|ROOFLINE|CUDA|NVTX|CUPTI)_[A-Z_]+$"; bool exclude_setting(const std::string&); } // namespace //--------------------------------------------------------------------------------------// void dump_log() { if(debug_msg) { std::cerr << lerr.str() << std::flush; lerr = std::stringstream{}; } } void dump_log_abort(int _v) { fprintf(stderr, "\n[omnitrace-avail] Exiting with signal %i...\n", _v); debug_msg = true; dump_log(); } template static IntArrayT compute_max_columns(IntArrayT _widths, BoolArrayT _using); string_t remove(string_t inp, const std::set& entries); template void write_entry(std::ostream& os, const Tp& _entry, int64_t _w, bool center, bool mark); template string_t banner(IntArrayT _breaks, std::array _use, char filler = '-', char delim = '|'); bool is_selected(const std::string& line); bool is_category_selected(const std::string& _line); std::string hl_selected(const std::string& line); template void write_component_info(std::ostream&, const array_t&, const array_t&, const array_t&); template void write_settings_info(std::ostream&, const array_t& = {}, const array_t& = {}, const array_t& = {}); template void write_hw_counter_info(std::ostream&, const array_t& = {}, const array_t& = {}, const array_t& = {}); template struct get_availability; template struct component_categories; void process_categories(parser_t&, const str_set_t&); //--------------------------------------------------------------------------------------// template struct get_availability { using this_type = get_availability; using metadata_t = component::metadata; using property_t = component::properties; static info_type get_info(); auto operator()() const { return get_info(); } }; //--------------------------------------------------------------------------------------// template struct get_availability> { using data_type = std::vector; static data_type get_info(data_type& _v) { TIMEMORY_FOLD_EXPRESSION(_v.emplace_back(get_availability::get_info())); return _v; } static data_type get_info() { data_type _v{}; return get_info(_v); } template decltype(auto) operator()(Args&&... _args) { return get_info(std::forward(_args)...); } }; //--------------------------------------------------------------------------------------// template <> struct get_availability { template decltype(auto) operator()(tim::type_list, Args&&... _args) const { return get_availability>{}(std::forward(_args)...); } template decltype(auto) operator()(Args&&... _args) const { return get_availability>{}(std::forward(_args)...); } }; //--------------------------------------------------------------------------------------// template struct component_categories { template void operator()(std::set& _v, type_list) const { // auto _cleanup = [](std::string _type, const std::string& _pattern) { auto _pos = std::string::npos; while((_pos = _type.find(_pattern)) != std::string::npos) _type.erase(_pos, _pattern.length()); return _type; }; (void) _cleanup; // unused but set if sizeof...(Tp) == 0 TIMEMORY_FOLD_EXPRESSION(_v.emplace( TIMEMORY_JOIN("::", "component", _cleanup(demangle(), "tim::")))); } void operator()(std::set& _v) const { if constexpr(!concepts::is_placeholder::value) (*this)(_v, trait::component_apis_t{}); } }; template <> struct component_categories { template void operator()(std::set& _v, std::index_sequence) const { TIMEMORY_FOLD_EXPRESSION( component_categories>{}(_v)); } void operator()(std::set& _v) const { (*this)(_v, std::make_index_sequence{}); } auto operator()() const { std::set _categories{}; (*this)(_categories); return _categories; } }; //--------------------------------------------------------------------------------------// int main(int argc, char** argv) { tim::set_env("OMNITRACE_INIT_TOOLING", "OFF", 1); omnitrace_init_library(); std::set _category_options = component_categories{}(); { auto _settings = tim::settings::shared_instance(); for(const auto& itr : *_settings) { if(exclude_setting(itr.second->get_env_name())) continue; auto _categories = itr.second->get_categories(); if(_categories.find("native") != _categories.end()) { _categories.erase("native"); _categories.emplace("timemory"); itr.second->set_categories(_categories); } for(const auto& eitr : itr.second->get_categories()) { _category_options.emplace(TIMEMORY_JOIN("::", "settings", eitr)); } } } array_t options = { false, false, false, false, false, false, false }; array_t fields = {}; array_t use_mark = {}; std::string cols_via{}; std::tie(num_cols, cols_via) = tim::utility::console::get_columns(); std::string col_msg = "(default: " + std::to_string(num_cols) + " [via " + cols_via + "])"; fields[VAL] = "VALUE_TYPE"; fields[ENUM] = "ENUMERATION"; fields[LANG] = "C++ ALIAS / PYTHON ENUMERATION"; fields[FNAME] = "FILENAME"; fields[CID] = "STRING_IDS"; fields[DESC] = "DESCRIPTION"; fields[CATEGORY] = "CATEGORY"; use_mark[VAL] = true; use_mark[ENUM] = true; use_mark[LANG] = true; use_mark[FNAME] = false; use_mark[CID] = false; use_mark[DESC] = false; use_mark[CATEGORY] = false; bool include_settings = false; bool include_components = false; bool include_hw_counters = false; std::string file = {}; parser_t parser("omnitrace-avail"); parser.enable_help(); parser.set_help_width(40); parser.add_argument({ "--debug" }, "Enable debug messages") .max_count(1) .action([](parser_t& p) { debug_msg = p.get("debug"); }); parser.add_argument({ "-a", "--all" }, "Print all available info") .max_count(1) .action([&](parser_t& p) { all_info = p.get("all"); if(all_info) { for(auto& itr : options) itr = true; options[ENUM] = false; options[LANG] = false; include_components = true; include_settings = true; include_hw_counters = true; } }); parser.add_argument({ "" }, ""); parser.add_argument({ "[CATEGORIES]" }, ""); parser .add_argument({ "-S", "--settings", "--print-settings" }, "Display the runtime settings") .max_count(1); parser .add_argument({ "-C", "--components", "--print-components" }, "Only display the components data") .max_count(1); parser .add_argument({ "-H", "--hw-counters", "--print-hw-counters" }, "Write the available hardware counters") .max_count(1); parser.add_argument({ "" }, ""); parser.add_argument({ "[VIEW OPTIONS]" }, ""); parser .add_argument({ "-A", "--available" }, "Only display available components/settings/hw-counters") .max_count(1) .action([](parser_t& p) { available_only = p.get("available"); }); parser .add_argument({ "-r", "--filter" }, "Filter the output according to provided regex (egrep + " "case-sensitive) [e.g. -r \"true\"]. Prefix " "with '~' to suppress matches") .min_count(1) .dtype("list of strings") .action([](parser_t& p) { regex_keys = p.get("filter"); }); parser .add_argument({ "-R", "--category-filter" }, "Filter the output according to provided regex w.r.t. the " "categories (egrep + case-sensitive) [e.g. -r \"true\"]. Prefix " "with '~' to suppress matches") .min_count(1) .dtype("list of strings") .action([](parser_t& p) { category_regex_keys = p.get("category-filter"); }); parser.add_argument({ "-i", "--ignore-case" }, "Ignore case when filtering") .max_count(1) .dtype("bool") .action([](parser_t& p) { case_insensitive = p.get("ignore-case"); }); parser .add_argument({ "-p", "--hl", "--highlight" }, "Highlight regex matches (only available on UNIX)") .max_count(1) .action([](parser_t&) { regex_hl = true; }); parser.add_argument({ "--alphabetical" }, "Sort the output alphabetically") .max_count(1) .action([](parser_t& p) { alphabetical = p.get("alphabetical"); }); parser .add_argument({ "--list-categories" }, "List the available categories for --categories option") .count(0) .action([_category_options](parser_t&) { std::cout << "Categories:\n"; for(const auto& itr : _category_options) std::cout << " " << itr << "\n"; }); parser.add_argument({ "" }, ""); parser.add_argument({ "[COLUMN OPTIONS]" }, ""); parser.add_argument({ "-b", "--brief" }, "Suppress availability/value info") .max_count(1) .action([](parser_t& p) { force_brief = p.get("brief"); }); parser.add_argument({ "-d", "--description" }, "Display the component description") .max_count(1); parser .add_argument({ "--categories" }, "Display the category information (use --list-categories to see " "the available categories)") .dtype("string") .action([&_category_options](parser_t& p) { process_categories(p, _category_options); }); parser.add_argument({ "-s", "--string" }, "Display all acceptable string identifiers") .max_count(1); parser .add_argument({ "-v", "--value" }, "Display the component data storage value type") .max_count(1); parser .add_argument({ "-f", "--filename" }, "Display the output filename for the component") .max_count(1); parser.add_argument({ "" }, ""); parser.add_argument({ "[WIDTH OPTIONS]" }, ""); parser .add_argument({ "-w", "--width" }, "if w > 0, truncate any columns greater than this width") .count(1) .dtype("int") .action([](parser_t& p) { max_width = p.get("width"); }); parser .add_argument( { "-c", "--columns" }, std::string{ "if c > 0, truncate the total width of all the columns to this " "value. Set '-w 0 -c 0' to remove all truncation" } + col_msg) .set_default(num_cols) .count(1) .dtype("int") .action([](parser_t& p) { num_cols = p.get("columns"); }); parser.add_argument({ "" }, ""); parser.add_argument({ "[OUTPUT OPTIONS]" }, ""); parser.add_argument({ "-O", "--output" }, "Write results to file") .count(1) .dtype("filename"); parser.add_argument({ "-M", "--markdown" }, "Write data in markdown") .max_count(1) .action([](parser_t& p) { markdown = p.get("markdown"); }); parser.add_argument({ "--csv" }, "Write data in csv") .max_count(1) .action([](parser_t& p) { csv = p.get("csv"); if(!p.exists("csv-separator")) global_delim = ","; }); parser .add_argument({ "--csv-separator" }, "Use the provided string instead of a ',' to separate values") .max_count(1) .action([](parser_t& p) { global_delim = p.get("csv-separator"); }); parser.add_positional_argument("REGEX_FILTER").set_default(std::string{}); auto err = parser.parse(argc, argv); if(parser.exists("help")) { parser.print_help(); return EXIT_SUCCESS; } if(err) { std::cerr << err << std::endl; parser.print_help(); return EXIT_FAILURE; } if(parser.exists("markdown") && parser.exists("csv")) { std::cerr << "Error! both '--markdown' and '--csv' options cannot be specified\n"; return EXIT_FAILURE; } if(parser.exists("list-categories")) return EXIT_SUCCESS; std::string _pos_regex{}; if(parser.get_positional_count() > 0) { err = parser.get("REGEX_FILTER", _pos_regex); if(err) { std::cerr << err << std::endl; parser.print_help(); return EXIT_FAILURE; } } if(!_pos_regex.empty()) { regex_keys.emplace_back(_pos_regex); category_regex_keys.emplace_back(_pos_regex); } auto _parser_set_if_exists = [&parser](auto& _var, const std::string& _opt) { using Tp = decay_t; if(parser.exists(_opt)) _var = parser.get(_opt); }; _parser_set_if_exists(options[FNAME], "filename"); _parser_set_if_exists(options[DESC], "description"); _parser_set_if_exists(options[VAL], "value"); _parser_set_if_exists(options[CID], "string"); _parser_set_if_exists(options[CATEGORY], "categories"); _parser_set_if_exists(file, "output"); _parser_set_if_exists(include_components, "components"); _parser_set_if_exists(include_settings, "settings"); _parser_set_if_exists(include_hw_counters, "hw-counters"); if(category_view.empty()) category_view = _category_options; if(!include_components && !include_settings && !include_hw_counters) include_settings = true; if(markdown || include_hw_counters) padding = 6; std::ostream* os = nullptr; std::ofstream ofs; if(!file.empty()) { ofs.open(file.c_str()); if(ofs) { os = &ofs; } else { std::cerr << "Error opening output file: " << file << std::endl; } } signal(SIGABRT, &dump_log_abort); signal(SIGSEGV, &dump_log_abort); signal(SIGQUIT, &dump_log_abort); if(!os) os = &std::cout; if(include_components) { write_component_info(*os, options, use_mark, fields); } dump_log(); if(include_settings) { write_settings_info( *os, { options[VAL], options[LANG], options[DESC], options[CATEGORY] }); } dump_log(); if(include_hw_counters) { write_hw_counter_info(*os, { true, !force_brief && !available_only, !options[DESC], options[DESC] }); } dump_log(); return 0; } //--------------------------------------------------------------------------------------// template struct enumerated_list; template