diff --git a/src/backends/extrae/CMakeLists.txt b/src/backends/extrae/CMakeLists.txt index 319ef9c27d5508c01cc65f0459e4f3ff4b222c0a..e8ec467adcb3b949d497598cec807f4d962e1ae0 100644 --- a/src/backends/extrae/CMakeLists.txt +++ b/src/backends/extrae/CMakeLists.txt @@ -1 +1 @@ -target_sources(nesmik PRIVATE extrae_type_stack.cpp) +target_sources(nesmik PRIVATE extrae_partial_tracer.cpp extrae_type_stack.cpp extrae_wrapper.cpp paraver_config.cpp) diff --git a/src/backends/extrae/extrae_partial_tracer.cpp b/src/backends/extrae/extrae_partial_tracer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2efb16fbaef79de742c29a37d67ebe7f388bcde --- /dev/null +++ b/src/backends/extrae/extrae_partial_tracer.cpp @@ -0,0 +1,196 @@ +#include "extrae_partial_tracer.hpp" + +#include +#include +#include +#include +extern "C" { +#include +} +#include + +ExtraePartialTracer::ExtraePartialTracer() + : mpi_helper_{}, extrae_wrapper_{state_type_} { + parallelism_descriptor_ = { + .mpi_descriptor_ = MPIDescriptor::Aware, + .thread_descriptor_ = ThreadDescriptor::Unsupported}; + + type_stack_strategy_ = std::make_unique(); + extrae_wrapper_.RegisterTypeWithDescription(type_description_, state_type_); +} + +void ExtraePartialTracer::shutdown() noexcept { + if (debug_output_.getValue().value_or(false) && mpi_helper_.IsRankNumber(0)) { + std::cout << "neSmiK Partial Tracer: Shutting down" << std::endl; + } + extrae_wrapper_.StopWithTypeAndRegionName(state_type_, "ON"); + Extrae_shutdown(); + is_shutdown_ = true; +} + +void ExtraePartialTracer::start() noexcept { + if (debug_output_.getValue().value_or(false) && mpi_helper_.IsRankNumber(0)) { + std::cout << "neSmiK Partial Tracer: Starting up" << std::endl; + } + Extrae_restart(); + extrae_wrapper_.StartWithTypeAndRegionName(state_type_, "ON"); + is_shutdown_ = false; +} + +void ExtraePartialTracer::Init() noexcept { + // And pause extrae + shutdown(); + + // then we check the env + const auto number_of_names = region_names_env_.getValue().value().size(); + if (number_of_names == 0) { + std::cout << "neSmiK Fatal: Please make sure to specify minimum one region " + "to partiallys trace" + << std::endl; + Terminator::exit(); + } + + if (number_of_names != regions_start_at_env_.getValue().value().size() || + number_of_names != regions_stop_at_env_.getValue().value().size()) { + std::cout << "neSmiK Fatal: Please make sure to specify same amounts of " + "names as well as starts and stops using e.g. " + << regions_stop_at_env_.getEnvName() << "=4,66,89" << std::endl; + Terminator::exit(); + } + + jobs_.reserve(number_of_names); + + // now create the start stop jobs + for (std::size_t i = 0; i < number_of_names; i++) { + StartStopJob job{.region_name = region_names_env_.getValue().value()[i], + .start_at = regions_start_at_env_.getValue().value()[i], + .stop_at = regions_stop_at_env_.getValue().value()[i]}; + jobs_.push_back(job); + } + + if (debug_output_.getValue().value_or(false) && mpi_helper_.IsRankNumber(0)) { + std::cout << "neSmiK Partial Tracer: Read the following jobs from env:" + << std::endl; + std::cout << "neSmiK Partial Tracer: {name,start_at,stop_at}" << std::endl; + for (std::size_t i = 0; i < jobs_.size(); i++) { + const auto &job = jobs_[i]; + std::cout << "neSmiK Partial Tracer: Jobs: " << i << "{\"" + << job.region_name << "\"," << job.start_at << "," + << job.stop_at << "}" << std::endl; + } + } +} + +void ExtraePartialTracer::RegionStart( + const ProperlyNestedRegionInformation ®ion) noexcept { + const std::string region_name = std::string(region.name); + + // A bit costly maybe if there is a lot of jobs, but much easier than keeping + // a tree or hashmap around + std::vector matching_jobs; + for (const auto &job : jobs_) { + if ((job.region_name.compare(region_name) == 0) && + region_counter_starts_[region_name] == job.start_at) { + // nice we found a matching one + matching_jobs.push_back(job); + } + } + + if (matching_jobs.size() > 1) { + std::cout + << "neSmiK Extrae::PartialTracer: Found multiple matching jobs, please " + "dont duplicate region names with the same start iteration" + << std::endl; + Terminator::exit(); + } + if (!matching_jobs.empty()) { + if (!is_shutdown_ && mpi_helper_.IsRankNumber(0)) { + std::cout + << "neSmiK Extrae::PartialTracer: The regions you selected are " + "overlapping. The tracer is already running for " + << region_name + << ". Please make sure to select non overlapping regions and restart" + << std::endl; + } + // We need to startup extrae and emit the region_start event + start(); + } else { + if (debug_output_.getValue().value_or(false) && + mpi_helper_.IsRankNumber(0)) { + std::cout << "neSmiK Extrae::PartialTracer: No Start found for " + << region_name << std::endl; + } + } + + region_counter_starts_[region_name]++; + + // if were running call extrae backend + if (!is_shutdown_) { + type_stack_strategy_->RegionStart(region); + } +} + +void ExtraePartialTracer::RegionStopLast( + const ProperlyNestedRegionInformation ®ion) noexcept { + const std::string region_name = std::string(region.name); + + if (!is_shutdown_) { + std::cout << "Region stop last " << region.name << std::endl; + type_stack_strategy_->RegionStopLast(region); + } + + std::vector matching_jobs; + for (const auto &job : jobs_) { + if ((job.region_name.compare(region_name) == 0) && + region_counter_stops_[region_name] == job.stop_at) { + // nice we found a matching one + matching_jobs.push_back(job); + } + } + + if (matching_jobs.size() > 1 && mpi_helper_.IsRankNumber(0)) { + std::cout + << "neSmiK Extrae::PartialTracer: Found multiple matching jobs, please " + "dont duplicate region names with the same stop iteration" + << std::endl; + Terminator::exit(); + } + + if (!matching_jobs.empty()) { + if (is_shutdown_ && mpi_helper_.IsRankNumber(0)) { + std::cout + << "neSmiK Extrae::PartialTracer: The regions you selected are " + "overlapping. The tracer is already stopped for " + << region_name + << ". Please make sure to select non overlapping regions and restart" + << std::endl; + } + // We need to stop extrae + shutdown(); + } else { + if (debug_output_.getValue().value_or(false) && + mpi_helper_.IsRankNumber(0)) { + std::cout << "neSmiK Extrae::PartialTracer: No Stop found for " + << region_name << std::endl; + } + } + + region_counter_stops_[region_name]++; +} + +void ExtraePartialTracer::Finalize() noexcept { + if (write_config_file_.getValue().value_or(true) && + mpi_helper_.IsRankNumber(0)) { + std::string fileName = "nesmik_running.cfg"; + std::ofstream out_file; + out_file.open(fileName); + ExtraeParaverConfig paraver_config{ + .stacked_window_name = std::nullopt, + .description = "neSmiK Partial Tracer state"}; + // write config + out_file << extrae_wrapper_.getParaverConfig(paraver_config) << std::endl; + } + + extrae_wrapper_.Finalize(); + type_stack_strategy_->Finalize(); +} diff --git a/src/backends/extrae/extrae_partial_tracer.hpp b/src/backends/extrae/extrae_partial_tracer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b4b0a3a7409dbcafbfd7ee68fc46be1cefe0caba --- /dev/null +++ b/src/backends/extrae/extrae_partial_tracer.hpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include "extrae_type_stack.hpp" + +struct StartStopJob { + std::string region_name; + unsigned int start_at; + unsigned int stop_at; +}; + +class ExtraePartialTracer : public ProperlyNestedAnnotationStrategy { + private: + EnvironmentVariable> region_names_env_ = + EnvironmentVariable>( + "PARTIAL_TRACER_REGION_NAMES", + "A comma separated list of names of regions which are disjunct to " + "partially trace.", + true); + EnvironmentVariable> regions_start_at_env_ = + EnvironmentVariable>( + "PARTIAL_TRACER_START_AT_CALL", + "A comma separated list of at which encounter of the region tracing " + "should start", + true); + + EnvironmentVariable> regions_stop_at_env_ = + EnvironmentVariable>( + "PARTIAL_TRACER_STOP_AT_CALL", + "A comma separated list of at which encounter of the region tracing " + "should stop", + true); + + EnvironmentVariable debug_output_ = + EnvironmentVariable("PARTIAL_TRACER_DEBUG", "DEBUG output", false); + + EnvironmentVariable write_config_file_ = EnvironmentVariable( + "EXTRAE_WRITE_CONFIG_FILE", + "Write corresponding Paraver .cfg file if set to True", false); + + MPIHelper mpi_helper_; + std::unordered_map region_counter_starts_; + std::unordered_map region_counter_stops_; + std::vector jobs_; + bool is_shutdown_{true}; + ExtraeWrapper extrae_wrapper_; + std::unique_ptr type_stack_strategy_; + void shutdown() noexcept; + void start() noexcept; + const extrae_type state_type_{82000}; + const std::string type_description_ = "Extrae::PartialTracer State"; + + public: + ExtraePartialTracer(); + inline static const std::string name = "Extrae::PartialTracer"; + + virtual void RegionStart( + const ProperlyNestedRegionInformation ®ion) noexcept override; + virtual void RegionStopLast( + const ProperlyNestedRegionInformation ®ion) noexcept override; + virtual void Init() noexcept override; + virtual void Finalize() noexcept override; +}; diff --git a/src/backends/extrae/extrae_type_stack.cpp b/src/backends/extrae/extrae_type_stack.cpp index be8be21208c0899c415ce5cac53fc54a0dbc0e6a..43b816fdd02ee812828dc85743ab93d5f9edbf7a 100644 --- a/src/backends/extrae/extrae_type_stack.cpp +++ b/src/backends/extrae/extrae_type_stack.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,125 +14,18 @@ extern "C" { #include } -ExtraeTypeStackStrategy::ExtraeTypeStackStrategy() : mpi_helper_{} { +ExtraeTypeStackStrategy::ExtraeTypeStackStrategy() + : mpi_helper_{}, extrae_wrapper_{base_type_} { parallelism_descriptor_ = { .mpi_descriptor_ = MPIDescriptor::Aware, .thread_descriptor_ = ThreadDescriptor::Unsupported}; } -const std::string ExtraeTypeStackStrategy::paraverConfigHead = R"( -#ParaverCFG -ConfigFile.Version: 3.4 -ConfigFile.NumWindows: 2 -ConfigFile.BeginDescription -Configuration to visualize the annotation done by neSmiK in Paraver -ConfigFile.EndDescription -)"; - -const std::string ExtraeTypeStackStrategy::paraverConfigOverviewWindow = R"( -################################################################################ -< NEW DISPLAYING WINDOW neSmiK Annotation Overview > -################################################################################ -window_name neSmiK Annotation Overview -window_type single -window_position_x 400 -window_position_y 150 -window_width 600 -window_height NESMIK_REPLACE_WINDOW_HEIGHT -window_comm_lines_enabled false -window_flags_enabled false -window_noncolor_mode true -window_custom_color_enabled false -window_semantic_scale_min_at_zero false -window_logical_filtered true -window_physical_filtered false -window_intracomms_enabled true -window_intercomms_enabled true -window_comm_fromto true -window_comm_tagsize true -window_comm_typeval true -window_maximum_y NESMIK_REPLACE_SEMANTIC_MAX -window_minimum_y 0 -window_compute_y_max false -window_level thread -window_scale_relative 1.000000000000 -window_end_time_relative 1.000000000000 -window_object appl { 1, { All } } -window_begin_time_relative 0.000000000000 -window_open true -window_drawmode draw_maximum -window_drawmode_rows draw_maximum -window_pixel_size 1 -window_labels_to_draw 1 -window_object_axis_position 0 -window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } -window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, Stacked Val}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } -window_filter_module evt_type NESMIK_REPLACE_NUM_LEVELS NESMIK_REPLACE_LEVELS -)"; - -const std::string ExtraeTypeStackStrategy::paraverConfigLevelWindow = R"( -################################################################################ -< NEW DISPLAYING WINDOW NESMIK_REPLACE_WINDOW_NAME > -################################################################################ -window_name NESMIK_REPLACE_WINDOW_NAME -window_type single -window_position_x 400 -window_position_y NESMIK_REPLACE_WINDOW_Y_POSITION -window_width 600 -window_height NESMIK_REPLACE_WINDOW_HEIGHT -window_comm_lines_enabled false -window_flags_enabled false -window_noncolor_mode true -window_custom_color_enabled false -window_semantic_scale_min_at_zero false -window_logical_filtered true -window_physical_filtered false -window_intracomms_enabled true -window_intercomms_enabled true -window_comm_fromto true -window_comm_tagsize true -window_comm_typeval true -window_maximum_y NESMIK_REPLACE_SEMANTIC_MAX -window_minimum_y 0 -window_compute_y_max false -window_level thread -window_scale_relative 1.000000000000 -window_end_time_relative 1.000000000000 -window_object appl { 1, { All } } -window_begin_time_relative 0.000000000000 -window_open true -window_drawmode draw_maximum -window_drawmode_rows draw_maximum -window_pixel_size 1 -window_labels_to_draw 1 -window_object_axis_position 0 -window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } -window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } -window_filter_module evt_type 1 NESMIK_REPLACE_EVENT_TYPE -)"; - void ExtraeTypeStackStrategy::Init() noexcept { - auto isInitializedVal = Extrae_is_initialized(); - if (isInitializedVal == 0) { - // Extrae is not initialized yet see - // https://github.com/bsc-performance-tools/extrae/blob/daee11a2f98e301eb146608f51df0c844e1ff381/include/extrae_types.h#L33 - Extrae_init(); - didInitialize = true; - std::cout << "NESMIK forced to Initialize extrae" << std::endl; - } -} - -extrae_value ExtraeTypeStackStrategy::get_value_by_region_name( - const std::string &name) { - // maybe insert - if (regionMapData.regionNameToValue.count(name) > 0) { - return regionMapData.regionNameToValue[name]; - } else - // if not allocate new entry - { - const auto newValue = regionMapData.regionNameToValue.size() + 1; - regionMapData.regionNameToValue[name] = newValue; - return newValue; + // initialize the stack levels up to MAX_STACK_LEVELS + for (std::size_t i = 0; i < MAX_STACK_LEVELS; i++) { + std::string description = "Level: " + std::to_string(i); + extrae_wrapper_.RegisterTypeWithDescription(description, base_type_ + i); } } @@ -139,142 +33,35 @@ void ExtraeTypeStackStrategy::RegionStart( const ProperlyNestedRegionInformation ®ion) noexcept { // First push to stack const auto regionName = std::string(region.name); - auto typeOffset = regionStackData.regionNameStack.size(); - regionStackData.regionNameStack.push(regionName); - - regionStackData.historicMaxStackSize = - std::max(typeOffset, regionStackData.historicMaxStackSize); - - auto value = get_value_by_region_name(regionName); - - auto type = baseType + typeOffset; - - Extrae_eventandcounters(type, value); + const auto level = region.stack_.size(); + max_stack_size = std::max(level, max_stack_size); + std::string description = "Level: " + std::to_string(level); + extrae_wrapper_.StartWithTypeNameAndRegionName(description, regionName); } void ExtraeTypeStackStrategy::RegionStopLast( const ProperlyNestedRegionInformation ®ion) noexcept { - if (regionStackData.regionNameStack.empty()) { - // whoops - std::cout << "imbalanced region stop REASON: Empty stack " << region.name - << std::endl; - return; - } - - const auto &lastRegionName = regionStackData.regionNameStack.top(); - - if (lastRegionName.compare(region.name) == 0) { - // its the same -> we pop it and end that stack level with a 0 - regionStackData.regionNameStack.pop(); - auto typeOffset = regionStackData.regionNameStack.size(); - auto type = baseType + typeOffset; - Extrae_eventandcounters(type, 0); - return; - } else { - std::cout << "imbalanced region stop " << region.name << std::endl; - } + const auto regionName = std::string(region.name); + const auto level = region.stack_.size(); + max_stack_size = std::max(level, max_stack_size); + std::string description = "Level: " + std::to_string(level); + extrae_wrapper_.StopWithTypeNameAndRegionName(description, regionName); } void ExtraeTypeStackStrategy::Finalize() noexcept { - unsigned int numberOfRegionsRegistered = - regionMapData.regionNameToValue.size(); - std::vector values{}; - std::vector routine_names{}; - - for (const auto &item : regionMapData.regionNameToValue) { - const auto &routine_name = item.first; - const auto &value = item.second; - routine_names.push_back(routine_name); - values.push_back(value); - } - // leaking memory a bit, but c vs c++ strings are not worth making it not - // leaky? - char **routine_c_names = new char *[numberOfRegionsRegistered]; - for (unsigned int i = 0; i < numberOfRegionsRegistered; i++) { - routine_c_names[i] = new char[routine_names[i].size() + 1]; - std::strcpy(routine_c_names[i], routine_names[i].c_str()); - } - - for (unsigned int level = 0; level <= regionStackData.historicMaxStackSize; - level++) { - extrae_type_t type = baseType + level; - std::string description = "Level: " + std::to_string(level); - Extrae_define_event_type(&type, const_cast(description.c_str()), - &numberOfRegionsRegistered, values.data(), - routine_c_names); - } - if (write_config_file_.getValue().value_or(write_config_file_default_) && mpi_helper_.IsRankNumber(0)) { std::string fileName = "nesmik_annotations.cfg"; - std::ofstream outputFile; - outputFile.open(fileName); - - // write header - outputFile << paraverConfigHead; - - // compute overview - auto numberOfEventTypes = - regionStackData.historicMaxStackSize + 1; // 0 based so plus 1 - auto allTypes = std::vector(numberOfEventTypes); - std::iota(std::begin(allTypes), std::end(allTypes), - baseType); // Fill with baseType baseType+1 .. - - // Build a small list of all types - std::string allTypesString = ""; - for (const auto &type : allTypes) { - allTypesString += std::to_string(type) + " "; - } - - auto overviewReplacedNumLevels = std::regex_replace( - paraverConfigOverviewWindow, std::regex("NESMIK_REPLACE_NUM_LEVELS"), - std::to_string(numberOfEventTypes)); - auto replacedWindowHeightOverview = std::regex_replace( - overviewReplacedNumLevels, std::regex("NESMIK_REPLACE_WINDOW_HEIGHT"), - std::to_string(paraverConfigWindowHeight_)); - auto replacedWindowSemanticMaxOverview = std::regex_replace( - replacedWindowHeightOverview, std::regex("NESMIK_REPLACE_SEMANTIC_MAX"), - std::to_string(numberOfRegionsRegistered)); - auto overviewReplacedAllTypes = - std::regex_replace(replacedWindowSemanticMaxOverview, - std::regex("NESMIK_REPLACE_LEVELS"), allTypesString); - - // write Overview - outputFile << overviewReplacedAllTypes; - outputFile << std::endl; - - // write separate windows - for (unsigned int level = 0; level <= regionStackData.historicMaxStackSize; - level++) { - std::string windowName = - "neSmiK Instrumentation Level: " + std::to_string(level); - - auto replacedEventType = std::regex_replace( - paraverConfigLevelWindow, std::regex("NESMIK_REPLACE_EVENT_TYPE"), - std::to_string(baseType + level)); - auto replacedWindowHeight = std::regex_replace( - replacedEventType, std::regex("NESMIK_REPLACE_WINDOW_HEIGHT"), - std::to_string(paraverConfigWindowHeight_)); - auto replacedWindowYPosition = std::regex_replace( - replacedWindowHeight, std::regex("NESMIK_REPLACE_WINDOW_Y_POSITION"), - std::to_string( - (paraverConfigWindowHeight_ + paraverConfigWindowHeightPad_) * - (level + 2 /* offset with overview window */))); - auto replacedWindowSemanticMax = std::regex_replace( - replacedWindowYPosition, std::regex("NESMIK_REPLACE_SEMANTIC_MAX"), - std::to_string(numberOfRegionsRegistered)); - auto replacedWindowName = std::regex_replace( - replacedWindowSemanticMax, std::regex("NESMIK_REPLACE_WINDOW_NAME"), - windowName); - - // write Overview - outputFile << replacedWindowName; - outputFile << std::endl; - } - } - - if (didInitialize) { - // Assumption is that if we needed to initialize the lib we also need to - // Finalize it - Extrae_fini(); + std::ofstream out_file; + out_file.open(fileName); + ExtraeParaverConfig paraver_config{ + .stacked_window_name = + std::optional("neSmiK Annotation Overview"), + .description = + "neSmiK annotation regions using the Extrae::TypeStack Backend" + + }; + // write config + out_file << extrae_wrapper_.getParaverConfig(paraver_config) << std::endl; } + extrae_wrapper_.Finalize(); } diff --git a/src/backends/extrae/extrae_type_stack.hpp b/src/backends/extrae/extrae_type_stack.hpp index 02370f8fb1d9ee34a9de8348d614256e97baec90..3cd2ee84f85d7ec0a9308488d133419a9e932985 100644 --- a/src/backends/extrae/extrae_type_stack.hpp +++ b/src/backends/extrae/extrae_type_stack.hpp @@ -1,45 +1,31 @@ +#ifndef NESMIK_EXTRAE_TYPE_STACK_HPP +#define NESMIK_EXTRAE_TYPE_STACK_HPP #include #include #include #include #include +#include "extrae_types.hpp" +#include "extrae_wrapper.hpp" #include "strategies.hpp" -// #include -typedef unsigned long extrae_value; -typedef unsigned long extrae_type; - -struct RegionStackData { - std::stack regionNameStack; - std::size_t historicMaxStackSize; -}; - -struct RegionMapData { - std::unordered_map regionNameToValue; -}; class ExtraeTypeStackStrategy : public ProperlyNestedAnnotationStrategy { private: const bool write_config_file_default_{true}; - const unsigned long baseType{81000}; - static const std::string paraverConfigHead; - static const std::string paraverConfigOverviewWindow; - static const std::string paraverConfigLevelWindow; + const extrae_type base_type_{81000}; // base event for the MPI wrapper + const std::size_t MAX_STACK_LEVELS = 50; // maximum of supported stack levels EnvironmentVariable write_config_file_ = EnvironmentVariable( "EXTRAE_WRITE_CONFIG_FILE", "Write corresponding Paraver .cfg file if set to True", false); - inline static const int paraverConfigWindowHeight_ = 115; - inline static const int paraverConfigWindowHeightPad_ = 35; - MPIHelper mpi_helper_; + ExtraeWrapper extrae_wrapper_; - inline static bool didInitialize{false}; + bool didInitialize{false}; - inline static thread_local RegionStackData regionStackData; - inline static thread_local RegionMapData regionMapData; - extrae_value get_value_by_region_name(const std::string &name); + std::size_t max_stack_size{0}; public: ExtraeTypeStackStrategy(); @@ -52,3 +38,4 @@ class ExtraeTypeStackStrategy : public ProperlyNestedAnnotationStrategy { virtual void Init() noexcept override; virtual void Finalize() noexcept override; }; +#endif // NESMIK_EXTRAE_TYPE_STACK_HPP diff --git a/src/backends/extrae/extrae_types.hpp b/src/backends/extrae/extrae_types.hpp new file mode 100644 index 0000000000000000000000000000000000000000..35c5025d0fecf5e34cf47eec9f9ff59fc2ef9fe2 --- /dev/null +++ b/src/backends/extrae/extrae_types.hpp @@ -0,0 +1,5 @@ +#ifndef NESMIK_EXTRAE_TYPES_HPP +#define NESMIK_EXTRAE_TYPES_HPP +typedef unsigned long extrae_value; +typedef unsigned long extrae_type; +#endif // NESMIK_EXTRAE_TYPES_HPP diff --git a/src/backends/extrae/extrae_wrapper.cpp b/src/backends/extrae/extrae_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45fe235976f72d6aface796daf5dc8de624aec03 --- /dev/null +++ b/src/backends/extrae/extrae_wrapper.cpp @@ -0,0 +1,142 @@ +#include "extrae_wrapper.hpp" + +#include +#include +#include +#include +#include +#include + +#include "extrae_types.hpp" +#include "paraver_config.hpp" + +extern "C" { +#include +} +ExtraeWrapper::ExtraeWrapper(extrae_type base_type) : base_type_(base_type) { + auto isInitializedVal = Extrae_is_initialized(); + if (isInitializedVal == 0) { + // Extrae is not initialized yet see + // https://github.com/bsc-performance-tools/extrae/blob/daee11a2f98e301eb146608f51df0c844e1ff381/include/extrae_types.h#L33 + Extrae_init(); + did_init_extrae_ = true; + std::cout << "neSmiK forced to Initialize extrae" << std::endl; + } +} + +extrae_value ExtraeWrapper::getValueByName(extrae_type type, + const std::string &name) { + value_map_t &value_map = string_to_value_; + if (value_map.count(name) > 0) { + return value_map[name]; + } else + // if not allocate new entry + { + const auto newValue = value_map.size() + 1; + value_map[name] = newValue; + return newValue; + } +} + +void ExtraeWrapper::RegisterTypeWithDescription( + const std::string &type_description, extrae_type type) { + type_map_[type_description] = type; +} +void ExtraeWrapper::StartWithTypeAndRegionName(extrae_type type, + const std::string &value_str) { + const auto value = getValueByName(type, value_str); + // write it in type specific map + type_to_value_map_[type][value_str] = value; + Extrae_eventandcounters(static_cast(type), + static_cast(value)); +} + +void ExtraeWrapper::StopWithTypeAndRegionName(extrae_type type, + const std::string &value_str) { + Extrae_eventandcounters(static_cast(type), 0); +} + +void ExtraeWrapper::StopWithTypeNameAndRegionName( + const std::string type_description, const std::string &value_str) { + // if this assertion fails, the type with that description has not been + // registered before and not started. + assert(type_map_.count(type_description) > 0); + return StopWithTypeAndRegionName(type_map_[type_description], value_str); +} + +void ExtraeWrapper::StartWithTypeNameAndRegionName( + const std::string type_description, const std::string &value_str) { + if (type_map_.count(type_description) == 0) { + const auto new_type = type_map_.size(); + type_map_[type_description] = base_type_ + new_type; + } + return StartWithTypeAndRegionName(type_map_[type_description], value_str); +} + +void ExtraeWrapper::Finalize() { + // now for ever type define the event types: + for (const auto &[description, type_from_map] : type_map_) { + // For every type registered: + extrae_type_t type = static_cast(type_from_map); + const value_map_t &value_map = type_to_value_map_[type]; + unsigned number_of_regions = value_map.size(); + + char **routine_c_names = new char *[number_of_regions]; + std::vector values; + + values.reserve(number_of_regions); + std::size_t i = 0; + for (const auto &[name, value] : value_map) { + values.push_back(static_cast(value)); + routine_c_names[i] = + new char[name.size() + 1]; // +1 for null terminiation + std::strcpy(routine_c_names[i], name.c_str()); + i++; + } + + Extrae_define_event_type(&type, const_cast(description.c_str()), + &number_of_regions, values.data(), + routine_c_names); + + // free up the memory, long live std::vector + for (std::size_t n = 0; n < number_of_regions; n++) { + delete[] routine_c_names[n]; + } + delete[] routine_c_names; + } + + if (did_init_extrae_) { + // Assumption is that if we needed to initialize the lib we also need to + // Finalize it + Extrae_fini(); + } +} + +std::string ExtraeWrapper::getParaverConfig(ExtraeParaverConfig config) { + ParaverConfig paraver_config(config.description); + + // create arrays + std::vector types; + std::vector window_names; + std::vector semantic_maximums; + for (const auto &[name, type] : type_map_) { + types.push_back(type); + window_names.push_back(name); + // now the ugly search for the semantic maximum + extrae_value semantic_maximum{0}; + for (const auto &[value_name, value] : type_to_value_map_[type]) { + semantic_maximum = std::max(value, semantic_maximum); + } + semantic_maximums.push_back(semantic_maximum); + } + + ParaverTimelineWindow window{ + .window_names = window_names, + .types = types, + .semantic_maximums = semantic_maximums, + .stacked_window_name = config.stacked_window_name}; + + paraver_config.addTimeline(window); + + return paraver_config.getString(); +} diff --git a/src/backends/extrae/extrae_wrapper.hpp b/src/backends/extrae/extrae_wrapper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..125668fc1c311036542c8e83c148756cccba623b --- /dev/null +++ b/src/backends/extrae/extrae_wrapper.hpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "extrae_types.hpp" + +struct ExtraeParaverConfig { + std::optional stacked_window_name; + std::string description; +}; + +using value_map_t = std::unordered_map; +class ExtraeWrapper { + const extrae_type base_type_; + + private: + std::unordered_map type_map_; + std::unordered_map type_to_value_map_; + std::unordered_map string_to_value_; + bool did_init_extrae_{false}; + + extrae_value getValueByName(extrae_type type, const std::string &name); + + public: + ExtraeWrapper(extrae_type base_type); + void RegisterTypeWithDescription(const std::string &type_description, + extrae_type type); + void StartWithTypeNameAndRegionName(const std::string type_description, + const std::string &value_str); + void StartWithTypeAndRegionName(extrae_type type, + const std::string &value_str); + void StopWithTypeAndRegionName(extrae_type type, + const std::string &value_str); + void StopWithTypeNameAndRegionName(const std::string type_description, + const std::string &value_str); + + std::string getParaverConfig(ExtraeParaverConfig config); + void Finalize(); +}; diff --git a/src/backends/extrae/paraver_config.cpp b/src/backends/extrae/paraver_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a04799e7bc5154aa061f0ab244b00fe9a537c162 --- /dev/null +++ b/src/backends/extrae/paraver_config.cpp @@ -0,0 +1,134 @@ + +#include "paraver_config.hpp" + +#include +#include +#include +#include + +const std::string ParaverConfig::config_head_ = R"( +#ParaverCFG +ConfigFile.Version: 3.4 +ConfigFile.NumWindows: 2 +ConfigFile.BeginDescription +NESMIK_REPLACE_DESCRIPTION +ConfigFile.EndDescription +)"; +const std::string ParaverConfig::time_line_window_template_ = R"( +################################################################################ +< NEW DISPLAYING WINDOW NESMIK_REPLACE_WINDOW_NAME > +################################################################################ +window_name NESMIK_REPLACE_WINDOW_NAME +window_type single +window_position_x 400 +window_position_y NESMIK_REPLACE_WINDOW_Y_POSITION +window_width 600 +window_height NESMIK_REPLACE_WINDOW_HEIGHT +window_comm_lines_enabled false +window_flags_enabled false +window_noncolor_mode true +window_custom_color_enabled false +window_semantic_scale_min_at_zero false +window_logical_filtered true +window_physical_filtered false +window_intracomms_enabled true +window_intercomms_enabled true +window_comm_fromto true +window_comm_tagsize true +window_comm_typeval true +window_maximum_y NESMIK_REPLACE_SEMANTIC_MAX +window_minimum_y 0 +window_compute_y_max false +window_level thread +window_scale_relative 1.000000000000 +window_end_time_relative 1.000000000000 +window_object appl { 1, { All } } +window_begin_time_relative 0.000000000000 +window_open true +window_drawmode draw_maximum +window_drawmode_rows draw_maximum +window_pixel_size 1 +window_labels_to_draw 1 +window_object_axis_position 0 +window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } +window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, Stacked Val}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } +window_filter_module evt_type NESMIK_REPLACE_NUM_EVENTTYPES NESMIK_REPLACE_EVENTTYPES +)"; + +ParaverConfig::ParaverConfig(const std::string &description) { + auto head_with_description = std::regex_replace( + config_head_, std::regex("NESMIK_REPLACE_DESCRIPTION"), description); + + config_ << head_with_description << "\n"; +} + +void ParaverConfig::addTimeline(ParaverTimelineWindow window_config) { + assert(window_config.types.size() == window_config.semantic_maximums.size() && + window_config.types.size() == window_config.window_names.size()); + + // replace window height + auto repalaced_window_height = std::regex_replace( + time_line_window_template_, std::regex("NESMIK_REPLACE_WINDOW_HEIGHT"), + std::to_string(window_height_)); + + if (window_config.stacked_window_name.has_value()) { + // Build a small list of all types + std::string allTypesString = ""; + for (const auto &type : window_config.types) { + allTypesString += std::to_string(type) + " "; + } + + auto replace_window_name = std::regex_replace( + repalaced_window_height, std::regex("NESMIK_REPLACE_WINDOW_NAME"), + window_config.stacked_window_name.value()); + + auto replaced_event_type = std::regex_replace( + replace_window_name, std::regex("NESMIK_REPLACE_EVENTTYPES"), + allTypesString); + + auto replaced_event_num = std::regex_replace( + replaced_event_type, std::regex("NESMIK_REPLACE_NUM_EVENTTYPES"), + std::to_string(window_config.types.size())); + + auto replaced_window_y_pos = std::regex_replace( + replaced_event_num, std::regex("NESMIK_REPLACE_WINDOW_Y_POSITION"), + std::to_string(150)); + + const extrae_type max_value_in_array = + *std::max_element(window_config.semantic_maximums.begin(), + window_config.semantic_maximums.end()); + auto replaced_semantic_max = std::regex_replace( + replaced_window_y_pos, std::regex("NESMIK_REPLACE_SEMANTIC_MAX"), + std::to_string(max_value_in_array)); + + config_ << replaced_semantic_max << "\n"; + } + + std::size_t number_of_types = window_config.types.size(); + // now iterate over the types + for (std::size_t i = 0; i < number_of_types; i++) { + auto replace_window_name = std::regex_replace( + repalaced_window_height, std::regex("NESMIK_REPLACE_WINDOW_NAME"), + window_config.window_names[i]); + + auto replaced_event_type = std::regex_replace( + replace_window_name, std::regex("NESMIK_REPLACE_EVENTTYPES"), + std::to_string(window_config.types[i])); + + auto replaced_event_num = std::regex_replace( + replaced_event_type, std::regex("NESMIK_REPLACE_NUM_EVENTTYPES"), + std::to_string(1)); + + auto replaced_window_y_pos = std::regex_replace( + replaced_event_num, std::regex("NESMIK_REPLACE_WINDOW_Y_POSITION"), + std::to_string((window_height_ + window_height_pad_) * (i + 2))); + + auto replaced_semantic_max = std::regex_replace( + replaced_window_y_pos, std::regex("NESMIK_REPLACE_SEMANTIC_MAX"), + std::to_string(window_config.semantic_maximums[i])); + + config_ << replaced_semantic_max << "\n"; + } +} + +std::string ParaverConfig::getString() { return config_.str(); } diff --git a/src/backends/extrae/paraver_config.hpp b/src/backends/extrae/paraver_config.hpp new file mode 100644 index 0000000000000000000000000000000000000000..91d609f97ec36551e7e7b9c51edacac468353283 --- /dev/null +++ b/src/backends/extrae/paraver_config.hpp @@ -0,0 +1,29 @@ + +#include +#include +#include +#include + +#include "extrae_types.hpp" + +struct ParaverTimelineWindow { + const std::vector &window_names; + const std::vector &types; + const std::vector semantic_maximums; + std::optional stacked_window_name; +}; + +class ParaverConfig { + private: + std::stringstream config_; + static const std::string config_head_; + static const std::string time_line_window_template_; + + const unsigned int window_height_ = 115; + const unsigned int window_height_pad_ = 35; + + public: + ParaverConfig(const std::string &description); + void addTimeline(ParaverTimelineWindow window_config); + std::string getString(); +}; diff --git a/src/delegator.cpp b/src/delegator.cpp index 2439203779a5c00f8744d85abdb9c64d52326bca..bb7a54f0ae703cea9050206c6cd9fd2ffa134bb1 100644 --- a/src/delegator.cpp +++ b/src/delegator.cpp @@ -18,6 +18,7 @@ #endif #ifdef ENABLE_EXTRAE +#include "backends/extrae/extrae_partial_tracer.hpp" #include "backends/extrae/extrae_type_stack.hpp" #endif @@ -41,6 +42,8 @@ void Delegator::TryInitProperlyNestedBackend(const std::string &backend) { #ifdef ENABLE_EXTRAE else if (backend.compare(ExtraeTypeStackStrategy::name) == 0) { pn_annotation_strategy_ = std::make_unique(); + } else if (backend.compare(ExtraePartialTracer::name) == 0) { + pn_annotation_strategy_ = std::make_unique(); } #endif #ifdef ENABLE_DLB @@ -52,12 +55,15 @@ void Delegator::TryInitProperlyNestedBackend(const std::string &backend) { const std::vector Delegator::available_backends = { #ifdef ENABLE_DLB - DLBTalpTreeStrategy::name, DLBTalpStrategy::name, + DLBTalpTreeStrategy::name, + DLBTalpStrategy::name, #endif #ifdef ENABLE_EXTRAE ExtraeTypeStackStrategy::name, + ExtraePartialTracer::name, #endif - "Default", "Detection"}; + "Default", + "Detection"}; bool Delegator::checkIfBackendAvailable(const std::string &backend) { bool found_backend{false}; diff --git a/src/utils/environment_variable.cpp b/src/utils/environment_variable.cpp index 979be296fab92a9d23bdbbe0c900466d462ca600..822867a113c5302969ddd55cdcd9f64bff4027a5 100644 --- a/src/utils/environment_variable.cpp +++ b/src/utils/environment_variable.cpp @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include template <> std::optional fromEnvString(const std::string &env_string) { @@ -26,3 +28,40 @@ template <> std::optional fromEnvString(const std::string &env_string) { return std::optional{env_string}; } + +template <> +std::optional> fromEnvString( + const std::string &env_string) { + const char delimiter = ','; + std::vector result; + std::stringstream stream(env_string); + + while (stream.good()) { + std::string entry; + std::getline(stream, entry, delimiter); + if (!entry.empty()) { + result.push_back(entry); + } + } + + if (result.size() > 0) { + return result; + } else { + return std::nullopt; + } +} + +template <> +std::optional> fromEnvString( + const std::string &env_string) { + const auto &strings = fromEnvString>(env_string); + + if (!strings.has_value()) { + return std::nullopt; + } + std::vector result; + for (const auto &string : strings.value()) { + result.push_back(std::stoul(string)); + } + return result; +} diff --git a/src/utils/environment_variable.hpp b/src/utils/environment_variable.hpp index f071d987ada09e4c5fbee02f11ab2eb5bb2cf84a..3f6f3f4ac81295343c9e4376f34aa3d0c42c5ad2 100644 --- a/src/utils/environment_variable.hpp +++ b/src/utils/environment_variable.hpp @@ -6,6 +6,7 @@ #include #include #include +#include // dont allow the compiler to guess, but force user to implement conversion template @@ -15,6 +16,13 @@ template <> std::optional fromEnvString(const std::string &env_string); template <> std::optional fromEnvString(const std::string &env_string); +template <> +std::optional> fromEnvString( + const std::string &env_string); + +template <> +std::optional> fromEnvString( + const std::string &env_string); template class EnvironmentVariable {