diff --git a/CMakeLists.txt b/CMakeLists.txt index 7eb87554..5deb99c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ IfUpdatedUnsetAll(lo2s_USE_STATIC_LIBS Binutils_USE_STATIC_LIBS OTF2_USE_STATIC_LIBS OTF2XX_USE_STATIC_LIBS + Libpfm_USE_STATIC_LIBS X86Adapt_STATIC x86_energy_STATIC ) @@ -43,6 +44,7 @@ if(lo2s_USE_STATIC_LIBS STREQUAL "OFF") set(X86Adapt_STATIC OFF CACHE BOOL "") set(x86_energy_STATIC OFF CACHE BOOL "") set(Sensors_USE_STATIC_LIBS OFF CACHE BOOL "") + set(Libpfm_USE_STATIC_LIBS OFF CACHE BOOL "") endif() if(lo2s_USE_STATIC_LIBS STREQUAL "MOSTLY") @@ -53,6 +55,7 @@ if(lo2s_USE_STATIC_LIBS STREQUAL "MOSTLY") set(X86Adapt_STATIC ON CACHE BOOL "") set(x86_energy_STATIC ON CACHE BOOL "") set(Sensors_USE_STATIC_LIBS ON CACHE BOOL "") + set(Libpfm_USE_STATIC_LIBS ON CACHE BOOL "") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") endif() @@ -64,6 +67,7 @@ if(lo2s_USE_STATIC_LIBS STREQUAL "ALL") set(X86Adapt_STATIC ON CACHE BOOL "") set(x86_energy_STATIC ON CACHE BOOL "") set(Sensors_USE_STATIC_LIBS ON CACHE BOOL "") + set(Libpfm_USE_STATIC_LIBS ON CACHE BOOL "") # Doesn't seem to work with clang, even though it should, # but at least it doesn't complain about it either @@ -101,6 +105,7 @@ find_package(x86_energy 2.0 CONFIG) find_package(StdFilesystem REQUIRED) find_package(Sensors) find_package(Veosinfo) +find_package(Libpfm) find_package(PkgConfig) if(PkgConfig_FOUND) @@ -118,6 +123,8 @@ option(UML_LOOK "Generate graphs with an UML look" OFF) add_feature_info("USE_RADARE" USE_RADARE "Use Radare to add instruction information to samples.") CMAKE_DEPENDENT_OPTION(USE_SENSORS "Use the libsensors to read system metrics." ON "Sensors_FOUND" OFF) add_feature_info("USE_SENSORS" USE_SENSORS "Use the libsensors to read system metrics.") +CMAKE_DEPENDENT_OPTION(USE_LIBPFM "Use libpfm to retrieve metric information." ON "Libpfm_FOUND" OFF) +add_feature_info("USE_LIBPFM" USE_LIBPFM "Use libpfm to retrieve metric information.") CMAKE_DEPENDENT_OPTION(USE_LIBAUDIT "Use libaudit for syscall name resolution" ON Audit_FOUND OFF) add_feature_info("USE_LIBAUDIT" USE_LIBAUDIT "Use libaudit for syscall name resolution.") CMAKE_DEPENDENT_OPTION(USE_VEOSINFO "Use libveosinfo to sample NEC SX-Aurora Tsubasa cards." ON "Veosinfo_FOUND" OFF) @@ -281,6 +288,15 @@ if (USE_VEOSINFO) endif() endif() +if (USE_LIBPFM) + if (Libpfm_FOUND) + target_compile_definitions(lo2s PUBLIC HAVE_LIBPFM) + target_link_libraries(lo2s PRIVATE Libpfm::libpfm) + else() + message(SEND_ERROR "Libpfm not found but requested.") + endif() +endif() + if (USE_LIBAUDIT) if (Audit_FOUND) target_compile_definitions(lo2s PUBLIC HAVE_LIBAUDIT) diff --git a/cmake/FindLibpfm.cmake b/cmake/FindLibpfm.cmake new file mode 100644 index 00000000..fc0b4a53 --- /dev/null +++ b/cmake/FindLibpfm.cmake @@ -0,0 +1,52 @@ + +# Copyright (c) 2022, Technische Universität Dresden, Germany +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions +# and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions +# and the following disclaimer in the documentation and/or other materials provided with the +# distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include(${CMAKE_CURRENT_LIST_DIR}/UnsetIfUpdated.cmake) + +option(Libpfm_USE_STATIC_LIBS "Link libpfm statically" OFF) + +UnsetIfUpdated(Libpfm_LIBRARIES, Libpfm_USE_STATIC_LIBS) + +find_path(Libpfm_INCLUDE_DIRS perfmon/pfmlib.h PATHS ENV C_INCLUDE_PATH ENV CPATH PATH_SUFFIXES include) + +if(Libpfm_USE_STATIC_LIBS) + find_library(Libpfm_LIBRARIES NAME libpfm.a HINTS ENV LIBRARY_PATH LD_LIBRARY_PATH) + else() + find_library(Libpfm_LIBRARIES NAME libpfm.so HINTS ENV LIBRARY_PATH LD_LIBRARY_PATH) +endif() + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Libpfm DEFAULT_MSG Libpfm_LIBRARIES Libpfm_INCLUDE_DIRS) + +if(Libpfm_FOUND) + add_library(libpfm INTERFACE) + target_include_directories(libpfm SYSTEM INTERFACE ${Libpfm_INCLUDE_DIRS}) + target_link_libraries(libpfm INTERFACE ${Libpfm_LIBRARIES}) + add_library(Libpfm::libpfm ALIAS libpfm) + endif() + + mark_as_advanced(Libpfm_LIBRARIES Libpfm_INCLUDE_DIRS) diff --git a/include/lo2s/perf/event_provider.hpp b/include/lo2s/perf/event_provider.hpp index a0200f81..c0c9c9bd 100644 --- a/include/lo2s/perf/event_provider.hpp +++ b/include/lo2s/perf/event_provider.hpp @@ -21,13 +21,13 @@ #pragma once +#include + #include #include #include #include -#include - namespace lo2s { namespace perf @@ -111,5 +111,7 @@ class EventProvider EventMap event_map_; }; + +bool event_is_openable(EventDescription& ev); } // namespace perf } // namespace lo2s diff --git a/include/lo2s/perf/pfm.hpp b/include/lo2s/perf/pfm.hpp new file mode 100644 index 00000000..975fcc66 --- /dev/null +++ b/include/lo2s/perf/pfm.hpp @@ -0,0 +1,180 @@ +/* + * 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 "event_provider.hpp" +#include + +#include +#include +#include +#include + +#include + +extern "C" +{ +#include +#include +#include +} + +namespace lo2s +{ +namespace perf +{ + +class PFM4 +{ +public: + static const PFM4& instance() + { + static PFM4 pfm4; + return pfm4; + } + + ~PFM4() + { + pfm_terminate(); + } + + std::optional pfm4_read_event(const std::string& ev_desc) const + { + pfm_perf_encode_arg_t arg; + struct perf_event_attr attr; + + memset(&arg, 0, sizeof(arg)); + memset(&attr, 0, sizeof(attr)); + arg.attr = &attr; + + int ret = pfm_get_os_event_encoding(ev_desc.c_str(), PFM_PLM3, PFM_OS_PERF_EVENT, &arg); + + if (ret != PFM_SUCCESS) + { + return std::nullopt; + } + + EventDescription ev = + EventDescription(ev_desc, (perf_type_id)attr.type, attr.config, attr.config1); + + if (!event_is_openable(ev)) + { + return std::nullopt; + } + + return ev; + } + + std::vector get_pfm4_events() const + { + std::vector events; + + pfm_pmu_info_t pinfo; + pfm_event_info_t info; + + for (int pmu_id = PFM_PMU_NONE; pmu_id < PFM_PMU_MAX; pmu_id++) + { + memset(&pinfo, 0, sizeof(pinfo)); + + if (pfm_get_pmu_info((pfm_pmu_t)pmu_id, &pinfo) != PFM_SUCCESS) + { + continue; + } + + for (int event_id = pinfo.first_event; event_id != -1; + event_id = pfm_get_event_next(event_id)) + { + memset(&info, 0, sizeof(info)); + if (pfm_get_event_info(event_id, PFM_OS_PERF_EVENT, &info) != PFM_SUCCESS) + { + continue; + } + + std::string full_event_name = fmt::format("{}::{}", pinfo.name, info.name); + // Some events are confusingly more like event groups, for which we have to retrieve + // the subevents + if (info.nattrs != 0) + { + bool has_umask = false; + for (int attr_id = 0; attr_id < info.nattrs; attr_id++) + { + pfm_event_attr_info_t attr_info; + memset(&attr_info, 0, sizeof(attr_info)); + auto ret = pfm_get_event_attr_info(info.idx, attr_id, PFM_OS_PERF_EVENT, + &attr_info); + + if (ret != PFM_SUCCESS) + { + Log::warn() << "Could not get event info for " << full_event_name + << ": " << pfm_strerror(ret); + } + + if (attr_info.type != PFM_ATTR_UMASK) + { + continue; + } + else + { + has_umask = true; + auto uevent = pfm4_read_event( + fmt::format("{}:{}", full_event_name, attr_info.name)); + if (uevent) + { + events.emplace_back(std::move(*uevent)); + } + } + } + + if (!has_umask) + { + auto event = pfm4_read_event(std::string(full_event_name)); + + if (event) + { + events.emplace_back(std::move(*event)); + } + } + } + else + { + auto event = pfm4_read_event(std::string(full_event_name)); + + if (event) + { + events.emplace_back(std::move(*event)); + } + } + } + } + + return events; + } + +private: + PFM4() + { + pfm_initialize(); + } +}; + +} // namespace perf +} // namespace lo2s diff --git a/src/config.cpp b/src/config.cpp index 690298db..d4399d6d 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -27,6 +27,9 @@ #include #include #include +#ifdef HAVE_LIBPFM +#include +#endif #include #include #include @@ -460,6 +463,11 @@ void parse_program_options(int argc, const char** argv) print_availability(std::cout, "Kernel PMU events", perf::EventProvider::get_pmu_events()); +#ifdef HAVE_LIBPFM + print_availability(std::cout, "Libpfm events", + perf::PFM4::instance().get_pfm4_events()); +#endif + std::cout << "(* Only available in process-monitoring mode" << std::endl; std::cout << "(# Only available in system-monitoring mode" << std::endl; diff --git a/src/perf/event_provider.cpp b/src/perf/event_provider.cpp index 3dd434ac..8cbbed40 100644 --- a/src/perf/event_provider.cpp +++ b/src/perf/event_provider.cpp @@ -25,6 +25,9 @@ #include #include #include +#ifdef HAVE_LIBPFM +#include +#endif #include #include #include @@ -49,10 +52,7 @@ extern "C" namespace { -#define PERF_EVENT(name, type, id) \ - { \ - (name), (type), (id) \ - } +#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_SW(name, id) PERF_EVENT(name, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##id) @@ -156,7 +156,7 @@ namespace perf const EventDescription sysfs_read_event(const std::string& ev_desc); -static bool event_is_openable(EventDescription& ev) +bool event_is_openable(EventDescription& ev) { struct perf_event_attr attr; memset(&attr, 0, sizeof(attr)); @@ -628,8 +628,19 @@ EventDescription EventProvider::cache_event(const std::string& name) } catch (const InvalidEvent& e) { +#ifdef HAVE_LIBPFM + auto ev = PFM4::instance().pfm4_read_event(name); + if (!ev) + { + event_map_.emplace(name, DescriptionCache::make_invalid()); + throw e; + } + + return *ev; +#else event_map_.emplace(name, DescriptionCache::make_invalid()); throw e; +#endif } }