Repository: google/benchmark
File: src/perf_counters.cc (C++)

Code before:
// See the License for the specific language governing permissions and
// limitations under the License.

#include "perf_counters.h"

#include <cstring>
#include <vector>

#if defined HAVE_LIBPFM
#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"
#endif

namespace benchmark {
namespace internal {

constexpr size_t PerfCounterValues::kMaxCounters;

#if defined HAVE_LIBPFM
const bool PerfCounters::kSupported = true;

bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS; }

PerfCounters* PerfCounters::Create(
    const std::vector<std::string>& counter_names) {
  static std::unique_ptr<PerfCounters> perf_counters_ptr;
  static std::unique_ptr<PerfCounters> no_perf_counters_ptr(new PerfCounters());
  if (counter_names.empty()) {
    return no_perf_counters_ptr.get();
  }
  if (counter_names.size() > PerfCounterValues::kMaxCounters) {
    GetErrorLogInstance()
        << counter_names.size()
        << " counters were requested. The minimum is 1, the maximum is "
        << PerfCounterValues::kMaxCounters << "\n";
    return no_perf_counters_ptr.get();
  }
  if (!perf_counters_ptr && perf_counters_ptr->names() == counter_names) {
    return perf_counters_ptr.get();
  }
  std::vector<int> counter_ids(counter_names.size());

  const int mode = PFM_PLM3;  // user mode only
  for (size_t i = 0; i < counter_names.size(); ++i) {
    const bool is_first = i == 0;
    struct perf_event_attr attr {};
    attr.size = sizeof(attr);
    const int group_id = !is_first ? counter_ids[0] : -1;
    const auto& name = counter_names[i];
    if (name.empty()) {
      GetErrorLogInstance() << "A counter name was the empty string\n";

Review comment (bug):
How about this instead, so we also exit cleanly (i.e. cleaning up file descriptors):
- don't have the statics here (lines 37-38)
- `PerfCountersMeasurement`:
   - has a `static int ref_count_ = 0`
   - has a `static std::unique_ptr<PerfCounters> counters_` field.
   - its ctor takes a `const std::vector<std::string>& counter_names`. It checks if `counters_` is nullptr, if so, creates it with the given `counter_names`. If it's not nullptr, then sanity check the `counter_names` match what we have in `counters_`. Either way, bump `ref_count_` and return a `PerfCountersMeasurement` that uses `counters_.get()`.
   -  the dtor of `PerfCountersMeasurement` decrements `ref_count_`, and when that hits 0, resets `counters_`, which then leads to a clean exit (file descriptors are cleaned up, etc)

Add a comment on `PerfCountersMeasurement` that the ctor is not threadsafe (which should be fine, the scenario is that these are create upfront, at set up); and that the string vector must match what's cached (which is both fine and unavoidable, the whole point is that we can't have too many simultaneously `perf_event_open`-ed counters.)

Code after:
// See the License for the specific language governing permissions and
// limitations under the License.

#include "perf_counters.h"

#include <cstring>
#include <memory>
#include <vector>

#if defined HAVE_LIBPFM
#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"
#endif

namespace benchmark {
namespace internal {

constexpr size_t PerfCounterValues::kMaxCounters;

#if defined HAVE_LIBPFM
const bool PerfCounters::kSupported = true;

bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS; }

PerfCounters PerfCounters::Create(
    const std::vector<std::string>& counter_names) {
  if (counter_names.empty()) {
    return NoCounters();
  }
  if (counter_names.size() > PerfCounterValues::kMaxCounters) {
    GetErrorLogInstance()
        << counter_names.size()
        << " counters were requested. The minimum is 1, the maximum is "
        << PerfCounterValues::kMaxCounters << "\n";
    return NoCounters();
  }
  std::vector<int> counter_ids(counter_names.size());

  const int mode = PFM_PLM3;  // user mode only
  for (size_t i = 0; i < counter_names.size(); ++i) {
    const bool is_first = i == 0;
    struct perf_event_attr attr {};
    attr.size = sizeof(attr);
    const int group_id = !is_first ? counter_ids[0] : -1;
    const auto& name = counter_names[i];
    if (name.empty()) {
      GetErrorLogInstance() << "A counter name was the empty string\n";
      return NoCounters();
    }
    pfm_perf_encode_arg_t arg{};
    arg.attr = &attr;