Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Google Inc.
Haihan Jiang <haihanj99@gmail.com>
Henrique Bucher <hbucher@gmail.com>
International Business Machines Corporation
Ilya Grashin <igrashin06@gmail.com>
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
Jern-Kuan Leong <jernkuan@gmail.com>
JianXiong Zhou <zhoujianxiong2@gmail.com>
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ Gergő Szitár <szitar.gergo@gmail.com>
Haihan Jiang <haihanj99@gmail.com>
Hannes Hauswedell <h2@fsfe.org>
Henrique Bucher <hbucher@gmail.com>
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
Iakov Sergeev <yahontu@gmail.com>
Ilya Grashin <igrashin06@gmail.com>
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
Jern-Kuan Leong <jernkuan@gmail.com>
JianXiong Zhou <zhoujianxiong2@gmail.com>
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
Expand Down
5 changes: 5 additions & 0 deletions docs/perf_counters.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ they are platform specific, but some (e.g. `CYCLES` or `INSTRUCTIONS`) are
mapped by libpfm to platform-specifics - see libpfm
[documentation](http://perfmon2.sourceforge.net/docs.html) for more details.

By default, counters measure user-mode events only. libpfm privilege modifiers
can select a different scope: `:u` measures user mode, `:k` measures kernel
mode, and `:u:k` measures both. Access to kernel-mode counters depends on the
host's `perf_event_paranoid` setting and the process capabilities.

The counter values are reported back through the [User Counters](../README.md#custom-counters)
mechanism, meaning, they are available in all the formats (e.g. JSON) supported
by User Counters.
30 changes: 4 additions & 26 deletions src/perf_counters.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/perf_event.h>
#include <sys/stat.h>

#include "perf_counters_libpfm.h"
#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"
#endif
Expand Down Expand Up @@ -149,8 +150,6 @@ PerfCounters PerfCounters::Create(
valid_names.reserve(counter_names.size());
counter_ids.reserve(counter_names.size());

const int kCounterMode = PFM_PLM3; // user mode only

// Group leads will be assigned on demand. The idea is that once we cannot
// create a counter descriptor, the reason is that this group has maxed out
// so we set the group_id again to -1 and retry - giving the algorithm a
Expand Down Expand Up @@ -182,37 +181,16 @@ PerfCounters PerfCounters::Create(
// Here first means first in group, ie the group leader
const bool is_first = (group_id < 0);

// This struct will be populated by libpfm from the counter string
// and then fed into the syscall perf_event_open
// This struct will be populated by libpfm from the counter string and then
// fed into the syscall perf_event_open.
struct perf_event_attr attr {};
attr.size = sizeof(attr);

// This is the input struct to libpfm.
pfm_perf_encode_arg_t arg{};
arg.attr = &attr;
const int pfm_get = pfm_get_os_event_encoding(name.c_str(), kCounterMode,
PFM_OS_PERF_EVENT, &arg);
const int pfm_get = ConfigurePerfEventAttr(name.c_str(), is_first, &attr);
if (pfm_get != PFM_SUCCESS) {
GetErrorLogInstance()
<< "Unknown performance counter name: " << name << "\n";
continue;
}

// We then proceed to populate the remaining fields in our attribute struct
// Note: the man page for perf_event_create suggests inherit = true and
// read_format = PERF_FORMAT_GROUP don't work together, but that's not the
// case.
attr.disabled = is_first;
attr.inherit = true;
attr.pinned = is_first;
attr.exclude_kernel = true;
attr.exclude_user = false;
attr.exclude_hv = true;

// Read all counters in a group in one read.
attr.read_format = PERF_FORMAT_GROUP; //| PERF_FORMAT_TOTAL_TIME_ENABLED |
// PERF_FORMAT_TOTAL_TIME_RUNNING;

uint64_t base_config = attr.config;
for (uint64_t pmu : GetPMUTypesForEvent(attr)) {
attr.config = (pmu << PERF_PMU_TYPE_SHIFT) | base_config;
Expand Down
52 changes: 52 additions & 0 deletions src/perf_counters_libpfm.h

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this new header for an inline method? why is the enormous method even marked as inline? this could be a private (.cc only) method in the perf_counters.cc file no?

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2021 Google Inc. All rights reserved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2021? and since when do we have copyright notices in headers?

if you're going to use AI for a PR PLEASE PLEASE review it before sending it to us.

//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BENCHMARK_PERF_COUNTERS_LIBPFM_H
#define BENCHMARK_PERF_COUNTERS_LIBPFM_H

#include <linux/perf_event.h>

#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"

namespace benchmark {
namespace internal {

inline int ConfigurePerfEventAttr(const char* name, bool is_group_leader,
perf_event_attr* attr) {
*attr = {};
attr->size = sizeof(*attr);

pfm_perf_encode_arg_t arg{};
arg.attr = attr;
const int kCounterMode = PFM_PLM3; // user mode unless overridden by name
const int status =
pfm_get_os_event_encoding(name, kCounterMode, PFM_OS_PERF_EVENT, &arg);
if (status != PFM_SUCCESS) {
return status;
}

// Preserve the privilege exclusions encoded by libpfm for modifiers such as
// ":u" and ":k", and populate only the benchmark-owned group attributes.
attr->disabled = is_group_leader;
attr->inherit = true;
attr->pinned = is_group_leader;
attr->read_format = PERF_FORMAT_GROUP;
return PFM_SUCCESS;
}

} // namespace internal
} // namespace benchmark

#endif // BENCHMARK_PERF_COUNTERS_LIBPFM_H
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ if (BENCHMARK_ENABLE_GTEST_TESTS)
add_gtest(statistics_gtest)
add_gtest(string_util_gtest)
add_gtest(perf_counters_gtest)
if (PFM_FOUND)
target_compile_definitions(perf_counters_gtest PRIVATE HAVE_LIBPFM)
target_link_libraries(perf_counters_gtest PFM::libpfm)
endif()
add_gtest(reporter_list_gtest)
add_gtest(time_unit_gtest)
add_gtest(min_time_parse_gtest)
Expand Down
47 changes: 47 additions & 0 deletions test/perf_counters_gtest.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <array>
#include <mutex>
#include <random>
#include <set>
Expand All @@ -6,6 +7,12 @@
#include <vector>

#include "../src/perf_counters.h"
#if defined HAVE_LIBPFM
#include <linux/perf_event.h>

#include "../src/perf_counters_libpfm.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is clearly wrong, compared to how we include other files, isn't it?

#include "perfmon/pfmlib.h"
#endif
#include "gmock/gmock.h"
#include "gtest/gtest.h"

Expand All @@ -19,6 +26,9 @@ struct MsgHandler {
using benchmark::internal::PerfCounters;
using benchmark::internal::PerfCountersMeasurement;
using benchmark::internal::PerfCounterValues;
#if defined HAVE_LIBPFM
using benchmark::internal::ConfigurePerfEventAttr;
#endif
using ::testing::AllOf;
using ::testing::Gt;
using ::testing::Lt;
Expand Down Expand Up @@ -49,6 +59,43 @@ TEST(PerfCountersTest, Init) {
EXPECT_EQ(PerfCounters::Initialize(), PerfCounters::kSupported);
}

#if defined HAVE_LIBPFM
TEST(PerfCountersTest, PreservesLibpfmPrivilegeModifiers) {
ASSERT_TRUE(PerfCounters::Initialize());

struct ExpectedPrivilegeMode {
const char* event_name;
bool exclude_user;
bool exclude_kernel;
};
const std::array<ExpectedPrivilegeMode, 4> modes = {{
{"INSTRUCTIONS", false, true},
{"INSTRUCTIONS:u", false, true},
{"INSTRUCTIONS:k", true, false},
{"INSTRUCTIONS:u:k", false, false},
}};

for (const auto& mode : modes) {
perf_event_attr attr{};
ASSERT_EQ(ConfigurePerfEventAttr(mode.event_name, true, &attr), PFM_SUCCESS)
<< mode.event_name;
EXPECT_EQ(attr.exclude_user, mode.exclude_user) << mode.event_name;
EXPECT_EQ(attr.exclude_kernel, mode.exclude_kernel) << mode.event_name;
EXPECT_TRUE(attr.exclude_hv) << mode.event_name;
EXPECT_TRUE(attr.disabled) << mode.event_name;
EXPECT_TRUE(attr.inherit) << mode.event_name;
EXPECT_TRUE(attr.pinned) << mode.event_name;
EXPECT_EQ(attr.read_format, PERF_FORMAT_GROUP) << mode.event_name;
}

perf_event_attr follower_attr{};
ASSERT_EQ(ConfigurePerfEventAttr("INSTRUCTIONS", false, &follower_attr),
PFM_SUCCESS);
EXPECT_FALSE(follower_attr.disabled);
EXPECT_FALSE(follower_attr.pinned);
}
#endif

TEST(PerfCountersTest, OneCounter) {
if (!HasRequiredPerfCounters({kGenericPerfEvent1})) {
GTEST_SKIP() << "Requested performance counters are not available.";
Expand Down
Loading