diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a9695c2d56..6572399cae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,8 +20,15 @@ set(TOP_LEVEL_SUBDIRS activefn # Add single-directory components context event handler sequence termination - scheduler standalone runtime trace timing demangle rdmahandle metrics + scheduler standalone runtime trace timing demangle rdmahandle ) +if(vt_perf_enabled) + list( + APPEND + TOP_LEVEL_SUBDIRS + metrics + ) +endif() set( PROJECT_SUBDIRS_LIST # Add component along with sub-directories diff --git a/src/vt/metrics/example_events.h b/src/vt/metrics/example_events.h index 78fd7ed286..59e274fca5 100644 --- a/src/vt/metrics/example_events.h +++ b/src/vt/metrics/example_events.h @@ -11,5 +11,15 @@ const std::unordered_map> example_even {"cache_references", std::make_pair(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES)}, {"cache_misses", std::make_pair(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES)}, {"branch_instructions", std::make_pair(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS)}, - {"branch_misses", std::make_pair(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES)} + {"branch_misses", std::make_pair(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES)}, + {"fp_arith_inst_retired_scalar_double", std::make_pair(PERF_TYPE_RAW, 0x5301c7)}, + {"fp_arith_inst_retired_scalar_single", std::make_pair(PERF_TYPE_RAW, 0x5302c7)}, + {"fp_arith_inst_retired_128b_packed_double", std::make_pair(PERF_TYPE_RAW, 0x5304c7)}, + {"fp_arith_inst_retired_128b_packed_single", std::make_pair(PERF_TYPE_RAW, 0x5308c7)}, + {"fp_arith_inst_retired_256b_packed_double", std::make_pair(PERF_TYPE_RAW, 0x5310c7)}, + {"fp_arith_inst_retired_256b_packed_single", std::make_pair(PERF_TYPE_RAW, 0x5320c7)}, + {"fp_arith_inst_retired_512b_packed_double", std::make_pair(PERF_TYPE_RAW, 0x5340c7)}, + {"fp_arith_inst_retired_512b_packed_single", std::make_pair(PERF_TYPE_RAW, 0x5380c7)} }; + +#endif /*INCLUDED_VT_METRICS_EXAMPLE_EVENTS_H*/ diff --git a/src/vt/metrics/perf_data.cc b/src/vt/metrics/perf_data.cc new file mode 100644 index 0000000000..7a0f0db7be --- /dev/null +++ b/src/vt/metrics/perf_data.cc @@ -0,0 +1,217 @@ +/* +//@HEADER +// ***************************************************************************** +// +// perf_data.cc +// DARMA/vt => Virtual Transport +// +// Copyright 2019-2021 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#include "perf_data.h" + +namespace vt { namespace metrics { + +PerfData::PerfData() +{ + event_map_ = example_event_map; + const char* env_p = getenv("VT_EVENTS"); + + // Check if the environment variable is set + if (env_p == nullptr) { + vtWarn("Warning: Environment variable VT_EVENTS not set, defaulting to 'instructions' for the PAPI event set.\n"); + event_names_.push_back("instructions"); + } + else { + std::string env_str(env_p); + std::stringstream ss(env_str); + std::string item; + + while (std::getline(ss, item, ',')) + { + event_names_.push_back(item); + } + } + + for (const auto &event_name : event_names_) + { + if (event_map_.find(event_name) == event_map_.end()) + { + cleanupBeforeAbort(); + vtAbort("Event name isn't in known perf events map: " + event_name); + } + } +} + +PerfData::~PerfData() +{ + for (auto& [task, fds] : task_fds_map_) + { + for (int fd : fds) + { + if (fd != -1) + { + close(fd); + } + } + } +} + +void PerfData::startTaskMeasurement(runnable::RunnableNew* task) +{ + // fmt::print("*********** startTaskMeasurement: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); + if (task_fds_map_.find(task) == task_fds_map_.end()) + { + // fmt::print(" Task was not in the task_fds_map_.\n"); + std::vector fds; + for (const auto &event_name : event_names_) + { + struct perf_event_attr pe = {}; + pe.type = event_map_.at(event_name).first; + pe.size = sizeof(struct perf_event_attr); + pe.config = event_map_.at(event_name).second; + + pe.disabled = 1; + pe.exclude_kernel = 1; + pe.exclude_hv = 1; + pe.inherit = 1; // Ensure event is inherited by threads + + if (event_name == "instructions") { + pe.pinned = 1; + } + + int fd = perf_event_open(&pe, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); + if (fd == -1) + { + cleanupBeforeAbort(); + vtAbort("Error opening perf event: " + std::string(strerror(errno))); + } + + fds.push_back(fd); + } + task_fds_map_[task] = fds; + } + + for (int fd : task_fds_map_[task]) + { + if (fd != -1) { + ioctl(fd, PERF_EVENT_IOC_RESET, 0); + ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); + } + } +} + +void PerfData::stopTaskMeasurement(runnable::RunnableNew* task) +{ + // fmt::print("*********** stopTaskMeasurement: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); + if (task_fds_map_.find(task) != task_fds_map_.end()) + { + for (int fd : task_fds_map_[task]) + { + if (fd != -1) { + ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); + } + } + } +} + +std::unordered_map PerfData::getTaskMeasurements(runnable::RunnableNew* task) +{ + // fmt::print("*********** getTaskMeasurements: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); + std::unordered_map measurements; + if (task_fds_map_.find(task) != task_fds_map_.end()) + { + for (size_t i = 0; i < task_fds_map_[task].size(); ++i) + { + uint64_t count; + if (task_fds_map_[task][i] != -1 && read(task_fds_map_[task][i], &count, sizeof(uint64_t)) != -1) { + measurements[event_names_[i]] = count; + } + else { + vtWarn("Failed to read perf event data for: " + event_names_[i]); + } + } + } + return measurements; +} + +void PerfData::purgeTask(runnable::RunnableNew* task) +{ + if (task_fds_map_.find(task) != task_fds_map_.end()) + { + for (int fd : task_fds_map_[task]) + { + if (fd != -1) + { + close(fd); + } + } + task_fds_map_.erase(task); + } +} + +std::unordered_map> PerfData::getEventMap() const { return event_map_; } + +void PerfData::startup() { event_map_ = example_event_map; } + +std::string PerfData::name() { return "PerfData"; } + +template +void PerfData::serialize(SerializerT& s) { + s | event_map_; +} + +void PerfData::cleanupBeforeAbort() +{ + for (auto& [task, fds] : task_fds_map_) + { + for (int fd : fds) + { + if (fd != -1) + { + close(fd); + } + } + } + task_fds_map_.clear(); +} + +long PerfData::perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) +{ + return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); +} + +}} // end namespace vt::metrics diff --git a/src/vt/metrics/perf_data.h b/src/vt/metrics/perf_data.h index 7cd9860622..c12313aa66 100644 --- a/src/vt/metrics/perf_data.h +++ b/src/vt/metrics/perf_data.h @@ -54,8 +54,12 @@ #include #include #include +#include +#include +#include +#include -namespace vt { namespace metrics { +namespace vt { namespace metrics { /** \file */ @@ -68,189 +72,28 @@ namespace vt { namespace metrics { struct PerfData: runtime::component::Component { public: - PerfData() - { - fmt::print("Constructing PerfData struct\n"); - event_map_ = example_event_map; - const char* env_p = getenv("VT_EVENTS"); + PerfData(); + ~PerfData(); + void startTaskMeasurement(runnable::RunnableNew* task); + void stopTaskMeasurement(runnable::RunnableNew* task); + std::unordered_map getTaskMeasurements(runnable::RunnableNew* task); + void purgeTask(runnable::RunnableNew* task); - // Check if the environment variable is set - if (env_p == nullptr) { - vtWarn("Warning: Environment variable VT_EVENTS not set, defaulting to 'instructions' for the PAPI event set.\n"); - event_names_.push_back("instructions"); - } - else { - std::string env_str(env_p); - std::stringstream ss(env_str); - std::string item; - - while (std::getline(ss, item, ',')) - { - event_names_.push_back(item); - } - } - - for (const auto &event_name : event_names_) - { - if (event_map_.find(event_name) == event_map_.end()) - { - cleanupBeforeAbort(); - vtAbort("Event name isn't in known perf events map: " + event_name); - } - } - } - - ~PerfData() - { - fmt::print("Destructing PerfData struct\n"); - for (auto& [task, fds] : task_fds_map_) - { - for (int fd : fds) - { - if (fd != -1) - { - close(fd); - } - } - } - } - - void startTaskMeasurement(runnable::RunnableNew* task) - { - fmt::print("*********** startTaskMeasurement: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); - if (task_fds_map_.find(task) == task_fds_map_.end()) - { - fmt::print(" Task was not in the task_fds_map_.\n"); - std::vector fds; - for (const auto &event_name : event_names_) - { - struct perf_event_attr pe = {}; - pe.type = event_map_.at(event_name).first; - pe.size = sizeof(struct perf_event_attr); - pe.config = event_map_.at(event_name).second; - - pe.disabled = 1; - pe.exclude_kernel = 1; - pe.exclude_hv = 1; - pe.inherit = 1; // Ensure event is inherited by threads - - if (event_name == "instructions") { - pe.pinned = 1; - } - - int fd = perf_event_open(&pe, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); - if (fd == -1) - { - cleanupBeforeAbort(); - vtAbort("Error opening perf event: " + std::string(strerror(errno))); - } - - fds.push_back(fd); - } - task_fds_map_[task] = fds; - } - else { - fmt::print(" Task was already in the task_fds_map_.\n"); - } - - for (int fd : task_fds_map_[task]) - { - if (fd != -1) { - ioctl(fd, PERF_EVENT_IOC_RESET, 0); - ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); - } - } - } - - void stopTaskMeasurement(runnable::RunnableNew* task) - { - fmt::print("*********** stopTaskMeasurement: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); - if (task_fds_map_.find(task) != task_fds_map_.end()) - { - for (int fd : task_fds_map_[task]) - { - if (fd != -1) { - ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); - } - } - } - } - - std::unordered_map getTaskMeasurements(runnable::RunnableNew* task) - { - fmt::print("*********** getTaskMeasurements: PerfData currently is tracking {} tasks\n", task_fds_map_.size()); - std::unordered_map measurements; - if (task_fds_map_.find(task) != task_fds_map_.end()) - { - for (size_t i = 0; i < task_fds_map_[task].size(); ++i) - { - uint64_t count; - if (task_fds_map_[task][i] != -1 && read(task_fds_map_[task][i], &count, sizeof(uint64_t)) != -1) { - measurements[event_names_[i]] = count; - } - else { - vtWarn("Failed to read perf event data for: " + event_names_[i]); - } - } - } - return measurements; - } - - void purgeTask(runnable::RunnableNew* task) - { - if (task_fds_map_.find(task) != task_fds_map_.end()) - { - for (int fd : task_fds_map_[task]) - { - if (fd != -1) - { - close(fd); - } - } - task_fds_map_.erase(task); - } - else - { - fmt::print("Attempting to purge task but task isn't being tracked.\n"); - } - } - - std::unordered_map> getEventMap() const { return event_map_; } - - void startup() override { event_map_ = example_event_map; } - - std::string name() override { return "PerfData"; } + std::unordered_map> getEventMap() const; + void startup() override; + std::string name() override; template - void serialize(SerializerT& s) { - s | event_map_; - } + void serialize(SerializerT& s); private: - std::unordered_map> event_map_ = {}; - std::unordered_map> task_fds_map_ = {}; - std::vector event_names_ = {}; - - void cleanupBeforeAbort() - { - for (auto& [task, fds] : task_fds_map_) - { - for (int fd : fds) - { - if (fd != -1) - { - close(fd); - } - } - } - task_fds_map_.clear(); - } + std::unordered_map> event_map_; + std::unordered_map> task_fds_map_; + std::vector event_names_; - static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) - { - return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); - } + void cleanupBeforeAbort(); + static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags); }; }} // end namespace vt::metrics diff --git a/src/vt/runnable/runnable.cc b/src/vt/runnable/runnable.cc index 8897fa9136..c7473b13c0 100644 --- a/src/vt/runnable/runnable.cc +++ b/src/vt/runnable/runnable.cc @@ -50,7 +50,6 @@ #include "vt/configs/debug/debug_var_unused.h" #include "vt/configs/arguments/app_config.h" #include "vt/scheduler/thread_manager.h" -#include "vt/metrics/perf_data.h" namespace vt { namespace runnable { @@ -268,7 +267,12 @@ void RunnableNew::send(elm::ElementIDStruct elm, MsgSizeType bytes) { /*static*/ void RunnableNew::operator delete(void* ptr) { RunnableNewAlloc::runnable->dealloc(reinterpret_cast(ptr)); - vt::thePerfData()->purgeTask(reinterpret_cast(ptr)); + #if vt_check_enabled(perf) + if (vt::thePerfData() != nullptr && curRT->isInitialized()) + { + vt::thePerfData()->purgeTask(reinterpret_cast(ptr)); + } + #endif } /*static*/ diff --git a/src/vt/runnable/runnable.h b/src/vt/runnable/runnable.h index 50b0e5ff9b..5c1020945a 100644 --- a/src/vt/runnable/runnable.h +++ b/src/vt/runnable/runnable.h @@ -54,6 +54,8 @@ #include "vt/context/runnable_context/continuation.h" #include "vt/pool/static_sized/memory_pool_equal.h" #include "vt/elm/elm_id.h" +#include "vt/metrics/perf_data.h" +#include "vt/runtime/runtime_inst.h" // fwd-declarations for the element types namespace vt { namespace vrt {