diff --git a/CMakeLists.txt b/CMakeLists.txt index d2973a67..14775e88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,7 @@ set(SOURCE_FILES src/perf/event.cpp src/perf/bio/block_device.cpp - src/perf/counter/counter_provider.cpp + src/perf/event_config.cpp src/perf/counter/group/reader.cpp src/perf/counter/userspace/reader.cpp diff --git a/include/lo2s/config.hpp b/include/lo2s/config.hpp index 53fd6f36..00728b63 100644 --- a/include/lo2s/config.hpp +++ b/include/lo2s/config.hpp @@ -83,23 +83,26 @@ struct Config std::uint64_t metric_count; std::uint64_t metric_frequency; + std::string metric_leader; + std::vector group_counters; + std::vector userspace_counters; // time synchronization bool use_clockid; bool use_pebs; clockid_t clockid; // x86_energy - bool use_x86_energy; + bool use_x86_energy = false; // block I/O - bool use_block_io; + bool use_block_io = false; // syscalls bool use_syscalls = false; std::vector syscall_filter; // NEC SX-Aurora Tsubasa - bool use_nec; + bool use_nec = false; std::chrono::microseconds nec_read_interval; std::chrono::milliseconds nec_check_interval; // Nvidia CUPTI - bool use_nvidia; + bool use_nvidia = false; std::string cuda_injectionlib_path; uint64_t nvidia_ringbuf_size; }; diff --git a/include/lo2s/perf/bio/writer.hpp b/include/lo2s/perf/bio/writer.hpp index ff259b35..11387421 100644 --- a/include/lo2s/perf/bio/writer.hpp +++ b/include/lo2s/perf/bio/writer.hpp @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -157,12 +158,10 @@ class Writer std::vector get_tracepoints() { - bio_queue_ = - perf::EventProvider::instance().create_tracepoint_event("block:block_bio_queue"); - bio_issue_ = - perf::EventProvider::instance().create_tracepoint_event("block:block_rq_issue"); + bio_queue_ = perf::EventConfig::instance().create_tracepoint_event("block:block_bio_queue"); + bio_issue_ = perf::EventConfig::instance().create_tracepoint_event("block:block_rq_issue"); bio_complete_ = - perf::EventProvider::instance().create_tracepoint_event("block:block_rq_complete"); + perf::EventConfig::instance().create_tracepoint_event("block:block_rq_complete"); return { bio_queue_.value(), bio_issue_.value(), bio_complete_.value() }; } diff --git a/include/lo2s/perf/counter/counter_collection.hpp b/include/lo2s/perf/counter/counter_collection.hpp index f0269ed9..6a8a2503 100644 --- a/include/lo2s/perf/counter/counter_collection.hpp +++ b/include/lo2s/perf/counter/counter_collection.hpp @@ -34,14 +34,18 @@ namespace counter { struct CounterCollection { - std::optional leader_; + CounterCollection() : leader(std::nullopt) + { + } + + std::optional leader = std::nullopt; std::vector counters; double get_scale(int index) const { if (index == 0) { - return leader_.value().scale(); + return leader.value().scale(); } else { @@ -49,14 +53,9 @@ struct CounterCollection } } - Event& leader() const - { - return const_cast(leader_.value()); - } - friend bool operator==(const CounterCollection& lhs, const CounterCollection& rhs) { - if (lhs.leader_.value() == rhs.leader_.value()) + if (lhs.leader.value() == rhs.leader.value()) { return lhs.counters == rhs.counters; } @@ -65,11 +64,11 @@ struct CounterCollection friend bool operator<(const CounterCollection& lhs, const CounterCollection& rhs) { - if (lhs.leader_.value() == rhs.leader_.value()) + if (lhs.leader.value() == rhs.leader.value()) { return lhs.counters < rhs.counters; } - return lhs.leader_.value() < rhs.leader_.value(); + return lhs.leader.value() < rhs.leader.value(); } }; diff --git a/include/lo2s/perf/counter/counter_provider.hpp b/include/lo2s/perf/counter/counter_provider.hpp deleted file mode 100644 index 662bd84c..00000000 --- a/include/lo2s/perf/counter/counter_provider.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -#pragma once - -#include -#include -#include - -#include -#include - -namespace lo2s -{ -namespace perf -{ -namespace counter -{ -class CounterProvider -{ -public: - CounterProvider() - { - } - - static CounterProvider& instance() - { - static CounterProvider provider; - return provider; - } - - void initialize_group_counters(const std::string& leader, - const std::vector& counters); - void initialize_userspace_counters(const std::vector& counters); - void initialize_tracepoints(const std::vector& tracepoints); - - bool has_group_counters(ExecutionScope scope); - bool has_userspace_counters(ExecutionScope scope); - - CounterCollection collection_for(MeasurementScope scope); - std::vector get_tracepoint_event_names(); - -private: - std::optional group_leader_; - std::vector group_events_; - std::vector userspace_events_; - std::vector tracepoint_events_; -}; -} // namespace counter -} // namespace perf -} // namespace lo2s diff --git a/include/lo2s/perf/event.hpp b/include/lo2s/perf/event.hpp index 9ce1d866..b4771ccd 100644 --- a/include/lo2s/perf/event.hpp +++ b/include/lo2s/perf/event.hpp @@ -302,6 +302,7 @@ class SimpleEvent : public Event static SimpleEvent raw(std::string name); }; +#ifndef USE_HW_BREAKPOINT_COMPAT class BreakpointEvent : public Event { public: @@ -314,6 +315,7 @@ class BreakpointEvent : public Event attr_.bp_len = HW_BREAKPOINT_LEN_8; } }; +#endif /** * Contains an event parsed from sysfs diff --git a/include/lo2s/perf/event_config.hpp b/include/lo2s/perf/event_config.hpp new file mode 100644 index 00000000..2b410156 --- /dev/null +++ b/include/lo2s/perf/event_config.hpp @@ -0,0 +1,70 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2016, + * 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 + +namespace lo2s +{ +namespace perf +{ +class EventConfig +{ + +public: + EventConfig(); + + static EventConfig& instance() + { + static EventConfig e; + return e; + } + + counter::CounterCollection counters_for(MeasurementScope scope); + + Event create_time_event(uint64_t local_time); + Event create_sampling_event(); + perf::tracepoint::TracepointEvent create_tracepoint_event(std::string name); + std::vector get_tracepoints(); + +private: + // When we poll on the fd given by perf_event_open, wakeup, when our buffer is 80% full + // Default behaviour is to wakeup on every event, which is horrible performance wise + void watermark(Event& ev) + { + ev.set_watermark(0.8 * config().mmap_pages * sysconf(_SC_PAGESIZE)); + } + + void read_userspace_counters(); + void read_group_counters(); + + std::optional sampling_event_; + std::optional group_counters_; + std::optional userspace_counters_; + std::optional> tracepoint_events_; + bool exclude_kernel_; +}; +} // namespace perf +} // namespace lo2s diff --git a/include/lo2s/perf/event_provider.hpp b/include/lo2s/perf/event_provider.hpp index 5c2a0c57..db50ee05 100644 --- a/include/lo2s/perf/event_provider.hpp +++ b/include/lo2s/perf/event_provider.hpp @@ -36,33 +36,16 @@ namespace perf class EventProvider { public: - EventProvider(); - EventProvider(const EventProvider&) = delete; - void operator=(const EventProvider&) = delete; - - static const EventProvider& instance() - { - return instance_mutable(); - } - - static Event get_event_by_name(const std::string& name); + Event get_event_by_name(const std::string& name); - static bool has_event(const std::string& name); + bool has_event(const std::string& name); - static std::vector get_predefined_events(); - static std::vector get_pmu_events(); + std::vector get_predefined_events(); + std::vector get_pmu_events(); - static Event fallback_metric_leader_event(); - - static Event create_time_event(std::uint64_t local_time, bool enable_on_exec = false); - static Event create_event(const std::string& name, perf_type_id type, std::uint64_t config, - std::uint64_t config1 = 0); - static SysfsEvent create_sampling_event(bool enable_on_exec); - static SysfsEvent create_sysfs_event(const std::string& name, bool use_config = true); - static tracepoint::TracepointEvent create_tracepoint_event(const std::string& name, - bool use_config = true, - bool enable_on_exec = false); + Event get_metric_leader(std::string metric_leader); + std::vector get_tracepoint_event_names(); class InvalidEvent : public std::runtime_error { public: @@ -72,15 +55,17 @@ class EventProvider } }; -private: - static EventProvider& instance_mutable() + static EventProvider& instance() { static EventProvider e; return e; } - static void apply_config_attrs(Event& event); - static void apply_default_attrs(Event& event); +private: + Event fallback_metric_leader_event(); + EventProvider(); + EventProvider(const EventProvider&) = delete; + void operator=(const EventProvider&) = delete; Event cache_event(const std::string& name); diff --git a/include/lo2s/perf/io_reader.hpp b/include/lo2s/perf/io_reader.hpp index 9032a0ea..10faf18c 100644 --- a/include/lo2s/perf/io_reader.hpp +++ b/include/lo2s/perf/io_reader.hpp @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include #include diff --git a/include/lo2s/perf/sample/reader.hpp b/include/lo2s/perf/sample/reader.hpp index 2b818226..95c51c39 100644 --- a/include/lo2s/perf/sample/reader.hpp +++ b/include/lo2s/perf/sample/reader.hpp @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -84,40 +85,18 @@ class Reader : public EventReader Log::debug() << "initializing event_reader for:" << scope.name() << ", enable_on_exec: " << enable_on_exec; - Event event = EventProvider::instance().create_sampling_event(enable_on_exec); - - do + Event event = EventConfig::instance().create_sampling_event(); + if (enable_on_exec) { - try - { - event_ = event.open(scope, config().cgroup_fd); - } - catch (const std::system_error& e) - { - if (e.code().value() == EACCES && !event.attr().exclude_kernel && - perf_event_paranoid() > 1) - { - event.mut_attr().exclude_kernel = 1; - perf_warn_paranoid(); - continue; - } - - if (!event.degrade_precision()) - { - Log::error() << "perf_event_open for sampling failed: " << e.what(); - - if (event.attr().use_clockid) - { - Log::error() << "maybe the specified clock is unavailable?"; - } - throw_errno(); - } - } - } while (!event_.value().is_valid()); + event.set_flags({ EventFlag::ENABLE_ON_EXEC }); + } + else + { + event.set_flags({ EventFlag::DISABLED }); + } - Log::debug() << "Using precise_ip level: " << event.attr().precise_ip; + event_ = event.open(scope, config().cgroup_fd); - // Exception safe, so much wow! try { init_mmap(event_.value().get_fd()); diff --git a/include/lo2s/perf/syscall/reader.hpp b/include/lo2s/perf/syscall/reader.hpp index 5a51d15b..4a6a3e1a 100644 --- a/include/lo2s/perf/syscall/reader.hpp +++ b/include/lo2s/perf/syscall/reader.hpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -70,10 +71,12 @@ class Reader : public EventReader Reader(Cpu cpu) : cpu_(cpu) { tracepoint::TracepointEvent enter_event = - EventProvider::instance().create_tracepoint_event("raw_syscalls:sys_enter"); + EventConfig::instance().create_tracepoint_event("raw_syscalls:sys_enter"); tracepoint::TracepointEvent exit_event = - EventProvider::instance().create_tracepoint_event("raw_syscalls:sys_exit"); + EventConfig::instance().create_tracepoint_event("raw_syscalls:sys_exit"); + enter_event.set_sample_type(PERF_SAMPLE_IDENTIFIER); + exit_event.set_sample_type(PERF_SAMPLE_IDENTIFIER); try { enter_ev_ = enter_event.open(cpu_, config().cgroup_fd); diff --git a/include/lo2s/perf/tracepoint/reader.hpp b/include/lo2s/perf/tracepoint/reader.hpp index 0dc2a475..5427c85a 100644 --- a/include/lo2s/perf/tracepoint/reader.hpp +++ b/include/lo2s/perf/tracepoint/reader.hpp @@ -23,6 +23,7 @@ #include +#include #include #include #include diff --git a/include/lo2s/trace/trace.hpp b/include/lo2s/trace/trace.hpp index 2a4f7cc9..a93ebd0c 100644 --- a/include/lo2s/trace/trace.hpp +++ b/include/lo2s/trace/trace.hpp @@ -19,7 +19,6 @@ * along with lo2s. If not, see . */ #pragma once -#include "otf2xx/definition/calling_context.hpp" #include #include #include @@ -28,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -233,7 +232,7 @@ class Trace } const perf::counter::CounterCollection& counter_collection = - perf::counter::CounterProvider::instance().collection_for(scope); + perf::EventConfig::instance().counters_for(scope); if (registry_.has(ByCounterCollection(counter_collection))) { @@ -247,7 +246,7 @@ class Trace if (scope.type == MeasurementScopeType::GROUP_METRIC) { - metric_class.add_member(get_event_metric_member(counter_collection.leader())); + metric_class.add_member(get_event_metric_member(counter_collection.leader.value())); } for (const auto& counter : counter_collection.counters) diff --git a/src/config.cpp b/src/config.cpp index ced941c0..983a8a87 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #ifdef HAVE_LIBPFM #include @@ -414,7 +413,7 @@ void parse_program_options(int argc, const char** argv) config.use_x86_energy = arguments.given("x86-energy"); config.use_sensors = arguments.given("sensors"); config.use_block_io = arguments.given("block-io"); - + config.tracepoint_events = arguments.get_all("tracepoint"); #ifdef HAVE_CUDA config.cuda_injectionlib_path = arguments.get("nvidia-injection-path"); #endif @@ -514,10 +513,10 @@ void parse_program_options(int argc, const char** argv) if (arguments.given("list-events")) { print_availability(std::cout, "predefined events", - perf::EventProvider::get_predefined_events()); - + perf::EventProvider::instance().get_predefined_events()); // TODO: find a better solution ? - std::vector sys_events = perf::EventProvider::get_pmu_events(); + std::vector sys_events = + perf::EventProvider::instance().get_pmu_events(); std::vector events(sys_events.begin(), sys_events.end()); print_availability(std::cout, "Kernel PMU events", events); @@ -535,7 +534,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(); + perf::EventProvider::instance().get_tracepoint_event_names(); if (tracepoints.empty()) { @@ -694,7 +693,7 @@ void parse_program_options(int argc, const char** argv) perf::perf_check_disabled(); } - if (config.sampling && !perf::EventProvider::has_event(config.sampling_event)) + if (config.sampling && !perf::EventProvider::instance().has_event(config.sampling_event)) { lo2s::Log::fatal() << "requested sampling event \'" << config.sampling_event << "\' is not available!"; @@ -822,12 +821,9 @@ void parse_program_options(int argc, const char** argv) perf_group_events.emplace_back("cpu-cycles"); } - perf::counter::CounterProvider::instance().initialize_tracepoints( - arguments.get_all("tracepoint")); - perf::counter::CounterProvider::instance().initialize_group_counters( - arguments.get("metric-leader"), perf_group_events); - perf::counter::CounterProvider::instance().initialize_userspace_counters(perf_userspace_events); - + config.metric_leader = arguments.get("metric-leader"); + config.group_counters = perf_group_events; + config.userspace_counters = perf_userspace_events; config.exclude_kernel = !static_cast(arguments.given("kernel")); if (arguments.count("x86-adapt-knob")) diff --git a/src/monitor/cpu_set_monitor.cpp b/src/monitor/cpu_set_monitor.cpp index 44fb8586..cc2d8eb9 100644 --- a/src/monitor/cpu_set_monitor.cpp +++ b/src/monitor/cpu_set_monitor.cpp @@ -27,7 +27,6 @@ #include #include -#include #include diff --git a/src/monitor/main_monitor.cpp b/src/monitor/main_monitor.cpp index e2a168fe..8b5313bd 100644 --- a/src/monitor/main_monitor.cpp +++ b/src/monitor/main_monitor.cpp @@ -33,6 +33,7 @@ namespace lo2s { namespace monitor { + MainMonitor::MainMonitor() : trace_(), metrics_(trace_) { if (config().sampling) diff --git a/src/monitor/process_monitor.cpp b/src/monitor/process_monitor.cpp index f4d66047..d019e16d 100644 --- a/src/monitor/process_monitor.cpp +++ b/src/monitor/process_monitor.cpp @@ -21,7 +21,6 @@ #include #include -#include #include namespace lo2s @@ -51,15 +50,20 @@ void ProcessMonitor::insert_thread(Process process, Thread thread, std::string n process_infos_.try_emplace(process, process, spawn); } + ExecutionScope scope = ExecutionScope(thread); if (config().sampling || - perf::counter::CounterProvider::instance().has_group_counters(ExecutionScope(thread)) || - perf::counter::CounterProvider::instance().has_userspace_counters(ExecutionScope(thread))) + !perf::EventConfig::instance() + .counters_for(MeasurementScope::group_metric(scope)) + .counters.empty() || + !perf::EventConfig::instance() + .counters_for(MeasurementScope::userspace_metric(scope)) + .counters.empty()) { try { - auto inserted = threads_.emplace( - std::piecewise_construct, std::forward_as_tuple(thread), - std::forward_as_tuple(ExecutionScope(thread), *this, spawn, is_process)); + auto inserted = + threads_.emplace(std::piecewise_construct, std::forward_as_tuple(thread), + std::forward_as_tuple(scope, *this, spawn, is_process)); assert(inserted.second); // actually start thread inserted.first->second.start(); diff --git a/src/monitor/scope_monitor.cpp b/src/monitor/scope_monitor.cpp index af9f6af9..12ee754c 100644 --- a/src/monitor/scope_monitor.cpp +++ b/src/monitor/scope_monitor.cpp @@ -56,14 +56,18 @@ ScopeMonitor::ScopeMonitor(ExecutionScope scope, MainMonitor& parent, bool enabl add_fd(syscall_writer_->fd()); } - if (perf::counter::CounterProvider::instance().has_group_counters(scope)) + if (!perf::EventConfig::instance() + .counters_for(MeasurementScope::group_metric(scope)) + .counters.empty()) { group_counter_writer_ = std::make_unique(scope, parent.trace(), enable_on_exec); add_fd(group_counter_writer_->fd()); } - if (perf::counter::CounterProvider::instance().has_userspace_counters(scope)) + if (!perf::EventConfig::instance() + .counters_for(MeasurementScope::userspace_metric(scope)) + .counters.empty()) { userspace_counter_writer_ = std::make_unique(scope, parent.trace()); diff --git a/src/monitor/tracepoint_monitor.cpp b/src/monitor/tracepoint_monitor.cpp index 35bbc37d..2bae4921 100644 --- a/src/monitor/tracepoint_monitor.cpp +++ b/src/monitor/tracepoint_monitor.cpp @@ -36,15 +36,14 @@ namespace monitor TracepointMonitor::TracepointMonitor(trace::Trace& trace, Cpu cpu) : monitor::PollMonitor(trace, "", config().perf_read_interval), cpu_(cpu) { - perf::counter::CounterCollection tracepoint_collection = - perf::counter::CounterProvider::instance().collection_for( - MeasurementScope::tracepoint(cpu_.as_scope())); + std::vector tracepoint_events = + perf::EventConfig::instance().get_tracepoints(); - for (const auto& event : tracepoint_collection.counters) + for (const auto& event : tracepoint_events) { - auto& mc = trace.tracepoint_metric_class(event.name()); + auto& mc = trace.tracepoint_metric_class(event); 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 deleted file mode 100644 index eb6922c5..00000000 --- a/src/perf/counter/counter_provider.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * 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 -#include - -#include - -namespace lo2s -{ -namespace perf -{ -namespace counter -{ -void CounterProvider::initialize_tracepoints(const std::vector& tracepoints) -{ - assert(tracepoint_events_.empty()); - - for (const auto& ev_name : tracepoints) - { - try - { - tracepoint_events_.emplace_back( - EventProvider::instance().create_tracepoint_event(ev_name, false)); - } - catch (const perf::EventProvider::InvalidEvent& e) - { - Log::warn() << "'" << ev_name - << "' does not name a known event, ignoring! (reason: " << e.what() << ")"; - } - } -} - -void CounterProvider::initialize_userspace_counters(const std::vector& counters) -{ - assert(userspace_events_.empty()); - - for (const auto& ev : counters) - { - try - { - userspace_events_.emplace_back(perf::EventProvider::get_event_by_name(ev)); - userspace_events_.back().sample_period(0); - } - catch (const perf::EventProvider::InvalidEvent& e) - { - Log::warn() << "'" << ev - << "' does not name a known event, ignoring! (reason: " << e.what() << ")"; - } - } -} - -void CounterProvider::initialize_group_counters(const std::string& leader, - const std::vector& counters) -{ - assert(group_events_.empty()); - - if (leader == "") - { - Log::info() << "choosing default metric-leader"; - - try - { - group_leader_ = EventProvider::get_event_by_name("cpu-clock"); - } - catch (const EventProvider::InvalidEvent& e) - { - Log::warn() << "cpu-clock isn't available, trying to use a fallback event"; - try - { - group_leader_ = EventProvider::fallback_metric_leader_event(); - } - catch (const perf::EventProvider::InvalidEvent& e) - { - Log::error() << "Failed to determine a suitable metric leader event"; - Log::error() << "Try manually specifying one with --metric-leader."; - - throw perf::EventProvider::InvalidEvent(leader); - } - } - } - else - { - try - { - group_leader_ = perf::EventProvider::get_event_by_name(leader); - } - catch (const perf::EventProvider::InvalidEvent& e) - { - Log::error() << "Metric leader " << leader << " not available."; - Log::error() << "Please choose another metric leader."; - - throw perf::EventProvider::InvalidEvent(leader); - } - } - - // DONT do group_leader_.sample_freq() here, since it requires config() to be complete - - for (const auto& ev : counters) - { - try - { - // skip event if it has already been declared as group leader - if (ev == group_leader_.value().name()) - { - Log::info() << "'" << ev - << "' has been requested as both the metric leader event and a regular " - "metric event. Will treat it as the leader."; - continue; - } - - group_events_.emplace_back(perf::EventProvider::get_event_by_name(ev)); - group_events_.back().sample_period(0); - } - catch (const perf::EventProvider::InvalidEvent& e) - { - Log::warn() << "'" << ev - << "' does not name a known event, ignoring! (reason: " << e.what() << ")"; - } - } -} - -CounterCollection CounterProvider::collection_for(MeasurementScope scope) -{ - assert(scope.type == MeasurementScopeType::GROUP_METRIC || - scope.type == MeasurementScopeType::USERSPACE_METRIC || - scope.type == MeasurementScopeType::TRACEPOINT); - - CounterCollection res; - if (scope.type == MeasurementScopeType::GROUP_METRIC) - { - if (group_leader_.value().is_available_in(scope.scope)) - { - res.leader() = group_leader_.value(); - for (auto& ev : group_events_) - { - if (ev.is_available_in(scope.scope)) - { - res.counters.emplace_back(std::move(ev)); - } - } - } - } - else if (scope.type == MeasurementScopeType::USERSPACE_METRIC) - { - for (auto& ev : userspace_events_) - { - if (ev.is_available_in(scope.scope)) - { - res.counters.emplace_back(std::move(ev)); - } - } - } - else - { - for (auto& ev : tracepoint_events_) - { - if (ev.is_available_in(scope.scope)) - { - res.counters.emplace_back(std::move(ev)); - } - } - } - return res; -} - -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()) - { - return !group_events_.empty(); - } - else - { - return group_leader_.value().is_available_in(scope) && - std::any_of(group_events_.begin(), group_events_.end(), - [scope](const auto& ev) { return ev.is_available_in(scope); }); - } - return false; -} - -bool CounterProvider::has_userspace_counters(ExecutionScope scope) -{ - if (scope.is_process()) - { - return !userspace_events_.empty(); - } - else - { - return std::any_of(userspace_events_.begin(), userspace_events_.end(), - [scope](const auto& ev) { return ev.is_available_in(scope); }); - } - - return false; -} - -} // namespace counter -} // namespace perf -} // namespace lo2s diff --git a/src/perf/counter/group/reader.cpp b/src/perf/counter/group/reader.cpp index 39d0dd45..159d1043 100644 --- a/src/perf/counter/group/reader.cpp +++ b/src/perf/counter/group/reader.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -49,85 +50,30 @@ namespace group template Reader::Reader(ExecutionScope scope, bool enable_on_exec) -: counter_collection_( - CounterProvider::instance().collection_for(MeasurementScope::group_metric(scope))), +: counter_collection_(EventConfig::instance().counters_for(MeasurementScope::group_metric(scope))), counter_buffer_(counter_collection_.counters.size() + 1) { - if (config().metric_use_frequency) + Log::debug() << "counter::Reader: leader event: '" << counter_collection_.leader->name() << "'"; + + if (enable_on_exec) { - counter_collection_.leader().sample_freq(config().metric_frequency); + counter_collection_.leader->set_flags({ EventFlag::ENABLE_ON_EXEC }); } else { - counter_collection_.leader().sample_period(config().metric_count); + counter_collection_.leader->set_flags({ EventFlag::DISABLED }); } - do - { - try - { - counter_leader_ = - counter_collection_.leader().open_as_group_leader(scope, config().cgroup_fd); - } - catch (const std::system_error& e) - { - // perf_try_event_open was used here before - if (counter_leader_.value().get_fd() < 0 && errno == EACCES && - !counter_collection_.leader().attr().exclude_kernel && perf_event_paranoid() > 1) - { - counter_collection_.leader().mut_attr().exclude_kernel = 1; - perf_warn_paranoid(); - - continue; - } - - if (!counter_leader_.value().is_valid()) - { - Log::error() << "perf_event_open for counter group leader failed"; - throw_errno(); - } - } - } while (!counter_leader_.value().is_valid()); - - Log::debug() << "counter::Reader: leader event: '" << counter_collection_.leader().name() - << "'"; + counter_leader_ = counter_collection_.leader->open(scope, config().cgroup_fd); for (auto& counter_ev : counter_collection_.counters) { - if (counter_ev.is_available_in(scope)) - { - std::optional counter = std::nullopt; - counter_ev.mut_attr().exclude_kernel = - counter_collection_.leader().attr().exclude_kernel; - - try - { - counter.value() = counter_leader_.value().open_child(counter_ev, scope); - counters_.emplace_back(std::move(counter.value())); - } - catch (const std::system_error& e) - { - if (!counter.value().is_valid()) - { - Log::error() << "failed to add counter '" << counter_ev.name() - << "': " << e.code().message(); - - if (e.code().value() == EINVAL) - { - Log::error() << "opening " << counter_collection_.counters.size() - << " counters at once might exceed the hardware limit of " - "simultaneously " - "openable counters."; - } - throw e; - } - } - } + counters_.emplace_back(counter_leader_->open_child(counter_ev, scope)); } if (!enable_on_exec) { - counter_leader_.value().enable(); + counter_leader_->enable(); } EventReader::init_mmap(counter_leader_.value().get_fd()); diff --git a/src/perf/counter/userspace/reader.cpp b/src/perf/counter/userspace/reader.cpp index c0a30141..8205b88f 100644 --- a/src/perf/counter/userspace/reader.cpp +++ b/src/perf/counter/userspace/reader.cpp @@ -46,42 +46,15 @@ namespace userspace template Reader::Reader(ExecutionScope scope) : counter_collection_( - CounterProvider::instance().collection_for(MeasurementScope::userspace_metric(scope))), + EventConfig::instance().counters_for(MeasurementScope::userspace_metric(scope))), counter_buffer_(counter_collection_.counters.size()), timer_fd_(timerfd_from_ns(config().userspace_read_interval)), data_(counter_collection_.counters.size()) { for (auto& event : counter_collection_.counters) { - std::optional counter = std::nullopt; - - try - { - counter.value() = event.open(scope); - counters_.emplace_back(std::move(counter.value())); - } - catch (const std::system_error& e) - { - // perf_try_event_open was used here before - if (counter.value().get_fd() < 0 && errno == EACCES && !event.attr().exclude_kernel && - perf_event_paranoid() > 1) - { - event.mut_attr().exclude_kernel = 1; - perf_warn_paranoid(); - - counter = event.open(scope); - } - - if (!counter.value().is_valid()) - { - Log::error() << "perf_event_open for counter failed"; - throw_errno(); - } - else - { - counters_.emplace_back(std::move(counter.value())); - } - } + std::optional counter = event.open(scope); + counters_.emplace_back(std::move(counter.value())); } } diff --git a/src/perf/event.cpp b/src/perf/event.cpp index d2450b5f..d6328669 100644 --- a/src/perf/event.cpp +++ b/src/perf/event.cpp @@ -33,7 +33,6 @@ extern "C" { #include -#include #include } @@ -149,7 +148,7 @@ SimpleEvent::SimpleEvent(const std::string name, perf_type_id type, std::uint64_ cpus_ = test_cpus(*this); - update_availability(); + event_is_openable(); } void SysfsEvent::parse_pmu_path(const std::string& ev_name) diff --git a/src/perf/event_config.cpp b/src/perf/event_config.cpp new file mode 100644 index 00000000..d2297786 --- /dev/null +++ b/src/perf/event_config.cpp @@ -0,0 +1,310 @@ +/* + * This file is part of the lo2s software. + * Linux OTF2 sampling + * + * Copyright (c) 2016, + * 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 + +namespace lo2s +{ +namespace perf +{ + +EventConfig::EventConfig() +{ + auto test_event = EventProvider::instance().get_event_by_name("cpu-cycles"); + + std::optional guard; + do + { + try + { + guard = test_event.open(Thread(0)); + } + catch (std::system_error& e) + { + if (test_event.get_flag(EventFlag::EXCLUDE_KERNEL) && e.code().value() == EACCES && + perf_event_paranoid() > 1) + { + perf_warn_paranoid(); + exclude_kernel_ = 1; + test_event.set_flags({ EventFlag::EXCLUDE_KERNEL }); + } + else + { + throw; + } + } + } while (!guard.has_value()); +} + +Event EventConfig::create_sampling_event() +{ + if (sampling_event_.has_value()) + { + return sampling_event_.value(); + } + + if (config().sampling) + { + sampling_event_ = EventProvider::instance().get_event_by_name(config().sampling_event); + Log::debug() << "using sampling event \'" << sampling_event_->name() + << "\', period: " << config().sampling_period; + + sampling_event_->sample_period(config().sampling_period); + + if (config().use_pebs) + { + sampling_event_->set_clockid(-1); + } + else + { + sampling_event_->set_clockid(config().clockid); + } + sampling_event_->set_flags({ EventFlag::MMAP }); + + sampling_event_->set_precise_ip(3); + } + else + { + Event event = EventProvider::instance().get_event_by_name("dummy"); + } + + sampling_event_->set_flags( + { EventFlag::SAMPLE_ID_ALL, EventFlag::COMM, EventFlag::CONTEXT_SWITCH }); + + if (exclude_kernel_) + { + sampling_event_->set_flags({ EventFlag::EXCLUDE_KERNEL }); + } + watermark(sampling_event_.value()); + + // TODO see if we can remove remove tid + sampling_event_->set_sample_type(PERF_SAMPLE_TIME | PERF_SAMPLE_IP | PERF_SAMPLE_TID | + PERF_SAMPLE_CPU); + + if (config().enable_cct) + { + sampling_event_->set_sample_type(PERF_SAMPLE_CALLCHAIN); + } + + if (config().sampling) + { + uint64_t precise_ip = 3; + do + { + + try + { + auto guard = sampling_event_->open(Thread(0)); + return sampling_event_.value(); + } + catch (...) + { + if (precise_ip == 0) + { + throw; + } + sampling_event_->set_precise_ip(--precise_ip); + } + + } while (true); + } + + return sampling_event_.value(); +} + +Event EventConfig::create_time_event(uint64_t local_time [[maybe_unused]]) + +{ +#ifndef USE_HW_BREAKPOINT_COMPAT + BreakpointEvent ev(local_time, HW_BREAKPOINT_W); + ev.sample_period(1); + ev.set_watermark(1); + ev.set_clockid(config().clockid); + ev.set_sample_type(PERF_SAMPLE_TIME); +#else + Event ev = EventProvider::instance().get_event_by_name("instructions"); + ev.sample_period(100000000); + ev.set_flags({ EventFlag::TASK }); +#endif + + ev.set_flags({ EventFlag::EXCLUDE_KERNEL, EventFlag::DISABLED }); + + return ev; +} + +std::vector EventConfig::get_tracepoints() +{ + if (tracepoint_events_.has_value()) + { + return tracepoint_events_.value(); + } + + tracepoint_events_ = std::vector(); + for (auto& ev_name : config().tracepoint_events) + { + tracepoint_events_->emplace_back(create_tracepoint_event(ev_name)); + } + return tracepoint_events_.value(); +} + +perf::tracepoint::TracepointEvent EventConfig::create_tracepoint_event(std::string name) +{ + auto ev = tracepoint::TracepointEvent(name); + watermark(ev); + ev.set_clockid(config().clockid); + ev.sample_period(1); + ev.set_sample_type(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME); + ev.set_flags({ EventFlag::DISABLED }); + return ev; +} + +void EventConfig::read_userspace_counters() +{ + if (userspace_counters_.has_value()) + { + return; + } + + counter::CounterCollection res; + for (const auto& ev : config().userspace_counters) + { + try + { + res.counters.emplace_back(perf::EventProvider::instance().get_event_by_name(ev)); + } + catch (const perf::EventProvider::InvalidEvent& e) + { + Log::warn() << "'" << ev + << "' does not name a known event, ignoring! (reason: " << e.what() << ")"; + } + } +} + +void EventConfig::read_group_counters() +{ + + if (group_counters_.has_value()) + { + return; + } + + counter::CounterCollection res; + res.leader = EventProvider::instance().get_metric_leader(config().metric_leader); + + res.leader->set_sample_type(PERF_SAMPLE_TIME | PERF_SAMPLE_READ); + if (config().metric_use_frequency) + { + res.leader->sample_freq(config().metric_frequency); + } + else + { + res.leader->sample_period(config().metric_count); + } + res.leader->set_clockid(config().clockid); + + res.leader->set_read_format(PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | + PERF_FORMAT_GROUP); + + watermark(res.leader.value()); + + // DONT do group_leader_.sample_freq() here, since it requires config() to be complete + + for (const auto& ev_name : config().group_counters) + { + try + { + // skip event if it has already been declared as group leader + if (ev_name == res.leader->name()) + { + Log::info() << "'" << ev_name + << "' has been requested as both the metric leader event and a regular " + "metric event. Will treat it as the leader."; + continue; + } + + Event ev = perf::EventProvider::instance().get_event_by_name(ev_name); + if (ev.is_valid()) + { + res.counters.emplace_back(ev); + res.counters.back().set_clockid(config().clockid); + res.counters.back().set_read_format(PERF_FORMAT_TOTAL_TIME_ENABLED | + PERF_FORMAT_TOTAL_TIME_RUNNING); + } + } + catch (const perf::EventProvider::InvalidEvent& e) + { + Log::warn() << "'" << ev_name + << "' does not name a known event, ignoring! (reason: " << e.what() << ")"; + } + } + + group_counters_ = res; +} + +counter::CounterCollection EventConfig::counters_for(MeasurementScope scope) +{ + assert(scope.type == MeasurementScopeType::GROUP_METRIC || + scope.type == MeasurementScopeType::USERSPACE_METRIC); + + counter::CounterCollection res; + if (scope.type == MeasurementScopeType::GROUP_METRIC) + { + read_group_counters(); + if (group_counters_->leader->is_available_in(scope.scope)) + { + res.leader = group_counters_->leader.value(); + for (auto& ev : group_counters_->counters) + { + if (ev.is_available_in(scope.scope)) + { + + res.counters.emplace_back(ev); + } + else + { + Log::warn() << "Scope " << scope.scope.name() << ": skipping " << ev.name() + << ": not available!"; + } + } + } + } + else if (scope.type == MeasurementScopeType::USERSPACE_METRIC) + { + read_userspace_counters(); + for (auto& ev : userspace_counters_->counters) + { + if (ev.is_available_in(scope.scope)) + { + res.counters.emplace_back(ev); + } + else + { + Log::warn() << "Skipping " << ev.name() << " not availabe in " << scope.scope.name() + << "!"; + } + } + } + + return res; +} + +} // namespace perf +} // namespace lo2s diff --git a/src/perf/event_provider.cpp b/src/perf/event_provider.cpp index 5f2befaa..15ede9a1 100644 --- a/src/perf/event_provider.cpp +++ b/src/perf/event_provider.cpp @@ -51,17 +51,9 @@ extern "C" namespace { -#define PERF_EVENT(name, type, id) \ - { \ - (name), (type), (id) \ - } -#define PERF_EVENT_HW(name, id) PERF_EVENT(name, PERF_TYPE_HARDWARE, PERF_COUNT_HW_##id) +#define PERF_EVENT(name, type, id) { (name), (type), (id) } #define PERF_EVENT_SW(name, id) PERF_EVENT(name, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##id) -#define PERF_MAKE_CACHE_ID(id) (id) -#define PERF_MAKE_CACHE_OP_ID(id) ((id) << 8) -#define PERF_MAKE_CACHE_OP_RES_ID(id) ((id) << 16) - template struct string_to_id { @@ -111,83 +103,6 @@ namespace lo2s namespace perf { -static void populate_event_map(std::unordered_map& map) -{ - Log::info() << "checking available events..."; - lo2s::perf::Event HW_EVENT_TABLE[] = { - PERF_EVENT_HW("cpu-cycles", CPU_CYCLES), - PERF_EVENT_HW("instructions", INSTRUCTIONS), - PERF_EVENT_HW("cache-references", CACHE_REFERENCES), - PERF_EVENT_HW("cache-misses", CACHE_MISSES), - PERF_EVENT_HW("branch-instructions", BRANCH_INSTRUCTIONS), - PERF_EVENT_HW("branch-misses", BRANCH_MISSES), - PERF_EVENT_HW("bus-cycles", BUS_CYCLES), -#ifdef HAVE_PERF_EVENT_STALLED_CYCLES_FRONTEND - PERF_EVENT_HW("stalled-cycles-frontend", STALLED_CYCLES_FRONTEND), -#endif -#ifdef HAVE_PERF_EVENT_STALLED_CYCLES_BACKEND - PERF_EVENT_HW("stalled-cycles-backend", STALLED_CYCLES_BACKEND), -#endif -#ifdef HAVE_PERF_EVENT_REF_CYCLES - PERF_EVENT_HW("ref-cycles", REF_CPU_CYCLES), -#endif - }; - lo2s::perf::Event SW_EVENT_TABLE[] = { - PERF_EVENT_SW("cpu-clock", CPU_CLOCK), - PERF_EVENT_SW("task-clock", TASK_CLOCK), - PERF_EVENT_SW("page-faults", PAGE_FAULTS), - PERF_EVENT_SW("context-switches", CONTEXT_SWITCHES), - PERF_EVENT_SW("cpu-migrations", CPU_MIGRATIONS), - PERF_EVENT_SW("minor-faults", PAGE_FAULTS_MIN), - PERF_EVENT_SW("major-faults", PAGE_FAULTS_MAJ), -#ifdef HAVE_PERF_EVENT_ALIGNMENT_FAULTS - PERF_EVENT_SW("alignment-faults", ALIGNMENT_FAULTS), -#endif -#ifdef HAVE_PERF_EVENT_EMULATION_FAULTS - PERF_EVENT_SW("emulation-faults", EMULATION_FAULTS), -#endif -#ifdef HAVE_PERF_EVENT_DUMMY - PERF_EVENT_SW("dummy", DUMMY), -#endif -#ifdef HAVE_PERF_EVENT_BPF_OUTPUT - PERF_EVENT_SW("bpf-output", BPF_OUTPUT), -#endif -#ifdef HAVE_PERF_EVENT_CGROUP_SWITCHES - PERF_EVENT_SW("cgroup-switches", CGROUP_SWITCHES), -#endif - }; - - 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) - { - Event event(ev); - map.emplace(event.name(), event); - } - - for (auto& ev : SW_EVENT_TABLE) - { - Event event(ev); - map.emplace(event.name(), event); - } - - std::stringstream name_fmt; - for (auto& cache : CACHE_NAME_TABLE) - { - for (auto& operation : CACHE_OPERATION_TABLE) - { - name_fmt.str(std::string()); - name_fmt << cache.name << '-' << operation.name; - - // don't use EventProvider::instance() here, will cause recursive init - map.emplace(name_fmt.str(), - EventProvider::create_event(name_fmt.str(), PERF_TYPE_HW_CACHE, - make_cache_config(cache.id, operation.id.op_id, - operation.id.result_id))); - } - } -} - std::vector EventProvider::get_pmu_events() { std::vector events; @@ -224,7 +139,7 @@ std::vector EventProvider::get_pmu_events() << '/'; try { - SysfsEvent event = EventProvider::instance().create_sysfs_event(event_name.str()); + SysfsEvent event(event_name.str()); events.emplace_back(event); } catch (const EventProvider::InvalidEvent& e) @@ -237,6 +152,32 @@ std::vector EventProvider::get_pmu_events() return events; } +std::vector EventProvider::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 {}; + } +} + Event EventProvider::fallback_metric_leader_event() { Log::debug() << "checking for metric leader event..."; @@ -261,21 +202,82 @@ Event EventProvider::fallback_metric_leader_event() throw InvalidEvent{ "no suitable metric leader event found" }; } -/** - * takes the name of an event, checks if it can be opened with each cpu and returns a PerfEvent - * with a set of working cpus - */ -const Event raw_read_event(const std::string& ev_name) -{ - // Do not check whether the event_is_openable because we don't know whether we are in - // system or process mode - return EventProvider::instance().create_event(ev_name, PERF_TYPE_RAW, - std::stoull(ev_name.substr(1), nullptr, 16), 0); -} - EventProvider::EventProvider() { - populate_event_map(event_map_); + event_map_.emplace("cpu-cycles", perf::SimpleEvent("cpu-cycles", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_CPU_CYCLES)); + event_map_.emplace("instructions", perf::SimpleEvent("instructions", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_INSTRUCTIONS)); + event_map_.emplace("cache-references", perf::SimpleEvent("cache-references", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_CACHE_REFERENCES)); + event_map_.emplace("cache-misses", perf::SimpleEvent("cache-misses", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_CACHE_MISSES)); + event_map_.emplace("branch-instructions", + perf::SimpleEvent("branch-instructions", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS)); + event_map_.emplace("branch-misses", perf::SimpleEvent("branch-misses", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_BRANCH_MISSES)); + event_map_.emplace("bus-cycles", perf::SimpleEvent("bus-cycles", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_BUS_CYCLES)); +#ifdef HAVE_PERF_EVENT_STALLED_CYCLES_FRONTEND + event_map_.emplace("stalled-cycles-frontend", + perf::SimpleEvent("stalled-cycles-frontend", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND)); +#endif +#ifdef HAVE_PERF_EVENT_STALLED_CYCLES_BACKEND + event_map_.emplace("stalled-cycles-backend", + perf::SimpleEvent("stalled-cycles-backend", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND)); +#endif +#ifdef HAVE_PERF_EVENT_REF_CYCLES + event_map_.emplace("ref-cycles", perf::SimpleEvent("ref-cycles", PERF_TYPE_HARDWARE, + PERF_COUNT_HW_REF_CPU_CYCLES)); +#endif + std::vector SW_EVENT_TABLE = { + PERF_EVENT_SW("cpu-clock", CPU_CLOCK), + PERF_EVENT_SW("task-clock", TASK_CLOCK), + PERF_EVENT_SW("page-faults", PAGE_FAULTS), + PERF_EVENT_SW("context-switches", CONTEXT_SWITCHES), + PERF_EVENT_SW("cpu-migrations", CPU_MIGRATIONS), + PERF_EVENT_SW("minor-faults", PAGE_FAULTS_MIN), + PERF_EVENT_SW("major-faults", PAGE_FAULTS_MAJ), +#ifdef HAVE_PERF_EVENT_ALIGNMENT_FAULTS + PERF_EVENT_SW("alignment-faults", ALIGNMENT_FAULTS), +#endif +#ifdef HAVE_PERF_EVENT_EMULATION_FAULTS + PERF_EVENT_SW("emulation-faults", EMULATION_FAULTS), +#endif +#ifdef HAVE_PERF_EVENT_DUMMY + PERF_EVENT_SW("dummy", DUMMY), +#endif +#ifdef HAVE_PERF_EVENT_BPF_OUTPUT + PERF_EVENT_SW("bpf-output", BPF_OUTPUT), +#endif +#ifdef HAVE_PERF_EVENT_CGROUP_SWITCHES + PERF_EVENT_SW("cgroup-switches", CGROUP_SWITCHES), +#endif + }; + + for (auto& ev : SW_EVENT_TABLE) + { + Event event(ev); + event_map_.emplace(event.name(), event); + } + + std::stringstream name_fmt; + for (auto& cache : CACHE_NAME_TABLE) + { + for (auto& operation : CACHE_OPERATION_TABLE) + { + name_fmt.str(std::string()); + name_fmt << cache.name << '-' << operation.name; + + event_map_.emplace(name_fmt.str(), + SimpleEvent(name_fmt.str(), PERF_TYPE_HW_CACHE, + make_cache_config(cache.id, operation.id.op_id, + operation.id.result_id))); + } + } } Event EventProvider::cache_event(const std::string& name) @@ -289,18 +291,18 @@ Event EventProvider::cache_event(const std::string& name) { if (regex_match(name, raw_regex)) { - return event_map_.emplace(name, raw_read_event(name)).first->second; + return event_map_.emplace(name, SimpleEvent::raw(name)).first->second; } else { - SysfsEvent event = EventProvider::instance().create_sysfs_event(name, false); + SysfsEvent event(name); return event_map_.emplace(name, event).first->second; } } catch (const InvalidEvent& e) { // emplace unavailable Sampling Event - SysfsEvent event = EventProvider::instance().create_sysfs_event(name, false); + SysfsEvent event(name); event.make_invalid(); event_map_.emplace(name, event); @@ -315,9 +317,8 @@ Event EventProvider::cache_event(const std::string& name) */ Event EventProvider::get_event_by_name(const std::string& name) { - auto& ev_map = instance().event_map_; - auto event_it = ev_map.find(name); - if (event_it != ev_map.end()) + auto event_it = event_map_.find(name); + if (event_it != event_map_.end()) { if (event_it->second.is_valid()) { @@ -330,15 +331,53 @@ Event EventProvider::get_event_by_name(const std::string& name) } else { - return instance_mutable().cache_event(name); + return cache_event(name); } } +Event EventProvider::get_metric_leader(std::string metric_leader) +{ + std::optional leader; + Log::info() << "choosing default metric-leader"; + if (metric_leader == "") + { + + try + { + leader = EventProvider::instance().get_event_by_name("cpu-clock"); + } + catch (const EventProvider::InvalidEvent& e) + { + Log::warn() << "cpu-clock isn't available, trying to use a fallback event"; + try + { + leader = EventProvider::instance().fallback_metric_leader_event(); + } + catch (const perf::EventProvider::InvalidEvent& e) + { + Log::error() << "Failed to determine a suitable metric leader event"; + Log::error() << "Try manually specifying one with --metric-leader."; + } + } + } + else + { + try + { + leader = perf::EventProvider::instance().get_event_by_name(metric_leader); + } + catch (const perf::EventProvider::InvalidEvent& e) + { + Log::error() << "Metric leader " << metric_leader << " not available."; + Log::error() << "Please choose another metric leader."; + } + } + return leader.value(); +} bool EventProvider::has_event(const std::string& name) { - auto& ev_map = instance().event_map_; - const auto event_it = ev_map.find(name); - if (event_it != ev_map.end()) + const auto event_it = event_map_.find(name); + if (event_it != event_map_.end()) { return (event_it->second.is_valid()); } @@ -346,7 +385,7 @@ bool EventProvider::has_event(const std::string& name) { try { - instance_mutable().cache_event(name); + cache_event(name); return true; } catch (const InvalidEvent&) @@ -358,12 +397,10 @@ bool EventProvider::has_event(const std::string& name) std::vector EventProvider::get_predefined_events() { - const auto& ev_map = instance().event_map_; - std::vector events; - events.reserve(ev_map.size()); + events.reserve(event_map_.size()); - for (const auto& event : ev_map) + for (const auto& event : event_map_) { if (event.second.is_valid()) { @@ -374,107 +411,5 @@ std::vector EventProvider::get_predefined_events() return events; } -// returns a standard TracepointEvent, can use config() if specified, otherwise sets default values -tracepoint::TracepointEvent EventProvider::create_tracepoint_event(const std::string& name, - bool use_config, - bool enable_on_exec) -{ - tracepoint::TracepointEvent event(name, enable_on_exec); - event.sample_period(0); - - if (use_config) - { - apply_config_attrs(event); - } - else - { - apply_default_attrs(event); - } - - return event; -} - -// returns a Event with bp_addr set to local_time, uses config() -Event EventProvider::create_time_event(uint64_t local_time, bool enable_on_exec) -{ -#ifndef USE_HW_BREAKPOINT_COMPAT - Event event("Time", PERF_TYPE_BREAKPOINT, 0); // TODO: name for time events -#else - Event event("Time", PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); -#endif - - event.sample_period(1); // may be overwritten by event.time_attrs - event.time_attrs(local_time, enable_on_exec); - - apply_config_attrs(event); - event.exclude_kernel(true); // overwrite config value - - return event; -} - -// returns a standard Event, uses config() if possible, else sets default values -Event EventProvider::create_event(const std::string& name, perf_type_id type, std::uint64_t config, - std::uint64_t config1) -{ - Event event(name, type, config, config1); - event.sample_period(0); - - try - { - apply_config_attrs(event); - event.exclude_kernel(true); // overwrite config value - } - catch (...) - { - apply_default_attrs(event); - } - - return event; -} - -// returns a SysfsEvent with sampling options enabled, uses config() -SysfsEvent EventProvider::create_sampling_event(bool enable_on_exec) -{ - SysfsEvent event(config().sampling_event, enable_on_exec); - apply_config_attrs(event); - - event.sample_period(config().sampling_period); - event.use_sampling_options(config().use_pebs, config().sampling, config().enable_cct); - - return event; -} - -// returns a standard SysfsEvent, can use config() if specified, otherwise sets default values -SysfsEvent EventProvider::create_sysfs_event(const std::string& name, bool use_config) -{ - SysfsEvent event(name); - event.sample_period(0); - - if (use_config) - { - apply_config_attrs(event); - } - else - { - apply_default_attrs(event); - } - - return event; -} - -void EventProvider::apply_config_attrs(Event& event) -{ - event.watermark(config().mmap_pages); - event.exclude_kernel(config().exclude_kernel); - event.clock_attrs(config().use_clockid, config().clockid); -} - -void EventProvider::apply_default_attrs(Event& event) -{ - event.watermark(16); // default mmap-pages value - event.exclude_kernel(true); // enabled by default - event.clock_attrs(true, CLOCK_MONOTONIC_RAW); -} - } // namespace perf } // namespace lo2s diff --git a/src/perf/time/reader.cpp b/src/perf/time/reader.cpp index ba6404d2..adb5c3ca 100644 --- a/src/perf/time/reader.cpp +++ b/src/perf/time/reader.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ Reader::Reader() "object, or the hardware breakpoint won't work."); Event event = - EventProvider::instance().create_time_event(reinterpret_cast(&local_time)); + EventConfig::instance().create_time_event(reinterpret_cast(&local_time)); try {