diff --git a/src/backends/detection/detection.cpp b/src/backends/detection/detection.cpp index 2be1c928bd6b6b6cfc4b724085277ab4bbf988d4..39c3baf36d8d9434281d8605925bc559e5052fc5 100644 --- a/src/backends/detection/detection.cpp +++ b/src/backends/detection/detection.cpp @@ -71,6 +71,13 @@ void DetectionStrategy::RegionStart( start_entry.stack_ = region.stack_; start_entry.thread_ = getThreadStoreOfCaller(); + // Count region started + std::string region_name(region.name); + if (count_starts_.find(region_name) == count_starts_.end()) { + count_starts_[region_name] = 0; + } + count_starts_[region_name]++; + std::lock_guard guard(starts_mutex_); starts_.push_back(start_entry); } @@ -85,6 +92,13 @@ void DetectionStrategy::RegionStopLast( stop_entry.stack_ = region.stack_; stop_entry.thread_ = getThreadStoreOfCaller(); + // Count region closed + std::string region_name(region.name); + if (count_ends_.find(region_name) == count_ends_.end()) { + count_ends_[region_name] = 0; + } + count_ends_[region_name]++; + std::lock_guard guard(ends_mutex_); ends_.push_back(stop_entry); @@ -99,30 +113,59 @@ void DetectionStrategy::RegionStopLast( has_errors_ = true; return; } - // now check if we actually pop the right name - if (region.name.compare(region.stack_.top()) != 0) { - if (verbose_mode_.getValue().value_or(false)) { - std::cout << "neSmiK WARNING: The stack says the next region to close is " - << region.stack_.top() << " but tried closing " << region.name - << std::endl; + + // now check if we actually pop the right name and if not we mark all the + // regions (until we find it) as overlapping. + bool is_overlapped = false; + std::stack stack = region.stack_; + while (!stack.empty() && region.name.compare(stack.top()) != 0) { + // Get value and pop + std::string top_region = stack.top(); + stack.pop(); + + if (overlapping_regions_.count(top_region) > 0) { + break; } + has_errors_ = true; - return; + is_overlapped = true; + + overlapping_regions_.insert(std::string(top_region)); + } + + if (is_overlapped) { + overlapping_regions_.insert(std::string(region.name)); + } + + if (is_overlapped && verbose_mode_.getValue().value_or(false)) { + std::cout << "neSmiK WARNING: The stack says the next region to close is " + << region.stack_.top() << " but tried closing " << region.name + << std::endl; } } +typedef struct region_summary_t { + unsigned int starts; + unsigned int stops; + bool overlapping; + bool thread_safe; +} region_summary_t; + void DetectionStrategy::Finalize() noexcept { int has_errors = static_cast(has_errors_); int has_been_called_from_threads = 0; pid_t first_thread_id = 0; + std::set used_multiple_threads; + for (const auto& start : starts_) { if (first_thread_id == 0) { first_thread_id = start.thread_.thread_id_; } if (start.thread_.thread_id_ != first_thread_id) { has_been_called_from_threads = 1; + used_multiple_threads.insert(std::string(start.region_name_)); break; } } @@ -133,6 +176,7 @@ void DetectionStrategy::Finalize() noexcept { } if (end.thread_.thread_id_ != first_thread_id) { has_been_called_from_threads = 1; + used_multiple_threads.insert(std::string(end.region_name_)); break; } } @@ -149,6 +193,121 @@ void DetectionStrategy::Finalize() noexcept { } #endif + int comm_size = mpi_helper_.getCommSize(); + + // TODO: Fix the Parallelism Helper as it should report at comm_size = 1 when + // there is no MPI present to make MPI aware backends to work, without much + // spegetty code. + if (comm_size == 0) { + comm_size++; + } + + bool someone_is_overlapping = overlapping_regions_.size() > 0; + bool someone_is_not_thread_safe = used_multiple_threads.size() > 0; + std::unordered_map region_summaries; + + for (int i = 0; i < comm_size; i++) { + if (mpi_helper_.IsRankNumber(i)) { + // Preparing region summary entries + // -------------------------------- + for (auto starts : count_starts_) { + unsigned int counted_ends = 0; + if (count_ends_.find(starts.first) != count_ends_.end()) { + auto ends = count_ends_.extract(starts.first); + counted_ends = ends.mapped(); + } + + bool overlapping = overlapping_regions_.count(starts.first) > 0; + bool thread_safe = used_multiple_threads.count(starts.first) == 0; + used_multiple_threads.erase(starts.first); + + bool mismatched = starts.second != counted_ends; + + // We add the report in the list either if we: + // - print everything as requested by the user + // - find that there is a mismatch between starts and stops + // - find that the region is overlapping with another + // - find that the region is not thread safe + if (show_all_regions_.getValue().value_or(false) || mismatched || + overlapping || !thread_safe) { + // We assume regions won't be repeated + region_summaries[starts.first] = { + .starts = starts.second, + .stops = counted_ends, + .overlapping = overlapping, + .thread_safe = thread_safe, + }; + } + } + for (auto ends : count_ends_) { + bool overlapping = overlapping_regions_.count(ends.first) > 0; + bool thread_safe = used_multiple_threads.count(ends.first) > 0; + used_multiple_threads.erase(ends.first); + + // All regions that make it that far must be printed because will have + // never been started. + region_summaries[ends.first] = { + .starts = 0, + .stops = ends.second, + .overlapping = overlapping, + .thread_safe = thread_safe, + }; + } + // The list of used multiple threads should be empty as all regions + // should have been treated by this point + // + // assert(used_multiple_threads.empty()) + + // Printing the summary + // -------------------- + // + // Print header + std::cout << "Process " << i << std::endl; + std::cout << "------------------------------------------------------"; + if (someone_is_overlapping) { + std::cout << "--------------"; + } + if (someone_is_not_thread_safe) { + std::cout << "---------------"; + } + std::cout << std::endl; + + // Print regions + for (auto region_summary : region_summaries) { + std::cout << std::setw(30) << region_summary.first << ": "; + std::cout << std::setw(10) << region_summary.second.starts; + std::cout << "/"; + std::cout << std::setw(10) << region_summary.second.stops; + + if (region_summary.second.overlapping) { + std::cout << " (overlapping) "; + } else if (someone_is_overlapping) { + std::cout << " "; + } + + if (!region_summary.second.thread_safe) { + std::cout << " (multithread) "; + } else if (someone_is_not_thread_safe) { + std::cout << " "; + } + std::cout << std::endl; + } + + // Close process summary + std::cout << "------------------------------------------------------"; + if (someone_is_overlapping) { + std::cout << "--------------"; + } + if (someone_is_not_thread_safe) { + std::cout << "---------------"; + } + std::cout << std::endl; + } +#ifdef WITH_MPI + PMPI_Barrier(MPI_COMM_WORLD); +#endif + } + // print summary report if (mpi_helper_.IsRankNumber(0)) { if (mpi_helper_.IsUsingMPI()) { diff --git a/src/backends/detection/detection.hpp b/src/backends/detection/detection.hpp index ed7f499f9acd598a33a89456939dc0ca9782ce1c..dfd5b4ef7129b3df59a7f1101fea2e846b4cf042 100644 --- a/src/backends/detection/detection.hpp +++ b/src/backends/detection/detection.hpp @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -36,12 +38,25 @@ class DetectionStrategy : public ProperlyNestedAnnotationStrategy { std::vector starts_; std::vector ends_; + // Regions that were overlapped or overlapping at some point + std::set overlapping_regions_; + + // Count of times a regions was open/closed + std::unordered_map count_starts_; + std::unordered_map count_ends_; + std::mutex starts_mutex_; std::mutex ends_mutex_; EnvironmentVariable verbose_mode_ = EnvironmentVariable( "DETECTION_VERBOSE", "Enables the verbose mode in the detection backend", false); + EnvironmentVariable show_all_regions_ = EnvironmentVariable( + "SHOW_ALL_REGIONS", + "Report summary for all regions. If disabled, only regions with odd " + "behaviour will be reported. (default:off)", + false); + bool has_errors_ = false; public: diff --git a/src/delegator.cpp b/src/delegator.cpp index d9e8e9c187fd5a578c3966952b3fd92476a17514..21d7d4d71540c1368bddf153ac0ccb94ce8615eb 100644 --- a/src/delegator.cpp +++ b/src/delegator.cpp @@ -234,9 +234,10 @@ void Delegator::RegionStop(std::string_view name) { break; } if (!name_stack_.empty()) { - if (name.compare(name_stack_.top()) != 0) { + if (name.compare(name_stack_.top()) != 0 && + verbose_mode_.getValue().value_or(false)) { std::cout << "neSmiK region_close() expected " << name_stack_.top() - << "but got " << name << std::endl; + << " but got " << name << std::endl; } name_stack_.pop(); } diff --git a/src/delegator.hpp b/src/delegator.hpp index 57ab41d5237968949c69fd5c6d402b1b28a5b1e5..cfb40ca93725b07fc1853a1e3a2820fc69654f8e 100644 --- a/src/delegator.hpp +++ b/src/delegator.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,9 @@ class Delegator { inline static std::unique_ptr mpi_helper_; + inline static EnvironmentVariable verbose_mode_ = + EnvironmentVariable("VERBOSE", "Enables the verbose mode", false); + public: static void Init(); static void RegionStart(std::string_view name); diff --git a/tests/cpp/TestCppNotNested.cpp b/tests/cpp/TestCppNotNested.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0200d2ea69779c1fb46f1039e0df82875386983a --- /dev/null +++ b/tests/cpp/TestCppNotNested.cpp @@ -0,0 +1,24 @@ +#include "nesmik/nesmik.hpp" + +int main() { + nesmik::nesmik_init(); + nesmik::region_start("Level 1"); + nesmik::region_start("Level 2"); + nesmik::region_start("Overlapped 1"); + nesmik::region_start("Overlapped 2"); + nesmik::region_start("Level 3"); + nesmik::region_stop("Level 3"); + nesmik::region_stop("Overlapped 1"); + nesmik::region_stop("Overlapped 2"); + +#pragma omp parallel num_threads(2) + { + nesmik::region_start("Multithread"); + nesmik::region_stop("Multithread"); + } + + nesmik::region_stop("Level 2"); + nesmik::region_stop("Level 1"); + nesmik::region_start("Never closed"); + nesmik::nesmik_finalize(); +}