diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 43fa57b691a2869b3b8a19bfbdace81536676c7f..ccb0564a5397f6b7b703b2413df47dfe5f484d72 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(default) - +add_subdirectory(detection) if(ENABLE_DLB) add_subdirectory(dlb) endif() diff --git a/src/backends/detection/CMakeLists.txt b/src/backends/detection/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e7f49757bc39bfad000e697d20711404c9806a13 --- /dev/null +++ b/src/backends/detection/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(nesmik PRIVATE detection.cpp) diff --git a/src/backends/detection/detection.cpp b/src/backends/detection/detection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4217fbca8b418839a313195ef7f999fac8edb9bb --- /dev/null +++ b/src/backends/detection/detection.cpp @@ -0,0 +1,228 @@ +#include "detection.hpp" +#include +#include +#include + +#include + +#include + +#include + +#ifdef WITH_MPI + #include +#endif + +// for convenience +using json = nlohmann::json; + +void to_json(json& j, const RegionEventEntry& p){ + + + // first convert to the stack to std::vector in order + std::stack local_stack = p.stack_; // copy stack + std::vector stack_vec(local_stack.size()); + + for(std::size_t i = 0; i< local_stack.size(); i++){ + stack_vec.at(i) = local_stack.top(); + local_stack.pop(); + } + + // we use camelCase as suggested in https://google.github.io/styleguide/jsoncstyleguide.xml?showone=Property_Name_Format#Property_Name_Format + j = json{ + {"threadId", p.thread_.thread_id_}, + {"processId", p.thread_.process_id_}, + {"regionName", p.region_name_}, + {"stack", stack_vec}, + {"timestamp", p.timestamp} + }; + +} +void from_json(const json& j, RegionEventEntry& p){ + +} + +ThreadStore getThreadStoreOfCaller(){ + ThreadStore thread_store; + thread_store.process_id_ = getpid(); + thread_store.thread_id_ = gettid(); + return thread_store; +} + +double getTimeStamp(){ + #ifdef WITH_MPI + return MPI_Wtime(); + #else + using clock = std::chrono::system_clock; + auto now = clock::now(); + double seconds = std::chrono::duration_cast>(now.time_since_epoch()).count(); + return seconds; + + #endif + + } + + +DetectionStrategy::DetectionStrategy():mpi_helper_{}{ + parallelism_descriptor_={ + .mpi_descriptor_ = MPIDescriptor::Aware, + .thread_descriptor_ = ThreadDescriptor::Supported + }; + + +} + +void DetectionStrategy::Init() noexcept { + +} + +void DetectionStrategy::RegionStart( + const ProperlyNestedRegionInformation ®ion) noexcept { + RegionEventEntry start_entry; + start_entry.timestamp= getTimeStamp(); + start_entry.region_name_=std::string(region.name); + start_entry.stack_ = region.stack_; + start_entry.thread_ = getThreadStoreOfCaller(); + + std::lock_guard guard(starts_mutex_); + starts_.push_back(start_entry); +} + +void DetectionStrategy::RegionStopLast( + const ProperlyNestedRegionInformation ®ion) noexcept { + + // first create an entry + RegionEventEntry stop_entry; + stop_entry.timestamp= getTimeStamp(); + stop_entry.region_name_ = std::string(region.name); + + stop_entry.stack_ = region.stack_; + stop_entry.thread_ = getThreadStoreOfCaller(); + + + std::lock_guard guard(ends_mutex_); + ends_.push_back(stop_entry); + + // two error checks are following + + if(region.stack_.empty()){ + std::cout << "neSmiK WARNING: The stack is already empty, so no more regions to close, but it tried to close " << region.name << std::endl; + has_errors_ = true; + return; + } + // now check if we actually pop the right name + if(region.name.compare(region.stack_.top())!= 0){ + std::cout << "neSmiK WARNING: The stack says the next region to close is " << region.stack_.top() << " but tried closing " << region.name<< std::endl; + has_errors_ = true; + return; + } + +} + + + + +void DetectionStrategy::Finalize() noexcept { + + + int has_errors = static_cast(has_errors_); + int has_been_called_from_threads = 0; + + + pid_t first_thread_id = 0; + + 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; + break; + } + } + + for(const auto& end : ends_){ + if(first_thread_id == 0){ + first_thread_id = end.thread_.thread_id_; + } + if(end.thread_.thread_id_ != first_thread_id){ + has_been_called_from_threads = 1; + break; + } + } + + #ifdef WITH_MPI + if(mpi_helper_.IsRankNumber(0)){ + PMPI_Reduce(MPI_IN_PLACE,&has_been_called_from_threads,1, MPI_INT, MPI_SUM,0,MPI_COMM_WORLD); + PMPI_Reduce(MPI_IN_PLACE,&has_errors,1, MPI_INT, MPI_SUM,0,MPI_COMM_WORLD); + } + else{ + int localUnused; + PMPI_Reduce(&has_been_called_from_threads,&localUnused,1, MPI_INT, MPI_SUM,0,MPI_COMM_WORLD); + PMPI_Reduce(&has_errors,&localUnused,1, MPI_INT, MPI_SUM,0,MPI_COMM_WORLD); + } + #endif + + + // print summary report + if(mpi_helper_.IsRankNumber(0)){ + if(mpi_helper_.IsUsingMPI()){ + std::cout << "############################### Using MPI: Yes ##################################################" << std::endl; + } + std::cout << "############################### Nesting Summary #################################################" << std::endl; + std::cout << "# #" << std::endl; + if(has_errors > 0 ){ + std::cout << "# neSmiK detected that the nesting of regions implemented by the application is NotProperlyNested #" << std::endl; + std::cout << "# Please be aware, that using backends that require ProperlyNested regions is not supported #" << std::endl; + std::cout << "# If you think this is a bug, as you expected them to be ProperlyNested, #" << std::endl; + std::cout << "# please look at the docs on instructions on how to proceed. #"<< std::endl; + std::cout << "# #" << std::endl; + if(mpi_helper_.IsUsingMPI()){ + std::cout << "# Nesmik also produced files called nesmik_detection_ends_.json and #" << std::endl; + std::cout << "# nesmik_detection_starts_.json with a json event trace you can use to debug. #" << std::endl; + }else{ + std::cout << "# Nesmik also produced files called nesmik_detection_ends.json and #" << std::endl; + std::cout << "# nesmik_detection_starts.json with a json event trace you can use to debug. #" << std::endl; + } + + }else{ + std::cout << "# neSmiK detected that the nesting of regions implemented by the application is ProperlyNested #" << std::endl; + std::cout << "# That is great news! #" << std::endl; + std::cout << "# #" << std::endl; + } + std::cout << "################################ Thread Summary #################################################" << std::endl; + std::cout << "# #" << std::endl; + if(has_been_called_from_threads > 0 ){ + std::cout << "# neSmiks annontation API has been called from more than one thread. #"<< std::endl; + std::cout << "# Note, that your backend needs to have multithreading support to generate proper results #"<< std::endl; + std::cout << "# #" << std::endl; + }else{ + std::cout << "# neSmiks annotation API has been NOT called from more than one thread. #"<< std::endl; + std::cout << "# #" << std::endl; + } + std::cout << "###################################################################################################" << std::endl; + } + + + + nlohmann::json j_starts = starts_; + nlohmann::json j_end = ends_; + + std::string filenname_starts = "nesmik_detection_starts"; + std::string filenname_ends = "nesmik_detection_ends"; + if(mpi_helper_.IsUsingMPI()){ + // prepend MPI rank to the filename + filenname_starts += "_" + std::to_string(mpi_helper_.getRankNumber()); + filenname_ends += "_" + std::to_string(mpi_helper_.getRankNumber()); + } + + std::ofstream file_starts(filenname_starts +".json"); + std::ofstream file_ends(filenname_ends +".json"); + + file_starts << j_starts; + file_ends << j_end; + + + + +} diff --git a/src/backends/detection/detection.hpp b/src/backends/detection/detection.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1660b3860d410bad264f371cfa842c936108244a --- /dev/null +++ b/src/backends/detection/detection.hpp @@ -0,0 +1,59 @@ +#ifndef NESMIK_DETECTION_H +#define NESMIK_DETECTION_H + +#include +#include + +#include +#include +#include +#include +#include + +#include + +// for convenience +using json = nlohmann::json; + + +struct ThreadStore{ + pid_t thread_id_; + pid_t process_id_; +}; + + +struct RegionEventEntry{ + ThreadStore thread_; + std::string region_name_; + std::stack stack_; + double timestamp; +}; + + + +void to_json(json& j, const RegionEventEntry& p); +void from_json(const json& j, RegionEventEntry& p); + + + +class DetectionStrategy : public ProperlyNestedAnnotationStrategy { + +private: + MPIHelper mpi_helper_; + std::vector starts_; + std::vector ends_; + + std::mutex starts_mutex_; + std::mutex ends_mutex_; + + bool has_errors_ = false; + +public: + DetectionStrategy(); + inline static const std::string name = "Detection"; + virtual void RegionStart(const ProperlyNestedRegionInformation ®ion) noexcept override; + virtual void RegionStopLast(const ProperlyNestedRegionInformation ®ion) noexcept override; + virtual void Init() noexcept; + virtual void Finalize() noexcept; +}; +#endif // NESMIK_DETECTION_H diff --git a/src/delegator.cpp b/src/delegator.cpp index f389174153ff5415bb0cf3a9898fbe140f19926d..910ae949eb8bbb4c48b10de73140cb467d776acd 100644 --- a/src/delegator.cpp +++ b/src/delegator.cpp @@ -5,6 +5,7 @@ #include #include "backends/default/default.hpp" +#include "backends/detection/detection.hpp" #include #include #include @@ -19,8 +20,7 @@ #endif void Delegator::InitDetectionBackend(const std::string_view backend) { - std::cout << "Not supported currnently, come back later pls" << std::endl; - exit(1); + pn_annotation_strategy_ = std::make_unique(); } void Delegator::InitNonProperlyNestedBackends(const std::string_view backend) { @@ -68,7 +68,7 @@ void Delegator::Init(std::string_view nesting_mode, std::string_view backend) { // detect nesting mode requested by the user if (nesting_mode.compare("?") == 0) { - nesting_mode_ = NestingMode::Detecion; + nesting_mode_ = NestingMode::Detection; } else if (nesting_mode.compare("ProperlyNested") == 0) { nesting_mode_ = NestingMode::ProperlyNested; } else if (nesting_mode.compare("NotProperlyNested") == 0) { @@ -77,7 +77,7 @@ void Delegator::Init(std::string_view nesting_mode, std::string_view backend) { // Some debug output switch (nesting_mode_) { - case NestingMode::Detecion: + case NestingMode::Detection: std::cout << "Selecting nesting mode to: " << "detecting nesting behavior" << std::endl; break; @@ -93,7 +93,7 @@ void Delegator::Init(std::string_view nesting_mode, std::string_view backend) { // next choose backend switch (nesting_mode_) { - case NestingMode::Detecion: + case NestingMode::Detection: InitDetectionBackend(backend); break; case NestingMode::ProperlyNested: @@ -112,9 +112,11 @@ void Delegator::Init(std::string_view nesting_mode, std::string_view backend) { } switch (nesting_mode_) { - case NestingMode::Detecion: + case NestingMode::NotProperlyNested: return npn_annotation_strategy_->Init(); + break; + case NestingMode::Detection: case NestingMode::ProperlyNested: return pn_annotation_strategy_->Init(); break; @@ -123,22 +125,29 @@ void Delegator::Init(std::string_view nesting_mode, std::string_view backend) { // should be thread safe to call? void Delegator::RegionStart(std::string_view name) { + + switch (nesting_mode_) { - case NestingMode::Detecion: case NestingMode::NotProperlyNested: return npn_annotation_strategy_->RegionStart({name}); + case NestingMode::Detection: case NestingMode::ProperlyNested: - return pn_annotation_strategy_->RegionStart({name}); + // First push to stack + name_stack_.push(std::string(name)); + return pn_annotation_strategy_->RegionStart({name, name_stack_}); break; } } void Delegator::RegionStop(std::string_view name) { switch (nesting_mode_) { - case NestingMode::Detecion: + case NestingMode::NotProperlyNested: return npn_annotation_strategy_->RegionStop({name}); + break; + case NestingMode::Detection: case NestingMode::ProperlyNested: - return pn_annotation_strategy_->RegionStopLast({name}); + pn_annotation_strategy_->RegionStopLast({name,name_stack_}); + if(!name_stack_.empty()) {name_stack_.pop();} break; } }; @@ -146,9 +155,11 @@ void Delegator::RegionStop(std::string_view name) { void Delegator::Finalize() { switch (nesting_mode_) { - case NestingMode::Detecion: + case NestingMode::NotProperlyNested: return npn_annotation_strategy_->Finalize(); + break; + case NestingMode::Detection: case NestingMode::ProperlyNested: return pn_annotation_strategy_->Finalize(); break; diff --git a/src/delegator.hpp b/src/delegator.hpp index 1e67cf03fd34c0f49493c10df2263bd92ee4e5ab..b9427831667220fe2f94e69a4a8ba8559d6d2a12 100644 --- a/src/delegator.hpp +++ b/src/delegator.hpp @@ -7,7 +7,7 @@ #include -enum class NestingMode { Detecion, ProperlyNested, NotProperlyNested }; +enum class NestingMode { Detection, ProperlyNested, NotProperlyNested }; class Delegator { private: @@ -15,7 +15,10 @@ private: pn_annotation_strategy_; inline static std::unique_ptr npn_annotation_strategy_; - inline static NestingMode nesting_mode_ = NestingMode::Detecion; + + inline static thread_local std::stack name_stack_; + + inline static NestingMode nesting_mode_ = NestingMode::Detection; public: static void Init(std::string_view nesting_mode, std::string_view backend); diff --git a/src/parallelism_helper.cpp b/src/parallelism_helper.cpp index 88f5e137b4c52239bc840a226e492226d77ff5dc..52f43786908fe0ef9e447c29bb76e3bfc099cbaf 100644 --- a/src/parallelism_helper.cpp +++ b/src/parallelism_helper.cpp @@ -19,6 +19,10 @@ bool MPIHelper::IsRankNumber(int asked_rank) const { return asked_rank== mpi_comm_rank; } +int MPIHelper::getRankNumber() const{ + return mpi_comm_rank; +} + MPIHelper::MPIHelper(){ // First check if MPI is initialized int is_initialized; @@ -49,6 +53,11 @@ bool MPIHelper::IsRankNumber(int asked_rank) const { return asked_rank==0; } + +int MPIHelper::getRankNumber() const{ + return mpi_comm_rank; +} + MPIHelper::MPIHelper(){} diff --git a/src/parallelism_helper.hpp b/src/parallelism_helper.hpp index a0edf1fa96ccd0daa38e9864d0d0b76f0b809816..5aebb05a0ab2df42e68a52bb96d053e41aba2a85 100644 --- a/src/parallelism_helper.hpp +++ b/src/parallelism_helper.hpp @@ -21,6 +21,8 @@ class MPIHelper{ bool IsUsingMPI() const; // Checks if rank is a certain one, and 0 if MPI is not active bool IsRankNumber(int asked_rank) const; + + int getRankNumber() const; }; diff --git a/src/strategies.hpp b/src/strategies.hpp index 6b5011031c384d95ac9a58b0888db49386d28ef5..dbebd8b6e861e6c4ea4dcbe27f3425f02835ec38 100644 --- a/src/strategies.hpp +++ b/src/strategies.hpp @@ -4,6 +4,7 @@ #include #include #include +#include /* @@ -35,6 +36,7 @@ struct ParallelismDescriptor{ struct ProperlyNestedRegionInformation { std::string_view name; + const std::stack &stack_; }; struct NotProperlyNestedRegionInformation {