From 32cb5ccb998716632af005514bcdeb88109f24dd Mon Sep 17 00:00:00 2001 From: Christian von Elm Date: Sun, 8 Dec 2024 16:00:21 +0100 Subject: [PATCH] Revert "implement suggested changes in PerfEvent" This reverts commit fc3217069e6eceed22b8e2f2e9ecc496eb594de2. --- CMakeLists.txt | 4 +- include/lo2s/perf/bio/writer.hpp | 15 +- .../lo2s/perf/counter/counter_collection.hpp | 6 +- .../lo2s/perf/counter/counter_provider.hpp | 9 +- include/lo2s/perf/counter/group/reader.hpp | 6 +- .../lo2s/perf/counter/userspace/reader.hpp | 4 +- include/lo2s/perf/event_provider.hpp | 10 +- include/lo2s/perf/io_reader.hpp | 24 +- include/lo2s/perf/multi_reader.hpp | 2 +- include/lo2s/perf/{event.hpp => reader.hpp} | 184 +++++++----- include/lo2s/perf/sample/reader.hpp | 23 +- include/lo2s/perf/syscall/reader.hpp | 23 +- include/lo2s/perf/time/reader.hpp | 4 +- include/lo2s/perf/tracepoint/event.hpp | 95 ------ include/lo2s/perf/tracepoint/format.hpp | 81 +++++ include/lo2s/perf/tracepoint/reader.hpp | 17 +- include/lo2s/perf/tracepoint/writer.hpp | 3 +- include/lo2s/platform.hpp | 4 +- include/lo2s/trace/reg_keys.hpp | 7 +- include/lo2s/trace/trace.hpp | 6 +- src/config.cpp | 12 +- src/monitor/tracepoint_monitor.cpp | 3 +- src/perf/counter/counter_provider.cpp | 32 +- src/perf/counter/group/reader.cpp | 39 ++- src/perf/counter/userspace/reader.cpp | 22 +- src/perf/event_provider.cpp | 42 +-- src/perf/{event.cpp => reader.cpp} | 283 ++++++++---------- src/perf/time/reader.cpp | 4 +- src/perf/tracepoint/{event.cpp => format.cpp} | 69 +++-- src/perf/tracepoint/writer.cpp | 6 +- src/perf/util.cpp | 2 +- src/platform.cpp | 2 +- src/trace/trace.cpp | 12 +- 33 files changed, 533 insertions(+), 522 deletions(-) rename include/lo2s/perf/{event.hpp => reader.hpp} (61%) delete mode 100644 include/lo2s/perf/tracepoint/event.hpp rename src/perf/{event.cpp => reader.cpp} (66%) rename src/perf/tracepoint/{event.cpp => format.cpp} (66%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0af879b..916b3e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,7 +185,7 @@ set(SOURCE_FILES src/process_controller.cpp src/perf/event_provider.cpp - src/perf/event.cpp + src/perf/reader.cpp src/perf/bio/block_device.cpp src/perf/counter/counter_provider.cpp @@ -197,8 +197,8 @@ set(SOURCE_FILES src/perf/sample/writer.cpp src/perf/time/converter.cpp src/perf/time/reader.cpp + src/perf/tracepoint/format.cpp src/perf/tracepoint/writer.cpp - src/perf/tracepoint/event.cpp src/perf/syscall/writer.cpp src/time/time.cpp diff --git a/include/lo2s/perf/bio/writer.hpp b/include/lo2s/perf/bio/writer.hpp index 8f07f993..6619e0e6 100644 --- a/include/lo2s/perf/bio/writer.hpp +++ b/include/lo2s/perf/bio/writer.hpp @@ -155,11 +155,12 @@ class Writer } } - std::vector get_tracepoints() + std::vector get_tracepoints() { - bio_queue_ = perf::tracepoint::TracepointEvent("block:block_bio_queue"); - bio_issue_ = perf::tracepoint::TracepointEvent("block:block_rq_issue"); - bio_complete_ = perf::tracepoint::TracepointEvent("block:block_rq_complete"); + + bio_queue_ = tracepoint::EventFormat("block:block_bio_queue"); + bio_issue_ = tracepoint::EventFormat("block:block_rq_issue"); + bio_complete_ = tracepoint::EventFormat("block:block_rq_complete"); return { bio_queue_, bio_issue_, bio_complete_ }; } @@ -181,9 +182,9 @@ class Writer trace::Trace& trace_; time::Converter& time_converter_; - perf::tracepoint::TracepointEvent bio_queue_; - perf::tracepoint::TracepointEvent bio_issue_; - perf::tracepoint::TracepointEvent bio_complete_; + tracepoint::EventFormat bio_queue_; + tracepoint::EventFormat bio_issue_; + tracepoint::EventFormat bio_complete_; // The unit "sector" is always 512 bit large, regardless of the actual sector size of the device static constexpr int SECTOR_SIZE = 512; }; diff --git a/include/lo2s/perf/counter/counter_collection.hpp b/include/lo2s/perf/counter/counter_collection.hpp index 864aaccb..df4bb66c 100644 --- a/include/lo2s/perf/counter/counter_collection.hpp +++ b/include/lo2s/perf/counter/counter_collection.hpp @@ -21,7 +21,7 @@ #pragma once -#include +#include #include @@ -33,8 +33,8 @@ namespace counter { struct CounterCollection { - PerfEvent leader; - std::vector counters; + SysfsEvent leader; + std::vector counters; double get_scale(int index) const { diff --git a/include/lo2s/perf/counter/counter_provider.hpp b/include/lo2s/perf/counter/counter_provider.hpp index 7f925b55..850b640f 100644 --- a/include/lo2s/perf/counter/counter_provider.hpp +++ b/include/lo2s/perf/counter/counter_provider.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include @@ -54,12 +54,11 @@ class CounterProvider bool has_userspace_counters(ExecutionScope scope); CounterCollection collection_for(MeasurementScope scope); - std::vector get_tracepoint_event_names(); private: - PerfEvent group_leader_; - std::vector group_events_; - std::vector userspace_events_; + SysfsEvent group_leader_; + std::vector group_events_; + std::vector userspace_events_; }; } // namespace counter } // namespace perf diff --git a/include/lo2s/perf/counter/group/reader.hpp b/include/lo2s/perf/counter/group/reader.hpp index 034a6a00..2d0ed070 100644 --- a/include/lo2s/perf/counter/group/reader.hpp +++ b/include/lo2s/perf/counter/group/reader.hpp @@ -23,8 +23,8 @@ #include #include -#include #include +#include #include @@ -60,8 +60,8 @@ class Reader : public EventReader }; protected: - PerfEventGuard counter_leader_; - std::vector counters_; + PerfEventInstance counter_leader_; + std::vector counters_; CounterCollection counter_collection_; GroupCounterBuffer counter_buffer_; }; diff --git a/include/lo2s/perf/counter/userspace/reader.hpp b/include/lo2s/perf/counter/userspace/reader.hpp index fbcfdfcb..1d92939b 100644 --- a/include/lo2s/perf/counter/userspace/reader.hpp +++ b/include/lo2s/perf/counter/userspace/reader.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -60,7 +60,7 @@ class Reader UserspaceCounterBuffer counter_buffer_; int timer_fd_; - std::vector counters_; + std::vector counters_; std::vector data_; }; } // namespace userspace diff --git a/include/lo2s/perf/event_provider.hpp b/include/lo2s/perf/event_provider.hpp index 6ec4a465..f0e30ce7 100644 --- a/include/lo2s/perf/event_provider.hpp +++ b/include/lo2s/perf/event_provider.hpp @@ -45,14 +45,14 @@ class EventProvider return instance_mutable(); } - static PerfEvent get_event_by_name(const std::string& name); + static SysfsEvent get_event_by_name(const std::string& name); static bool has_event(const std::string& name); - static std::vector get_predefined_events(); + static std::vector get_predefined_events(); static std::vector get_pmu_events(); - static PerfEvent fallback_metric_leader_event(); + static SysfsEvent fallback_metric_leader_event(); class InvalidEvent : public std::runtime_error { @@ -70,9 +70,9 @@ class EventProvider return e; } - PerfEvent cache_event(const std::string& name); + SysfsEvent cache_event(const std::string& name); - std::unordered_map event_map_; + std::unordered_map event_map_; }; } // namespace perf diff --git a/include/lo2s/perf/io_reader.hpp b/include/lo2s/perf/io_reader.hpp index eec5b610..2344e8ec 100644 --- a/include/lo2s/perf/io_reader.hpp +++ b/include/lo2s/perf/io_reader.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -60,11 +60,11 @@ struct __attribute((__packed__)) TracepointSampleType struct IoReaderIdentity { - IoReaderIdentity(std::string tracepoint, Cpu cpu) : tracepoint(tracepoint), cpu(cpu) + IoReaderIdentity(tracepoint::EventFormat tracepoint, Cpu cpu) : tracepoint(tracepoint), cpu(cpu) { } - tracepoint::TracepointEvent tracepoint; + tracepoint::EventFormat tracepoint; Cpu cpu; friend bool operator>(const IoReaderIdentity& lhs, const IoReaderIdentity& rhs) @@ -93,9 +93,11 @@ class IoReader : public PullReader public: IoReader(IoReaderIdentity identity) : identity_(identity) { + perf::TracepointEvent event(0, identity.tracepoint.id()); + try { - event_ = identity_.tracepoint.open(identity.cpu); + ev_instance_ = event.open(identity.cpu); } catch (const std::system_error& e) { @@ -107,10 +109,10 @@ class IoReader : public PullReader try { - init_mmap(event_.get_fd()); + init_mmap(ev_instance_.get_fd()); Log::debug() << "perf_tracepoint_reader mmap initialized"; - event_.enable(); + ev_instance_.enable(); } catch (...) { @@ -121,7 +123,7 @@ class IoReader : public PullReader void stop() { - event_.disable(); + ev_instance_.disable(); } TracepointSampleType* top() @@ -131,7 +133,7 @@ class IoReader : public PullReader int fd() const { - return event_.get_fd(); + return ev_instance_.get_fd(); } IoReader& operator=(const IoReader&) = delete; @@ -141,19 +143,19 @@ class IoReader : public PullReader { PullReader::operator=(std::move(other)); std::swap(identity_, other.identity_); - std::swap(event_, other.event_); + std::swap(ev_instance_, other.ev_instance_); return *this; } IoReader(IoReader&& other) : PullReader(std::move(other)), identity_(other.identity_) { - std::swap(event_, other.event_); + std::swap(ev_instance_, other.ev_instance_); } private: IoReaderIdentity identity_; - PerfEventGuard event_; + perf::PerfEventInstance ev_instance_; }; } // namespace perf } // namespace lo2s diff --git a/include/lo2s/perf/multi_reader.hpp b/include/lo2s/perf/multi_reader.hpp index 4692067a..f2d0f7a0 100644 --- a/include/lo2s/perf/multi_reader.hpp +++ b/include/lo2s/perf/multi_reader.hpp @@ -53,7 +53,7 @@ class MultiReader { for (auto tp : writer_.get_tracepoints()) { - IoReaderIdentity id(tp.name(), cpu); + IoReaderIdentity id(tp, cpu); auto reader = readers_.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id)); fds_.emplace_back(reader.first->second.fd()); diff --git a/include/lo2s/perf/event.hpp b/include/lo2s/perf/reader.hpp similarity index 61% rename from include/lo2s/perf/event.hpp rename to include/lo2s/perf/reader.hpp index 1a451579..cbd8df1c 100644 --- a/include/lo2s/perf/event.hpp +++ b/include/lo2s/perf/reader.hpp @@ -2,7 +2,7 @@ * This file is part of the lo2s software. * Linux OTF2 sampling * - * Copyright (c) 2024, + * Copyright (c) 2017, * Technische Universitaet Dresden, Germany * * lo2s is free software: you can redistribute it and/or modify @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -36,7 +35,6 @@ extern "C" { #include -#include } #else extern "C" @@ -59,7 +57,7 @@ enum class Availability UNIVERSAL }; -class PerfEventGuard; +class PerfEventInstance; /** * Base class for all Event types @@ -68,39 +66,29 @@ class PerfEventGuard; class PerfEvent { public: - PerfEvent(const std::string& ev_name, bool enable_on_exec = false); - PerfEvent([[maybe_unused]] uint64_t addr, bool enable_on_exec = false); - PerfEvent(std::string name, perf_type_id type, std::uint64_t config, std::uint64_t config1 = 0, - std::set cpus = std::set()); + PerfEvent(bool enable_on_exec); PerfEvent(); /** * returns an opened instance of any PerfEvent object + * @param group_fd file descriptor of the leader event, omit if there is no leader */ - PerfEventGuard open(std::variant location, int cgroup_fd = config().cgroup_fd); - PerfEventGuard open(ExecutionScope location, int cgroup_fd = config().cgroup_fd); + PerfEventInstance open(std::variant location, int group_fd = -1, + int cgroup_fd = config().cgroup_fd); - /** - * returns an opened instance of a PerfEvent object after formating it as a leader Event - */ - PerfEventGuard open_as_group_leader(std::variant location, - int cgroup_fd = config().cgroup_fd); - PerfEventGuard open_as_group_leader(ExecutionScope location, - int cgroup_fd = config().cgroup_fd); - - const Availability& get_availability() const + perf_event_attr& get_attr() { - return availability_; - }; + return attr_; + } - std::string get_name() const + double get_scale() const { - return name_; + return scale_; } - std::set get_cpus() const + void set_scale(double scale) { - return cpus_; + scale_ = scale; } std::string get_unit() const @@ -108,31 +96,60 @@ class PerfEvent return unit_; } - perf_event_attr& get_attr() + void set_unit(std::string unit) { - return attr_; + unit_ = unit; } - double get_scale() const + friend bool operator==(const PerfEvent& lhs, const PerfEvent& rhs) { - return scale_; + return !memcmp(&lhs.attr_, &rhs.attr_, sizeof(struct perf_event_attr)); } - void set_scale(double scale) + friend bool operator<(const PerfEvent& lhs, const PerfEvent& rhs) { - scale_ = scale; + return memcmp(&lhs.attr_, &rhs.attr_, sizeof(struct perf_event_attr)); } - void set_unit(std::string unit) + ~PerfEvent() { - unit_ = unit; } - void set_sample_period(const int& period); - void set_sample_freq(); - void set_availability(); +protected: + struct perf_event_attr attr_; + double scale_ = 1; + std::string unit_ = "#"; +}; + +/** + * Contains an event parsed from sysfs + * @note call on either as_counter(), as_sample() or as_group_leader() after creation to get a valid + * event, otherwise the availability will be set to UNAVAILABLE + */ +class SysfsEvent : public PerfEvent +{ +public: + SysfsEvent(); + SysfsEvent(bool enable_on_exec, const std::string& ev_name); + SysfsEvent(std::string name, perf_type_id type, std::uint64_t config, std::uint64_t config1 = 0, + std::set cpus = std::set()); + + void as_counter(); + void as_group_leader(); + void as_sample(); const std::set& supported_cpus() const; + + // automatically determine availability + void set_availability(); + + const Availability& get_availability() const + { + return availability_; + }; + + bool degrade_percision(); + bool is_valid() const; bool is_available_in(ExecutionScope scope) const @@ -142,91 +159,93 @@ class PerfEvent return scope.is_thread() || cpus_.empty() || cpus_.count(scope.as_cpu()); } - bool degrade_precision(); - - friend bool operator==(const PerfEvent& lhs, const PerfEvent& rhs) + std::string get_name() const { - return !memcmp(&lhs.attr_, &rhs.attr_, sizeof(struct perf_event_attr)); + return name_; } - friend bool operator<(const PerfEvent& lhs, const PerfEvent& rhs) + std::set get_cpus() const { - return memcmp(&lhs.attr_, &rhs.attr_, sizeof(struct perf_event_attr)); + return cpus_; } - friend bool operator>(const PerfEvent& lhs, const PerfEvent& rhs) + void set_common_attrs(std::string name, perf_type_id type, std::uint64_t config, + std::uint64_t config1 = 0, std::set cpus = std::set()) { - return memcmp(&lhs.attr_, &rhs.attr_, sizeof(struct perf_event_attr)); + name_ = name; + cpus_ = cpus; + attr_.type = type; + attr_.config = config; + attr_.config1 = config1; } -protected: - void set_common_attrs(bool enable_on_exec); - - struct perf_event_attr attr_; - double scale_ = 1; - std::string unit_ = "#"; - +private: std::string name_ = ""; std::set cpus_; Availability availability_ = Availability::UNAVAILABLE; }; +class TimeEvent : public PerfEvent +{ +public: + TimeEvent(bool enable_on_exec, uint64_t addr); +}; + /** - * Contains an event parsed from sysfs - * @note call on as_sample() after creation to get a valid - * event, otherwise the availability will be set to UNAVAILABLE + * Contains an event that is addressable via ID + * @note Call on as_syscall() after creation if needed */ -class SysfsEvent : public PerfEvent +class TracepointEvent : public PerfEvent { public: - using PerfEvent::PerfEvent; + TracepointEvent(bool enable_on_exec, int event_id); - void as_sample(); + void as_syscall() + { + attr_.sample_type |= PERF_SAMPLE_IDENTIFIER; + } +}; + +/** + * Contains a raw event for testing + */ +class RawEvent : public PerfEvent +{ +public: + RawEvent(bool enable_on_exec); }; /** * Contains an opened instance of PerfEvent. - * Use any PerfEvent.open() method to construct an object + * Use PerfEvent.open() to construct an object */ -class PerfEventGuard +class PerfEventInstance { public: - PerfEventGuard(); - PerfEventGuard(PerfEvent& ev, std::variant location, int group_fd, int cgroup_fd); + PerfEventInstance(); + PerfEventInstance(PerfEvent& ev, std::variant location, int group_fd, + int cgroup_fd); - PerfEventGuard(PerfEventGuard&) = delete; - PerfEventGuard& operator=(const PerfEventGuard&) = delete; + PerfEventInstance(PerfEventInstance&) = delete; + PerfEventInstance& operator=(const PerfEventInstance&) = delete; - PerfEventGuard(PerfEventGuard&& other) + PerfEventInstance(PerfEventInstance&& other) { std::swap(fd_, other.fd_); std::swap(ev_, other.ev_); } - PerfEventGuard& operator=(PerfEventGuard&& other) + PerfEventInstance& operator=(PerfEventInstance&& other) { std::swap(fd_, other.fd_); std::swap(ev_, other.ev_); return *this; } - /** - * opens child as a counter of the calling (leader) event - */ - PerfEventGuard open_child(PerfEvent child, std::variant location, - int cgroup_fd = config().cgroup_fd); - PerfEventGuard open_child(PerfEvent child, ExecutionScope location, - int cgroup_fd = config().cgroup_fd); - void enable(); void disable(); - void set_id(const uint64_t& id) - { - ioctl(fd_, PERF_EVENT_IOC_ID, &id); - } - - void set_output(const PerfEventGuard& other_ev); + void set_output(const PerfEventInstance& other_ev); void set_syscall_filter(); int get_fd() const @@ -243,16 +262,15 @@ class PerfEventGuard T read() { T val; - - if (::read(fd_, &val, sizeof(val)) == -1) + if (::read(fd_, &val, sizeof(val)) != sizeof(T)) { - throw_errno(); + throw std::system_error(errno, std::system_category()); } return val; } - ~PerfEventGuard() + ~PerfEventInstance() { close(fd_); } diff --git a/include/lo2s/perf/sample/reader.hpp b/include/lo2s/perf/sample/reader.hpp index 814b578a..ffcb2155 100644 --- a/include/lo2s/perf/sample/reader.hpp +++ b/include/lo2s/perf/sample/reader.hpp @@ -21,9 +21,9 @@ #pragma once -#include #include #include +#include #include #include @@ -85,7 +85,7 @@ class Reader : public EventReader Log::debug() << "initializing event_reader for:" << scope.name() << ", enable_on_exec: " << enable_on_exec; - SysfsEvent event(config().sampling_event, enable_on_exec); + SysfsEvent event(enable_on_exec, config().sampling_event); event.as_sample(); @@ -93,7 +93,14 @@ class Reader : public EventReader { try { - event_ = event.open(scope); + if (scope.is_cpu()) + { + ev_instance_ = event.open(scope.as_cpu()); + } + else + { + ev_instance_ = event.open(scope.as_thread()); + } } catch (const std::system_error& e) { @@ -105,7 +112,7 @@ class Reader : public EventReader continue; } - if (!event.degrade_precision()) + if (!event.degrade_percision()) { Log::error() << "perf_event_open for sampling failed: " << e.what(); @@ -116,19 +123,19 @@ class Reader : public EventReader throw_errno(); } } - } while (!event_.is_valid()); + } while (!ev_instance_.is_valid()); Log::debug() << "Using precise_ip level: " << event.get_attr().precise_ip; // Exception safe, so much wow! try { - init_mmap(event_.get_fd()); + init_mmap(ev_instance_.get_fd()); Log::debug() << "mmap initialized"; if (!enable_on_exec) { - event_.enable(); + ev_instance_.enable(); } } catch (...) @@ -161,7 +168,7 @@ class Reader : public EventReader bool has_cct_; private: - PerfEventGuard event_; + PerfEventInstance ev_instance_; }; } // namespace sample } // namespace perf diff --git a/include/lo2s/perf/syscall/reader.hpp b/include/lo2s/perf/syscall/reader.hpp index c30bc88a..3fdf7088 100644 --- a/include/lo2s/perf/syscall/reader.hpp +++ b/include/lo2s/perf/syscall/reader.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,6 +39,7 @@ extern "C" { #include #include +#include #include } @@ -68,11 +69,15 @@ class Reader : public EventReader Reader(Cpu cpu) : cpu_(cpu) { - tracepoint::TracepointEvent enter_event("raw_syscalls:sys_enter"); - tracepoint::TracepointEvent exit_event("raw_syscalls:sys_exit"); + TracepointEvent enter_event(0, tracepoint::EventFormat("raw_syscalls:sys_enter").id()); + TracepointEvent exit_event(0, tracepoint::EventFormat("raw_syscalls:sys_exit").id()); + + enter_event.as_syscall(); + exit_event.as_syscall(); try { + // instance with tracepoint::EventFormat enter (default) enter_ev_ = enter_event.open(cpu_); exit_ev_ = exit_event.open(cpu_); } @@ -96,8 +101,8 @@ class Reader : public EventReader enter_ev_.enable(); exit_ev_.enable(); - enter_ev_.set_id(sys_enter_id); - exit_ev_.set_id(sys_exit_id); + ioctl(enter_ev_.get_fd(), PERF_EVENT_IOC_ID, &sys_enter_id); + ioctl(exit_ev_.get_fd(), PERF_EVENT_IOC_ID, &sys_exit_id); } Reader(Reader&& other) @@ -119,10 +124,14 @@ class Reader : public EventReader private: Cpu cpu_; - PerfEventGuard enter_ev_; - PerfEventGuard exit_ev_; + PerfEventInstance enter_ev_; + PerfEventInstance exit_ev_; + const static std::filesystem::path base_path; }; +template +const std::filesystem::path Reader::base_path = + std::filesystem::path("/sys/kernel/debug/tracing/events"); } // namespace syscall } // namespace perf } // namespace lo2s diff --git a/include/lo2s/perf/time/reader.hpp b/include/lo2s/perf/time/reader.hpp index cf5046bc..5e0595f5 100644 --- a/include/lo2s/perf/time/reader.hpp +++ b/include/lo2s/perf/time/reader.hpp @@ -22,8 +22,8 @@ #pragma once #include -#include #include +#include #include @@ -67,7 +67,7 @@ class Reader : public EventReader perf::Clock::time_point perf_time; private: - PerfEventGuard ev_instance_; + PerfEventInstance ev_instance_; }; } // namespace time } // namespace perf diff --git a/include/lo2s/perf/tracepoint/event.hpp b/include/lo2s/perf/tracepoint/event.hpp deleted file mode 100644 index ee4668e8..00000000 --- a/include/lo2s/perf/tracepoint/event.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of the lo2s software. - * Linux OTF2 sampling - * - * Copyright (c) 2024, - * 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 - -namespace lo2s -{ -namespace perf -{ -namespace tracepoint -{ - -/** - * Contains an event that is addressable via name - */ -class TracepointEvent : public PerfEvent -{ -public: - class ParseError : public std::runtime_error - { - public: - ParseError(const std::string& what) : std::runtime_error(what) - { - } - - ParseError(const std::string& what, int error_code); - }; - - TracepointEvent() : id_(-1) - { - } - - TracepointEvent(const std::string& name, bool enable_on_exec = false); - - void parse_format(); - - const auto& fields() const - { - return fields_; - } - - const auto& field(const std::string& name) const - { - for (const auto& field : fields()) - { - if (field.name() == name) - { - return field; - } - } - throw std::out_of_range("field not found"); - } - - int id() - { - return id_; - } - - std::string name() const - { - return name_; - } - -private: - void parse_format_line(const std::string& line); - - const static std::filesystem::path base_path_; - int id_; - std::string name_; - std::vector fields_; -}; - -} // namespace tracepoint -} // namespace perf -} // namespace lo2s diff --git a/include/lo2s/perf/tracepoint/format.hpp b/include/lo2s/perf/tracepoint/format.hpp index 3a768231..c21982b1 100644 --- a/include/lo2s/perf/tracepoint/format.hpp +++ b/include/lo2s/perf/tracepoint/format.hpp @@ -86,6 +86,87 @@ class EventField std::ptrdiff_t offset_; std::size_t size_ = 0; }; + +class EventFormat +{ +public: + class ParseError : public std::runtime_error + { + public: + ParseError(const std::string& what) : std::runtime_error(what) + { + } + + ParseError(const std::string& what, int error_code); + }; + +public: + EventFormat() : id_(-1) + { + } + + EventFormat(const std::string& name); + + int id() const + { + return id_; + } + + const auto& common_fields() const + { + return common_fields_; + } + + const auto& fields() const + { + return fields_; + } + + std::string name() const + { + return name_; + } + + const auto& field(const std::string& name) const + { + for (const auto& field : fields()) + { + if (field.name() == name) + { + return field; + } + } + throw std::out_of_range("field not found"); + } + + friend bool operator==(const EventFormat& lhs, const EventFormat& rhs) + { + return lhs.id_ == rhs.id_; + } + + friend bool operator<(const EventFormat& lhs, const EventFormat& rhs) + { + return lhs.id_ < rhs.id_; + } + + friend bool operator>(const EventFormat& lhs, const EventFormat& rhs) + { + return lhs.id_ > rhs.id_; + } + + static std::vector get_tracepoint_event_names(); + +private: + void parse_id(); + void parse_format_line(const std::string& line); + + std::string name_; + int id_; + std::vector common_fields_; + std::vector fields_; + + const static std::filesystem::path base_path_; +}; } // namespace tracepoint } // namespace perf } // namespace lo2s diff --git a/include/lo2s/perf/tracepoint/reader.hpp b/include/lo2s/perf/tracepoint/reader.hpp index 32d48fbf..99e6ca13 100644 --- a/include/lo2s/perf/tracepoint/reader.hpp +++ b/include/lo2s/perf/tracepoint/reader.hpp @@ -21,10 +21,10 @@ #pragma once -#include #include #include +#include #include #include @@ -108,17 +108,17 @@ class Reader : public EventReader { struct perf_event_header header; uint64_t time; - uint64_t id; // uint32_t size; // char data[size]; RecordDynamicFormat raw_data; }; - Reader(Cpu cpu, std::string name) : event_(name), cpu_(cpu) + Reader(Cpu cpu, int event_id) : cpu_(cpu) { + TracepointEvent event(0, event_id); try { - ev_instance_ = event_.open(cpu_); + ev_instance_ = event.open(cpu_); } catch (const std::system_error& e) { @@ -127,7 +127,7 @@ class Reader : public EventReader } Log::debug() << "Opened perf_sample_tracepoint_reader for " << cpu_ << " with id " - << event_.id(); + << event_id; try { @@ -156,13 +156,16 @@ class Reader : public EventReader protected: using EventReader::init_mmap; - TracepointEvent event_; private: Cpu cpu_; - PerfEventGuard ev_instance_; + PerfEventInstance ev_instance_; + const static std::filesystem::path base_path; }; +template +const std::filesystem::path Reader::base_path = + std::filesystem::path("/sys/kernel/debug/tracing/events"); } // namespace tracepoint } // namespace perf } // namespace lo2s diff --git a/include/lo2s/perf/tracepoint/writer.hpp b/include/lo2s/perf/tracepoint/writer.hpp index aad1a46e..5de7c329 100644 --- a/include/lo2s/perf/tracepoint/writer.hpp +++ b/include/lo2s/perf/tracepoint/writer.hpp @@ -44,7 +44,7 @@ namespace tracepoint class Writer : public Reader { public: - Writer(Cpu cpu, const std::string& name, trace::Trace& trace, + Writer(Cpu cpu, const EventFormat& event, trace::Trace& trace, const otf2::definition::metric_class& metric_class); Writer(const Writer& other) = delete; @@ -57,6 +57,7 @@ class Writer : public Reader bool handle(const Reader::RecordSampleType* sample); private: + EventFormat event_; otf2::writer::local& writer_; otf2::definition::metric_instance metric_instance_; diff --git a/include/lo2s/platform.hpp b/include/lo2s/platform.hpp index b20ccba7..9a220259 100644 --- a/include/lo2s/platform.hpp +++ b/include/lo2s/platform.hpp @@ -32,7 +32,7 @@ #include -#include +#include /* gracefully copied from https://github.com/deater/perf_event_tests/blob/master/ */ @@ -94,6 +94,6 @@ enum class Processor ARM1176 = 204, }; -std::vector get_mem_events(); +std::vector get_mem_events(); } // namespace platform } // namespace lo2s diff --git a/include/lo2s/trace/reg_keys.hpp b/include/lo2s/trace/reg_keys.hpp index 1b17f199..056d8522 100644 --- a/include/lo2s/trace/reg_keys.hpp +++ b/include/lo2s/trace/reg_keys.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -137,24 +137,21 @@ struct BySamplingEventName { }; -using BySamplingEvent = SimpleKeyType; +using BySamplingEvent = SimpleKeyType; struct ByCounterCollectionTag { }; - using ByCounterCollection = SimpleKeyType; struct ByNecDeviceTag { }; - using ByNecDevice = SimpleKeyType; struct ByMeasurementScopeTypeTag { }; - using ByMeasurementScopeType = SimpleKeyType; template diff --git a/include/lo2s/trace/trace.hpp b/include/lo2s/trace/trace.hpp index 6741e268..fb44becd 100644 --- a/include/lo2s/trace/trace.hpp +++ b/include/lo2s/trace/trace.hpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -176,7 +175,7 @@ class Trace return cpuid_metric_class_; } - otf2::definition::metric_member& get_event_metric_member(perf::PerfEvent event) + otf2::definition::metric_member& get_event_metric_member(perf::SysfsEvent event) { return registry_.emplace( BySamplingEvent(event), intern(event.get_name()), intern(event.get_name()), @@ -275,8 +274,7 @@ class Trace return metric_class; } - otf2::definition::metric_class& - tracepoint_metric_class(const perf::tracepoint::TracepointEvent& event); + otf2::definition::metric_class& tracepoint_metric_class(const std::string& event_name); const otf2::definition::interrupt_generator& interrupt_generator() const { diff --git a/src/config.cpp b/src/config.cpp index e79c4335..2c2a100b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -72,7 +72,7 @@ static inline void list_arguments_sorted(std::ostream& os, const std::string& de } static inline void print_availability(std::ostream& os, const std::string& description, - std::vector events) + const std::vector& events) { std::vector event_names; for (const auto& ev : events) @@ -489,11 +489,8 @@ void parse_program_options(int argc, const char** argv) { print_availability(std::cout, "predefined events", perf::EventProvider::get_predefined_events()); - - // TODO: find a better solution ? - std::vector sys_events = perf::EventProvider::get_pmu_events(); - std::vector events(sys_events.begin(), sys_events.end()); - print_availability(std::cout, "Kernel PMU events", events); + print_availability(std::cout, "Kernel PMU events", + perf::EventProvider::get_pmu_events()); #ifdef HAVE_LIBPFM print_availability(std::cout, "Libpfm events", @@ -508,8 +505,7 @@ void parse_program_options(int argc, const char** argv) if (arguments.given("list-tracepoints")) { - std::vector tracepoints = - perf::counter::CounterProvider::instance().get_tracepoint_event_names(); + auto tracepoints = perf::tracepoint::EventFormat::get_tracepoint_event_names(); if (tracepoints.empty()) { diff --git a/src/monitor/tracepoint_monitor.cpp b/src/monitor/tracepoint_monitor.cpp index 5719c40f..98195062 100644 --- a/src/monitor/tracepoint_monitor.cpp +++ b/src/monitor/tracepoint_monitor.cpp @@ -39,8 +39,9 @@ TracepointMonitor::TracepointMonitor(trace::Trace& trace, Cpu cpu) for (const auto& event_name : config().tracepoint_events) { auto& mc = trace.tracepoint_metric_class(event_name); + perf::tracepoint::EventFormat event(event_name); std::unique_ptr writer = - std::make_unique(cpu, event_name, trace, mc); + std::make_unique(cpu, event, trace, mc); add_fd(writer->fd()); perf_writers_.emplace(std::piecewise_construct, std::forward_as_tuple(writer->fd()), diff --git a/src/perf/counter/counter_provider.cpp b/src/perf/counter/counter_provider.cpp index 4713f9d2..e42b5b0d 100644 --- a/src/perf/counter/counter_provider.cpp +++ b/src/perf/counter/counter_provider.cpp @@ -43,7 +43,7 @@ void CounterProvider::initialize_userspace_counters(const std::vector CounterProvider::get_tracepoint_event_names() -{ - try - { - std::ifstream ifs_available_events; - ifs_available_events.exceptions(std::ios::failbit | std::ios::badbit); - - ifs_available_events.open("/sys/kernel/debug/tracing/available_events"); - ifs_available_events.exceptions(std::ios::badbit); - - std::vector available; - - for (std::string tracepoint; std::getline(ifs_available_events, tracepoint);) - { - available.emplace_back(std::move(tracepoint)); - } - - return available; - } - catch (const std::ios_base::failure& e) - { - Log::debug() << "Retrieving kernel tracepoint event names failed: " << e.what(); - return {}; - } -} - bool CounterProvider::has_group_counters(ExecutionScope scope) { if (scope.is_process()) diff --git a/src/perf/counter/group/reader.cpp b/src/perf/counter/group/reader.cpp index 1492779d..857037d2 100644 --- a/src/perf/counter/group/reader.cpp +++ b/src/perf/counter/group/reader.cpp @@ -25,8 +25,8 @@ #include #include -#include #include +#include #include #include @@ -53,12 +53,19 @@ Reader::Reader(ExecutionScope scope, bool enable_on_exec) CounterProvider::instance().collection_for(MeasurementScope::group_metric(scope))), counter_buffer_(counter_collection_.counters.size() + 1) { - counter_collection_.leader.set_sample_freq(); + counter_collection_.leader.as_group_leader(); do { try { - counter_leader_ = counter_collection_.leader.open_as_group_leader(scope); + if (scope.is_cpu()) + { + counter_leader_ = std::move(counter_collection_.leader.open(scope.as_cpu())); + } + else + { + counter_leader_ = std::move(counter_collection_.leader.open(scope.as_thread())); + } } catch (const std::system_error& e) { @@ -87,17 +94,34 @@ Reader::Reader(ExecutionScope scope, bool enable_on_exec) { if (counter_ev.is_available_in(scope)) { - PerfEventGuard counter; - counter_ev.get_attr().exclude_kernel = - counter_collection_.leader.get_attr().exclude_kernel; + PerfEventInstance counter; do { try { - counter = counter_leader_.open_child(counter_ev, scope); + if (scope.is_cpu()) + { + counter = + std::move(counter_ev.open(scope.as_cpu(), counter_leader_.get_fd())); + } + else + { + counter = + std::move(counter_ev.open(scope.as_thread(), counter_leader_.get_fd())); + } } catch (const std::system_error& e) { + // perf_try_event_open was used here before + if (counter.get_fd() < 0 && errno == EACCES && + !counter_ev.get_attr().exclude_kernel && perf_event_paranoid() > 1) + { + counter_ev.get_attr().exclude_kernel = 1; + perf_warn_paranoid(); + + continue; + } + if (!counter.is_valid()) { Log::error() << "failed to add counter '" << counter_ev.get_name() @@ -122,7 +146,6 @@ Reader::Reader(ExecutionScope scope, bool enable_on_exec) { counter_leader_.enable(); } - EventReader::init_mmap(counter_leader_.get_fd()); } template class Reader; diff --git a/src/perf/counter/userspace/reader.cpp b/src/perf/counter/userspace/reader.cpp index 26b50158..3ffe7e0a 100644 --- a/src/perf/counter/userspace/reader.cpp +++ b/src/perf/counter/userspace/reader.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -53,11 +53,18 @@ Reader::Reader(ExecutionScope scope) { for (auto& event : counter_collection_.counters) { - PerfEventGuard counter; + PerfEventInstance counter; try { - counter = event.open(scope); + if (scope.is_cpu()) + { + counter = event.open(scope.as_cpu()); + } + else + { + counter = event.open(scope.as_thread()); + } } catch (const std::system_error& e) { @@ -68,7 +75,14 @@ Reader::Reader(ExecutionScope scope) event.get_attr().exclude_kernel = 1; perf_warn_paranoid(); - counter = event.open(scope); + if (scope.is_cpu()) + { + counter = std::move(event.open(scope.as_cpu())); + } + else + { + counter = std::move(event.open(scope.as_thread())); + } } if (!counter.is_valid()) diff --git a/src/perf/event_provider.cpp b/src/perf/event_provider.cpp index 5f4d608a..7fe4e2ed 100644 --- a/src/perf/event_provider.cpp +++ b/src/perf/event_provider.cpp @@ -55,7 +55,7 @@ namespace #define PERF_EVENT_HW(name, id) PERF_EVENT(name, PERF_TYPE_HARDWARE, PERF_COUNT_HW_##id) #define PERF_EVENT_SW(name, id) PERF_EVENT(name, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##id) -static lo2s::perf::PerfEvent HW_EVENT_TABLE[] = { +static lo2s::perf::SysfsEvent HW_EVENT_TABLE[] = { PERF_EVENT_HW("cpu-cycles", CPU_CYCLES), PERF_EVENT_HW("instructions", INSTRUCTIONS), PERF_EVENT_HW("cache-references", CACHE_REFERENCES), @@ -74,7 +74,7 @@ static lo2s::perf::PerfEvent HW_EVENT_TABLE[] = { #endif }; -static lo2s::perf::PerfEvent SW_EVENT_TABLE[] = { +static lo2s::perf::SysfsEvent SW_EVENT_TABLE[] = { PERF_EVENT_SW("cpu-clock", CPU_CLOCK), PERF_EVENT_SW("task-clock", TASK_CLOCK), PERF_EVENT_SW("page-faults", PAGE_FAULTS), @@ -193,20 +193,20 @@ static bool event_is_openable(SysfsEvent& provider) return true; } -static void populate_event_map(std::unordered_map& map) +static void populate_event_map(std::unordered_map& map) { Log::info() << "checking available events..."; map.reserve(array_size(HW_EVENT_TABLE) + array_size(SW_EVENT_TABLE) + array_size(CACHE_NAME_TABLE) * array_size(CACHE_OPERATION_TABLE)); for (auto& ev : HW_EVENT_TABLE) { - PerfEvent event(ev); + SysfsEvent event(ev); map.emplace(event.get_name(), event); } for (auto& ev : SW_EVENT_TABLE) { - PerfEvent event(ev); + SysfsEvent event(ev); map.emplace(event.get_name(), event); } @@ -218,9 +218,9 @@ static void populate_event_map(std::unordered_map& map) name_fmt.str(std::string()); name_fmt << cache.name << '-' << operation.name; - map.emplace(name_fmt.str(), PerfEvent(name_fmt.str(), PERF_TYPE_HW_CACHE, - make_cache_config(cache.id, operation.id.op_id, - operation.id.result_id))); + map.emplace(name_fmt.str(), SysfsEvent(name_fmt.str(), PERF_TYPE_HW_CACHE, + make_cache_config(cache.id, operation.id.op_id, + operation.id.result_id))); } } } @@ -273,7 +273,7 @@ std::vector EventProvider::get_pmu_events() return events; } -PerfEvent EventProvider::fallback_metric_leader_event() +SysfsEvent EventProvider::fallback_metric_leader_event() { Log::debug() << "checking for metric leader event..."; for (auto candidate : { @@ -284,7 +284,7 @@ PerfEvent EventProvider::fallback_metric_leader_event() { try { - const PerfEvent ev = get_event_by_name(candidate); + const SysfsEvent ev = get_event_by_name(candidate); Log::debug() << "found suitable metric leader event: " << candidate; return ev; } @@ -357,7 +357,7 @@ static constexpr std::uint64_t apply_mask(std::uint64_t value, std::uint64_t mas return res; } -static void event_attr_update(PerfEvent& event, std::uint64_t value, const std::string& format) +static void event_attr_update(SysfsEvent& event, std::uint64_t value, const std::string& format) { // Parse config terms // @@ -389,14 +389,14 @@ static void event_attr_update(PerfEvent& event, std::uint64_t value, const std:: } /** - * takes the name of an event, checks if it can be opened with each cpu and returns a PerfEvent + * takes the name of an event, checks if it can be opened with each cpu and returns a SysfsEvent * with a set of working cpus */ -const PerfEvent raw_read_event(const std::string& ev_name) +const SysfsEvent raw_read_event(const std::string& ev_name) { uint64_t code = std::stoull(ev_name.substr(1), nullptr, 16); - PerfEvent ev(0); + RawEvent ev(0); ev.get_attr().config = code; std::set cpus; @@ -404,7 +404,7 @@ const PerfEvent raw_read_event(const std::string& ev_name) { try { - PerfEventGuard ev_instance = ev.open(cpu, -1); + PerfEventInstance ev_instance = ev.open(cpu, -1, -1); cpus.emplace(cpu); } catch (const std::system_error& e) @@ -412,7 +412,7 @@ const PerfEvent raw_read_event(const std::string& ev_name) } } - const PerfEvent event(ev_name, PERF_TYPE_RAW, code, 0, cpus); + const SysfsEvent event(ev_name, PERF_TYPE_RAW, code, 0, cpus); // Do not check whether the event_is_openable because we don't know whether we are in // system or process mode return event; @@ -580,7 +580,7 @@ EventProvider::EventProvider() populate_event_map(event_map_); } -PerfEvent EventProvider::cache_event(const std::string& name) +SysfsEvent EventProvider::cache_event(const std::string& name) { // Format for raw events is r followed by a hexadecimal number static const std::regex raw_regex("r[[:xdigit:]]{1,8}"); @@ -608,10 +608,10 @@ PerfEvent EventProvider::cache_event(const std::string& name) /** * takes the name of an event and looks it up in an internal event list. - * @returns The corresponding PerfEvent if it is available + * @returns The corresponding SysfsEvent if it is available * @throws InvalidEvent if the event is unavailable */ -PerfEvent EventProvider::get_event_by_name(const std::string& name) +SysfsEvent EventProvider::get_event_by_name(const std::string& name) { auto& ev_map = instance().event_map_; auto event_it = ev_map.find(name); @@ -654,11 +654,11 @@ bool EventProvider::has_event(const std::string& name) } } -std::vector EventProvider::get_predefined_events() +std::vector EventProvider::get_predefined_events() { const auto& ev_map = instance().event_map_; - std::vector events; + std::vector events; events.reserve(ev_map.size()); for (const auto& event : ev_map) diff --git a/src/perf/event.cpp b/src/perf/reader.cpp similarity index 66% rename from src/perf/event.cpp rename to src/perf/reader.cpp index b3a42042..2b7b6ee9 100644 --- a/src/perf/event.cpp +++ b/src/perf/reader.cpp @@ -2,7 +2,7 @@ * This file is part of the lo2s software. * Linux OTF2 sampling * - * Copyright (c) 2024, + * Copyright (c) 2017, * Technische Universitaet Dresden, Germany * * lo2s is free software: you can redistribute it and/or modify @@ -19,13 +19,11 @@ * along with lo2s. If not, see . */ -#include #include +#include #include -#include - extern "C" { #include @@ -46,68 +44,7 @@ struct overloaded : Ts... template overloaded(Ts...) -> overloaded; -PerfEvent::PerfEvent() : name_("") -{ - memset(&attr_, 0, sizeof(attr_)); - attr_.size = sizeof(attr_); - attr_.type = PERF_TYPE_RAW; - - attr_.config = 0; - attr_.config1 = 0; - - attr_.sample_period = 0; - attr_.exclude_kernel = 1; -} - -PerfEvent::PerfEvent(const std::string& ev_name, bool enable_on_exec) : name_(ev_name) -{ - set_common_attrs(enable_on_exec); - - PerfEvent other = EventProvider::instance().get_event_by_name(ev_name); - attr_.type = static_cast(other.get_attr().type); - attr_.config = other.get_attr().config; - attr_.config1 = other.get_attr().config1; - cpus_ = other.get_cpus(); - - set_availability(); -} - -PerfEvent::PerfEvent([[maybe_unused]] uint64_t addr, bool enable_on_exec) -{ - set_common_attrs(enable_on_exec); - attr_.exclude_kernel = 1; - -#ifndef USE_HW_BREAKPOINT_COMPAT - attr_.type = PERF_TYPE_BREAKPOINT; - attr_.bp_type = HW_BREAKPOINT_W; - attr_.bp_len = HW_BREAKPOINT_LEN_8; - attr_.wakeup_events = 1; - attr_.bp_addr = addr; -#else - attr_.type = PERF_TYPE_HARDWARE; - attr_.config = PERF_COUNT_HW_INSTRUCTIONS; - attr_.sample_period = 100000000; - attr_.task = 1; -#endif -} - -PerfEvent::PerfEvent(std::string name, perf_type_id type, std::uint64_t config, - std::uint64_t config1, std::set cpus) -: name_(name), cpus_(cpus) -{ - memset(&attr_, 0, sizeof(attr_)); - attr_.size = sizeof(attr_); - attr_.type = -1; - - attr_.sample_type = PERF_SAMPLE_TIME; - attr_.type = type; - attr_.config = config; - attr_.config1 = config1; - - set_availability(); -} - -void PerfEvent::set_common_attrs(bool enable_on_exec) +PerfEvent::PerfEvent(bool enable_on_exec) { memset(&attr_, 0, sizeof(attr_)); attr_.size = sizeof(attr_); @@ -134,89 +71,59 @@ void PerfEvent::set_common_attrs(bool enable_on_exec) attr_.sample_type = PERF_SAMPLE_TIME; } -void PerfEvent::set_sample_period(const int& period) +PerfEvent::PerfEvent() { - Log::debug() << "counter::Reader: sample_period: " << period; - attr_.sample_period = period; + memset(&attr_, 0, sizeof(attr_)); + attr_.size = sizeof(attr_); + attr_.type = -1; } -void PerfEvent::set_sample_freq() +SysfsEvent::SysfsEvent() { - attr_.freq = config().metric_use_frequency; - if (attr_.freq) - { - Log::debug() << "counter::Reader: sample_freq: " << config().metric_frequency; - attr_.sample_freq = config().metric_frequency; - } - else - { - set_sample_period(config().metric_count); - } + set_common_attrs("", static_cast(-1), 0); + set_availability(); } -const std::set& PerfEvent::supported_cpus() const +SysfsEvent::SysfsEvent(bool enable_on_exec, const std::string& ev_name) : PerfEvent(enable_on_exec) { - if (cpus_.empty()) - { - return Topology::instance().cpus(); - } - return cpus_; + SysfsEvent other = EventProvider::instance().get_event_by_name(ev_name); + set_common_attrs(other.get_name(), static_cast(other.get_attr().type), + other.get_attr().config, other.get_attr().config1, other.get_cpus()); + set_availability(); } -bool PerfEvent::is_valid() const +SysfsEvent::SysfsEvent(std::string name, perf_type_id type, std::uint64_t config, + std::uint64_t config1, std::set cpus) +: PerfEvent() { - return (availability_ != Availability::UNAVAILABLE); + attr_.sample_type = PERF_SAMPLE_TIME; + + set_common_attrs(name, type, config, config1, cpus); + set_availability(); } -void PerfEvent::set_availability() +void SysfsEvent::as_counter() { - PerfEventGuard proc_ev; - PerfEventGuard sys_ev; - - try - { - proc_ev = open(Thread(0), -1); - } - catch (const std::system_error& e) - { - } - - try - { - sys_ev = open(*supported_cpus().begin(), -1); - } - catch (const std::system_error& e) - { - } - - if (proc_ev.get_fd() == -1) - { - if (sys_ev.get_fd() == -1) - availability_ = Availability::UNAVAILABLE; - else - availability_ = Availability::SYSTEM_MODE; - } - else - { - if (sys_ev.get_fd() == -1) - availability_ = Availability::PROCESS_MODE; - else - availability_ = Availability::UNIVERSAL; - } + attr_.sample_period = 0; } -bool PerfEvent::degrade_precision() +void SysfsEvent::as_group_leader() { - /* reduce exactness of IP can help if the kernel does not support really exact events */ - if (attr_.precise_ip == 0) + attr_.freq = config().metric_use_frequency; + + if (attr_.freq) { - return false; + Log::debug() << "counter::Reader: sample_freq: " << config().metric_frequency; + attr_.sample_freq = config().metric_frequency; } else { - attr_.precise_ip--; - return true; + Log::debug() << "counter::Reader: sample_period: " << config().metric_count; + attr_.sample_period = config().metric_count; } + + attr_.read_format |= PERF_FORMAT_GROUP; + attr_.sample_type |= PERF_SAMPLE_READ; } void SysfsEvent::as_sample() @@ -257,68 +164,116 @@ void SysfsEvent::as_sample() } attr_.precise_ip = 3; - - // make event available if possible - set_availability(); } -PerfEventGuard PerfEvent::open(std::variant location, int cgroup_fd) +const std::set& SysfsEvent::supported_cpus() const { - return PerfEventGuard(*this, location, -1, cgroup_fd); + if (cpus_.empty()) + { + return Topology::instance().cpus(); + } + return cpus_; } -PerfEventGuard PerfEvent::open(ExecutionScope location, int cgroup_fd) +bool SysfsEvent::degrade_percision() { - if (location.is_cpu()) + /* reduce exactness of IP can help if the kernel does not support really exact events */ + if (attr_.precise_ip == 0) { - return PerfEventGuard(*this, location.as_cpu(), -1, cgroup_fd); + return false; } else { - return PerfEventGuard(*this, location.as_thread(), -1, cgroup_fd); + attr_.precise_ip--; + return true; } } -PerfEventGuard PerfEvent::open_as_group_leader(std::variant location, int cgroup_fd) +bool SysfsEvent::is_valid() const { - attr_.read_format |= PERF_FORMAT_GROUP; - attr_.sample_type |= PERF_SAMPLE_READ; - - return open(location, cgroup_fd); + return (availability_ != Availability::UNAVAILABLE); } -PerfEventGuard PerfEvent::open_as_group_leader(ExecutionScope location, int cgroup_fd) +void SysfsEvent::set_availability() { - attr_.read_format |= PERF_FORMAT_GROUP; - attr_.sample_type |= PERF_SAMPLE_READ; + PerfEventInstance proc_ev; + PerfEventInstance sys_ev; - return open(location, cgroup_fd); -} + try + { + proc_ev = open(Thread(0), -1, -1); + } + catch (const std::system_error& e) + { + } -PerfEventGuard PerfEventGuard::open_child(PerfEvent child, std::variant location, - int cgroup_fd) -{ - return PerfEventGuard(child, location, fd_, cgroup_fd); -} + try + { + sys_ev = open(*supported_cpus().begin(), -1, -1); + } + catch (const std::system_error& e) + { + } -PerfEventGuard PerfEventGuard::open_child(PerfEvent child, ExecutionScope location, int cgroup_fd) -{ - if (location.is_cpu()) + if (proc_ev.get_fd() == -1) { - return PerfEventGuard(child, location.as_cpu(), fd_, cgroup_fd); + if (sys_ev.get_fd() == -1) + availability_ = Availability::UNAVAILABLE; + else + availability_ = Availability::SYSTEM_MODE; } else { - return PerfEventGuard(child, location.as_thread(), fd_, cgroup_fd); + if (sys_ev.get_fd() == -1) + availability_ = Availability::PROCESS_MODE; + else + availability_ = Availability::UNIVERSAL; } } -PerfEventGuard::PerfEventGuard() : fd_(-1) +TimeEvent::TimeEvent(bool enable_on_exec, uint64_t addr) : PerfEvent(enable_on_exec) { + attr_.exclude_kernel = 1; + +#ifndef USE_HW_BREAKPOINT_COMPAT + attr_.type = PERF_TYPE_BREAKPOINT; + attr_.bp_type = HW_BREAKPOINT_W; + attr_.bp_len = HW_BREAKPOINT_LEN_8; + attr_.wakeup_events = 1; + attr_.bp_addr = addr; +#else + attr_.type = PERF_TYPE_HARDWARE; + attr_.config = PERF_COUNT_HW_INSTRUCTIONS; + attr_.sample_period = 100000000; + attr_.task = 1; +#endif +} + +TracepointEvent::TracepointEvent(bool enable_on_exec, int event_id) : PerfEvent(enable_on_exec) +{ + attr_.type = PERF_TYPE_TRACEPOINT; + attr_.config = event_id; + + attr_.sample_type |= PERF_SAMPLE_RAW; } -PerfEventGuard::PerfEventGuard(PerfEvent& ev, std::variant location, int group_fd, - int cgroup_fd) +RawEvent::RawEvent(bool enable_on_exec) : PerfEvent(enable_on_exec) +{ + attr_.sample_period = 0; + attr_.type = PERF_TYPE_RAW; + attr_.config1 = 0; + attr_.exclude_kernel = 1; +} + +PerfEventInstance PerfEvent::open(std::variant location, int group_fd, int cgroup_fd) +{ + return PerfEventInstance(*this, location, group_fd, cgroup_fd); +}; + +PerfEventInstance::PerfEventInstance() : fd_(-1){}; + +PerfEventInstance::PerfEventInstance(PerfEvent& ev, std::variant location, + int group_fd, int cgroup_fd) : ev_(ev) { // can be deleted when scope gets replaced @@ -342,7 +297,7 @@ PerfEventGuard::PerfEventGuard(PerfEvent& ev, std::variant location } } -void PerfEventGuard::enable() +void PerfEventInstance::enable() { auto ret = ioctl(fd_, PERF_EVENT_IOC_ENABLE); if (ret == -1) @@ -351,7 +306,7 @@ void PerfEventGuard::enable() } } -void PerfEventGuard::disable() +void PerfEventInstance::disable() { auto ret = ioctl(fd_, PERF_EVENT_IOC_DISABLE); if (ret == -1) @@ -360,7 +315,7 @@ void PerfEventGuard::disable() } } -void PerfEventGuard::set_output(const PerfEventGuard& other_ev) +void PerfEventInstance::set_output(const PerfEventInstance& other_ev) { if (ioctl(fd_, PERF_EVENT_IOC_SET_OUTPUT, other_ev.get_fd()) == -1) { @@ -368,7 +323,7 @@ void PerfEventGuard::set_output(const PerfEventGuard& other_ev) } } -void PerfEventGuard::set_syscall_filter() +void PerfEventInstance::set_syscall_filter() { std::vector names; std::transform(config().syscall_filter.cbegin(), config().syscall_filter.end(), diff --git a/src/perf/time/reader.cpp b/src/perf/time/reader.cpp index 87651e35..4addb837 100644 --- a/src/perf/time/reader.cpp +++ b/src/perf/time/reader.cpp @@ -19,7 +19,7 @@ * along with lo2s. If not, see . */ -#include +#include #include #include @@ -57,7 +57,7 @@ Reader::Reader() static_assert(sizeof(local_time) == 8, "The local time object must not be a big fat " "object, or the hardware breakpoint won't work."); - PerfEvent event((uint64_t)&local_time); + TimeEvent event(0, (uint64_t)&local_time); try { diff --git a/src/perf/tracepoint/event.cpp b/src/perf/tracepoint/format.cpp similarity index 66% rename from src/perf/tracepoint/event.cpp rename to src/perf/tracepoint/format.cpp index 791e40d2..b0bee5a3 100644 --- a/src/perf/tracepoint/event.cpp +++ b/src/perf/tracepoint/format.cpp @@ -2,7 +2,7 @@ * This file is part of the lo2s software. * Linux OTF2 sampling * - * Copyright (c) 2024, + * Copyright (c) 2017, * Technische Universitaet Dresden, Germany * * lo2s is free software: you can redistribute it and/or modify @@ -19,8 +19,18 @@ * along with lo2s. If not, see . */ -#include +#include + +#include + +#include +#include #include +#include + +#include + +#include namespace lo2s { @@ -28,26 +38,12 @@ namespace perf { namespace tracepoint { - -TracepointEvent::TracepointEvent(const std::string& name, bool enable_on_exec) -: PerfEvent(), name_(name) -{ - set_common_attrs(enable_on_exec); - parse_format(); - - attr_.config = id_; - attr_.type = PERF_TYPE_TRACEPOINT; - attr_.sample_type |= PERF_SAMPLE_RAW | PERF_SAMPLE_IDENTIFIER; -} - -const std::filesystem::path TracepointEvent::base_path_ = "/sys/kernel/debug/tracing/events"; - -TracepointEvent::ParseError::ParseError(const std::string& what, int error_code) +EventFormat::ParseError::ParseError(const std::string& what, int error_code) : std::runtime_error{ what + ": " + std::strerror(error_code) } { } -void TracepointEvent::parse_format() +EventFormat::EventFormat(const std::string& name) : name_(name) { using namespace std::string_literals; @@ -87,7 +83,7 @@ void TracepointEvent::parse_format() } } -void TracepointEvent::parse_format_line(const std::string& line) +void EventFormat::parse_format_line(const std::string& line) { static std::regex field_regex( "^\\s+field:([^;]+);\\s+offset:(\\d+);\\s+size:(\\d+);\\s+signed:(\\d+);$"); @@ -113,14 +109,45 @@ void TracepointEvent::parse_format_line(const std::string& line) } std::string name = type_name_match[2]; - tracepoint::EventField field(name, offset, size); + EventField field(name, offset, size); - if (!nitro::lang::starts_with(name, "common_")) + if (nitro::lang::starts_with(name, "common_")) + { + common_fields_.emplace_back(field); + } + else { fields_.emplace_back(field); } } +std::vector EventFormat::get_tracepoint_event_names() +{ + try + { + std::ifstream ifs_available_events; + ifs_available_events.exceptions(std::ios::failbit | std::ios::badbit); + + ifs_available_events.open("/sys/kernel/debug/tracing/available_events"); + ifs_available_events.exceptions(std::ios::badbit); + + std::vector available; + + for (std::string tracepoint; std::getline(ifs_available_events, tracepoint);) + { + available.emplace_back(std::move(tracepoint)); + } + + return available; + } + catch (const std::ios_base::failure& e) + { + Log::debug() << "Retrieving kernel tracepoint event names failed: " << e.what(); + return {}; + } +} + +const std::filesystem::path EventFormat::base_path_ = "/sys/kernel/debug/tracing/events"; } // namespace tracepoint } // namespace perf } // namespace lo2s diff --git a/src/perf/tracepoint/writer.cpp b/src/perf/tracepoint/writer.cpp index 70c1ba34..e9b66786 100644 --- a/src/perf/tracepoint/writer.cpp +++ b/src/perf/tracepoint/writer.cpp @@ -14,9 +14,9 @@ namespace perf namespace tracepoint { -Writer::Writer(Cpu cpu, const std::string& name, trace::Trace& trace_, +Writer::Writer(Cpu cpu, const EventFormat& event, trace::Trace& trace_, const otf2::definition::metric_class& metric_class) -: Reader(cpu, name), +: Reader(cpu, event.id()), event_(event), writer_(trace_.create_metric_writer(fmt::format("tracepoint metrics for {}", cpu))), metric_instance_( trace_.metric_instance(metric_class, writer_.location(), trace_.system_tree_cpu_node(cpu))), @@ -30,7 +30,7 @@ bool Writer::handle(const Reader::RecordSampleType* sample) metric_event_.timestamp(time_converter_(sample->time)); std::size_t index = 0; - for (const auto& field : Reader::event_.fields()) + for (const auto& field : event_.fields()) { if (!field.is_integer()) { diff --git a/src/perf/util.cpp b/src/perf/util.cpp index 4ae7755e..024e2b00 100644 --- a/src/perf/util.cpp +++ b/src/perf/util.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/platform.cpp b/src/platform.cpp index 0774d081..e59af746 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -217,7 +217,7 @@ Processor detect_processor(void) return Processor::UNKNOWN; } -std::vector get_mem_events() +std::vector get_mem_events() { static auto proc = detect_processor(); switch (proc) diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp index 53979de9..a3f7ecba 100644 --- a/src/trace/trace.cpp +++ b/src/trace/trace.cpp @@ -579,20 +579,20 @@ Trace::metric_instance(const otf2::definition::metric_class& metric_class, return registry_.create(metric_class, recorder, scope); } -otf2::definition::metric_class& -Trace::tracepoint_metric_class(const perf::tracepoint::TracepointEvent& event) +otf2::definition::metric_class& Trace::tracepoint_metric_class(const std::string& event_name) { - if (!registry_.has(ByString(event.get_name()))) + if (!registry_.has(ByString(event_name))) { auto& mc = registry_.create( - ByString(event.get_name()), otf2::common::metric_occurence::async, + ByString(event_name), otf2::common::metric_occurence::async, otf2::common::recorder_kind::abstract); + perf::tracepoint::EventFormat event(event_name); for (const auto& field : event.fields()) { if (field.is_integer()) { - mc.add_member(metric_member(event.get_name() + "::" + field.name(), "?", + mc.add_member(metric_member(event_name + "::" + field.name(), "?", otf2::common::metric_mode::absolute_next, otf2::common::type::int64, "#")); } @@ -601,7 +601,7 @@ Trace::tracepoint_metric_class(const perf::tracepoint::TracepointEvent& event) } else { - return registry_.get(ByString(event.get_name())); + return registry_.get(ByString(event_name)); } }