From 6a3724ceace2bf04b7e7a0b5f66678e19347dd95 Mon Sep 17 00:00:00 2001 From: Christian von Elm Date: Mon, 11 Dec 2023 14:31:50 +0100 Subject: [PATCH] Implement Topdown metric support This commit adds support for the topdown metrics (fe-bound, be-bound etc.) as seen in perf --- CMakeLists.txt | 3 + include/lo2s/config.hpp | 2 + include/lo2s/measurement_scope.hpp | 8 ++ include/lo2s/monitor/scope_monitor.hpp | 3 + include/lo2s/perf/counter/topdown/reader.hpp | 72 +++++++++++ include/lo2s/perf/counter/topdown/writer.hpp | 54 ++++++++ include/lo2s/trace/trace.hpp | 28 +++++ src/config.cpp | 4 + src/monitor/scope_monitor.cpp | 9 ++ src/perf/counter/topdown/reader.cpp | 123 +++++++++++++++++++ src/perf/counter/topdown/writer.cpp | 61 +++++++++ src/trace/trace.cpp | 15 +++ 12 files changed, 382 insertions(+) create mode 100644 include/lo2s/perf/counter/topdown/reader.hpp create mode 100644 include/lo2s/perf/counter/topdown/writer.hpp create mode 100644 src/perf/counter/topdown/reader.cpp create mode 100644 src/perf/counter/topdown/writer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eee607ae..ff50e1ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,9 @@ set(SOURCE_FILES src/perf/counter/userspace/reader.cpp src/perf/counter/userspace/writer.cpp + src/perf/counter/topdown/reader.cpp + + src/perf/counter/topdown/writer.cpp src/perf/counter/group/writer.cpp src/perf/sample/writer.cpp diff --git a/include/lo2s/config.hpp b/include/lo2s/config.hpp index 13ed1127..4b465208 100644 --- a/include/lo2s/config.hpp +++ b/include/lo2s/config.hpp @@ -96,6 +96,8 @@ struct Config bool use_nec; std::chrono::microseconds nec_read_interval; std::chrono::milliseconds nec_check_interval; + //topdown + bool topdown; }; const Config& config(); diff --git a/include/lo2s/measurement_scope.hpp b/include/lo2s/measurement_scope.hpp index e8239761..0b16bdac 100644 --- a/include/lo2s/measurement_scope.hpp +++ b/include/lo2s/measurement_scope.hpp @@ -31,6 +31,7 @@ enum class MeasurementScopeType USERSPACE_METRIC, BIO, SYSCALL, + TOPDOWN, UNKNOWN }; @@ -71,6 +72,11 @@ struct MeasurementScope return { MeasurementScopeType::SYSCALL, s }; } + static MeasurementScope topdown(ExecutionScope s) + { + return { MeasurementScopeType::SYSCALL, s }; + } + friend bool operator==(const MeasurementScope& lhs, const MeasurementScope& rhs) { return (lhs.scope == rhs.scope) && lhs.type == rhs.type; @@ -101,6 +107,8 @@ struct MeasurementScope return fmt::format("block layer I/O events for {}", scope.name()); case MeasurementScopeType::SYSCALL: return fmt::format("syscall events for {}", scope.name()); + case MeasurementScopeType::TOPDOWN: + return fmt::format("topdown events for {}", scope.name()); default: throw new std::runtime_error("Unknown ExecutionScopeType!"); } diff --git a/include/lo2s/monitor/scope_monitor.hpp b/include/lo2s/monitor/scope_monitor.hpp index f68cd6e7..6ac3cb9d 100644 --- a/include/lo2s/monitor/scope_monitor.hpp +++ b/include/lo2s/monitor/scope_monitor.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -74,6 +75,8 @@ class ScopeMonitor : public PollMonitor std::unique_ptr sample_writer_; std::unique_ptr group_counter_writer_; std::unique_ptr userspace_counter_writer_; + + std::unique_ptr topdown_writer_; }; } // namespace monitor } // namespace lo2s diff --git a/include/lo2s/perf/counter/topdown/reader.hpp b/include/lo2s/perf/counter/topdown/reader.hpp new file mode 100644 index 00000000..cf8a5216 --- /dev/null +++ b/include/lo2s/perf/counter/topdown/reader.hpp @@ -0,0 +1,72 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2021, + * Technische Universitaet Dresden, Germany + * + * lo2s is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lo2s is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lo2s. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +#include +namespace lo2s +{ +namespace perf +{ +namespace counter +{ +namespace topdown +{ +struct TopdownEvent +{ + uint64_t nr; + uint64_t slots; + uint64_t retiring; + uint64_t bad_spec; + uint64_t fe_bound; + uint64_t be_bound; +}; + +template +class Reader +{ +public: + Reader(ExecutionScope scope); + + void read(); + + int fd() + { + return timer_fd_; + } + +protected: + int leader_fd_; + std::vector counter_fds_; + int timer_fd_; +}; +} // namespace topdown +} // namespace counter +} // namespace perf +} // namespace lo2s diff --git a/include/lo2s/perf/counter/topdown/writer.hpp b/include/lo2s/perf/counter/topdown/writer.hpp new file mode 100644 index 00000000..a82237a6 --- /dev/null +++ b/include/lo2s/perf/counter/topdown/writer.hpp @@ -0,0 +1,54 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2018, + * Technische Universitaet Dresden, Germany + * + * lo2s is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lo2s is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lo2s. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace lo2s +{ +namespace perf +{ +namespace counter +{ +namespace topdown +{ +class Writer : public Reader +{ +public: + Writer(ExecutionScope scope, trace::Trace& trace); + + bool handle(TopdownEvent* ev); + +private: + time::Converter time_converter_; + otf2::writer::local& writer_; + otf2::definition::metric_instance metric_instance_; + otf2::event::metric metric_event_; +}; +} // namespace topdown +} // namespace counter +} // namespace perf +} // namespace lo2s diff --git a/include/lo2s/trace/trace.hpp b/include/lo2s/trace/trace.hpp index 0005fc45..ca7018fb 100644 --- a/include/lo2s/trace/trace.hpp +++ b/include/lo2s/trace/trace.hpp @@ -130,6 +130,7 @@ class Trace otf2::definition::mapping_table merge_syscall_contexts(const std::set& used_syscalls); otf2::writer::local& sample_writer(const ExecutionScope& scope); + otf2::writer::local& topdown_writer(const ExecutionScope& scope); otf2::writer::local& metric_writer(const MeasurementScope& scope); otf2::writer::local& syscall_writer(const Cpu& cpu); otf2::writer::local& bio_writer(BlockDevice dev); @@ -169,6 +170,32 @@ class Trace return cpuid_metric_class_; } + otf2::definition::metric_class topdown_metric_class() + { + if (!topdown_metric_class_) + { + topdown_metric_class_ = registry_.create( + otf2::common::metric_occurence::async, otf2::common::recorder_kind::abstract); + topdown_metric_class_->add_member(metric_member( + "slots", "Number of UOPS slots", otf2::common::metric_mode::absolute_point, + otf2::common::type::int64, "slots")); + topdown_metric_class_->add_member(metric_member( + "retiring", "Number of UOPS retired", otf2::common::metric_mode::absolute_point, + otf2::common::type::int64, "slots")); + topdown_metric_class_->add_member(metric_member( + "bad_spec", "Number of UOPS lost to bad speculation", + otf2::common::metric_mode::absolute_point, otf2::common::type::int64, "slots")); + topdown_metric_class_->add_member(metric_member( + "fe_bound", "Number of front-end bound UOPS", + otf2::common::metric_mode::absolute_point, otf2::common::type::int64, "slots")); + topdown_metric_class_->add_member(metric_member( + "be_bound", "Number of back-end bound UOPS", + otf2::common::metric_mode::absolute_point, otf2::common::type::int64, "slots")); + } + + return topdown_metric_class_; + } + otf2::definition::metric_member& get_event_metric_member(perf::EventDescription event) { return registry_.emplace( @@ -347,6 +374,7 @@ class Trace otf2::definition::regions_group& syscall_regions_group_; otf2::definition::detail::weak_ref cpuid_metric_class_; + otf2::definition::detail::weak_ref topdown_metric_class_; std::map, otf2::definition::detail::weak_ref> perf_group_metric_classes_; std::map, otf2::definition::detail::weak_ref> diff --git a/src/config.cpp b/src/config.cpp index 4407c7f5..d1a48d50 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -161,6 +161,7 @@ void parse_program_options(int argc, const char** argv) auto& sensors_options = parser.group("sensors options"); auto& io_options = parser.group("I/O recording options"); auto& nec_options = parser.group("NEC SX-Aurora Tsubasa recording options"); + auto& topdown_options = parser.group("Topdown metrics recording options"); lo2s::Config config; @@ -344,6 +345,8 @@ void parse_program_options(int argc, const char** argv) .metavar("MSEC") .default_value("100"); + topdown_options.toggle("topdown", "Enable recording of topdown metrics"); + nitro::options::arguments arguments; try { @@ -370,6 +373,7 @@ void parse_program_options(int argc, const char** argv) config.use_sensors = arguments.given("sensors"); config.use_block_io = arguments.given("block-io"); config.use_nec = arguments.given("nec"); + config.topdown = arguments.given("topdown"); config.command = arguments.positionals(); if (arguments.given("help")) diff --git a/src/monitor/scope_monitor.cpp b/src/monitor/scope_monitor.cpp index c97c3d1b..efe6273c 100644 --- a/src/monitor/scope_monitor.cpp +++ b/src/monitor/scope_monitor.cpp @@ -72,6 +72,11 @@ ScopeMonitor::ScopeMonitor(ExecutionScope scope, MainMonitor& parent, bool enabl add_fd(userspace_counter_writer_->fd()); } + if(config().topdown) + { + topdown_writer_ = std::make_unique(scope, parent.trace()); + add_fd(topdown_writer_->fd()); + } // note: start() can now be called } @@ -116,6 +121,10 @@ void ScopeMonitor::monitor(int fd) { userspace_counter_writer_->read(); } + if (fd == topdown_writer_->fd()) + { + topdown_writer_->read(); + } } } // namespace monitor } // namespace lo2s diff --git a/src/perf/counter/topdown/reader.cpp b/src/perf/counter/topdown/reader.cpp new file mode 100644 index 00000000..b9932c6b --- /dev/null +++ b/src/perf/counter/topdown/reader.cpp @@ -0,0 +1,123 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2021, + * Technische Universitaet Dresden, Germany + * + * lo2s is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lo2s is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lo2s. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +extern "C" +{ +#include +#include +#include +} +namespace lo2s +{ +namespace perf +{ +namespace counter +{ +namespace topdown +{ + +template +Reader::Reader(ExecutionScope scope) +{ + struct itimerspec tspec; + memset(&tspec, 0, sizeof(struct itimerspec)); + tspec.it_value.tv_nsec = 1; + + tspec.it_interval.tv_sec = + std::chrono::duration_cast(config().userspace_read_interval).count(); + + tspec.it_interval.tv_nsec = + (config().userspace_read_interval % std::chrono::seconds(1)).count(); + timer_fd_ = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + + timerfd_settime(timer_fd_, TFD_TIMER_ABSTIME, &tspec, NULL); + + struct perf_event_attr leader_perf_attr; + memset(&leader_perf_attr, 0, sizeof(struct perf_event_attr)); + leader_perf_attr.type = PERF_TYPE_RAW; + leader_perf_attr.size = sizeof(struct perf_event_attr); + leader_perf_attr.config = 0x400; + leader_perf_attr.read_format = PERF_FORMAT_GROUP; + leader_perf_attr.disabled = 1; + + leader_fd_ = perf_event_open(&leader_perf_attr, scope, -1, 0); + + if (leader_fd_ == -1) + { + Log::error() << errno; + } + std::vector counters = { 0x8000, 0x8100, 0x8200, 0x8300 }; + + for (auto counter : counters) + { + struct perf_event_attr perf_attr; + memset(&perf_attr, 0, sizeof(perf_attr)); + perf_attr.type = PERF_TYPE_RAW, perf_attr.size = sizeof(struct perf_event_attr); + perf_attr.config = counter; + perf_attr.read_format = PERF_FORMAT_GROUP; + perf_attr.disabled = 0; + int fd = perf_event_open(&perf_attr, scope, leader_fd_, 0); + + if (fd == -1) + { + Log::error() << errno; + } + counter_fds_.emplace_back(fd); + } + + ::ioctl(leader_fd_, PERF_EVENT_IOC_ENABLE); +} + +template +void Reader::read() +{ + struct TopdownEvent ev; + [[maybe_unused]] auto ret = ::read(leader_fd_, &ev, sizeof(ev)); + + assert(ret == sizeof(ev)); + + static_cast(this)->handle(&ev); + + [[maybe_unused]] uint64_t expirations; + if (::read(timer_fd_, &expirations, sizeof(expirations)) == -1 && errno != EAGAIN) + { + Log::error() << "Flushing timer fd failed"; + throw_errno(); + } +} + +template class Reader; +} // namespace topdown +} // namespace counter +} // namespace perf +} // namespace lo2s diff --git a/src/perf/counter/topdown/writer.cpp b/src/perf/counter/topdown/writer.cpp new file mode 100644 index 00000000..36790f88 --- /dev/null +++ b/src/perf/counter/topdown/writer.cpp @@ -0,0 +1,61 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2017, + * Technische Universitaet Dresden, Germany + * + * lo2s is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * lo2s is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with lo2s. If not, see . + */ + +#include +#include +#include +#include + +namespace lo2s +{ +namespace perf +{ +namespace counter +{ +namespace topdown +{ +Writer::Writer(ExecutionScope scope, trace::Trace& trace) +: Reader(scope), time_converter_(time::Converter::instance()), writer_(trace.topdown_writer(scope)), + metric_instance_(trace.metric_instance(trace.topdown_metric_class(), writer_.location(), + trace.location(scope))), + metric_event_(otf2::chrono::genesis(), metric_instance_) +{ +} + +bool Writer::handle(TopdownEvent* ev) +{ + metric_event_.timestamp(lo2s::time::now()); + otf2::event::metric::values& values = metric_event_.raw_values(); + + values[0] = ev->slots; + values[1] = ev->retiring; + values[2] = ev->bad_spec; + values[3] = ev->fe_bound; + values[4] = ev->be_bound; + + writer_.write(metric_event_); + return false; +} + +} // namespace topdown +} // namespace counter +} // namespace perf +} // namespace lo2s diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp index 00e6a0d7..d408b57c 100644 --- a/src/trace/trace.cpp +++ b/src/trace/trace.cpp @@ -479,6 +479,21 @@ otf2::writer::local& Trace::bio_writer(BlockDevice dev) return archive_(intern_location); } +otf2::writer::local& Trace::topdown_writer(const ExecutionScope& writer_scope) +{ + MeasurementScope scope = MeasurementScope::topdown(writer_scope); + + const auto& intern_location = registry_.emplace( + ByMeasurementScope(scope), intern(scope.name()), + registry_.get( + ByExecutionScope(groups_.get_parent(writer_scope))), + otf2::definition::location::location_type::cpu_thread); + + comm_locations_group_.add_member(intern_location); + + return archive_(intern_location); +} + otf2::writer::local& Trace::create_metric_writer(const std::string& name) { const auto& location = registry_.create(