blob: d6af31b1e4a26b4485fe5cf31ce7ff4f8220951c [file] [log] [blame]
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pmu_profiler.h"
#include "hwcpipe_log.h"
namespace hwcpipe
{
const std::unordered_map<CpuCounter, uint64_t, CpuCounterHash> pmu_mappings{
{CpuCounter::Cycles, PERF_COUNT_HW_CPU_CYCLES},
{CpuCounter::Instructions, PERF_COUNT_HW_INSTRUCTIONS},
{CpuCounter::CacheReferences, PERF_COUNT_HW_CACHE_REFERENCES},
{CpuCounter::CacheMisses, PERF_COUNT_HW_CACHE_MISSES},
{CpuCounter::BranchInstructions, PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
{CpuCounter::BranchMisses, PERF_COUNT_HW_BRANCH_MISSES},
};
PmuProfiler::PmuProfiler(const CpuCounterSet &enabled_counters) :
enabled_counters_(enabled_counters)
{
// Set up PMU counters
for (const auto &counter : enabled_counters)
{
const auto &pmu_config = pmu_mappings.find(counter);
if (pmu_config != pmu_mappings.end())
{
try
{
// Create a PMU counter with the specified configuration
auto pmu_counter_res = pmu_counters_.emplace(counter, pmu_config->second);
// Try reading a value from the counter to check that it opened correctly
auto &pmu_counter = pmu_counter_res.first->second;
pmu_counter.get_value<long long>();
// PMU counter is created and can retrieve values
available_counters_.insert(counter);
}
catch (const std::runtime_error &e)
{
// PMU counter initialization failed
HWCPIPE_LOG("%s", e.what());
}
}
}
if (available_counters_.size() == 0)
{
throw std::runtime_error("PMU counters not available.");
}
}
void PmuProfiler::run()
{
for (auto &pmu_counter : pmu_counters_)
{
pmu_counter.second.reset();
prev_measurements_[pmu_counter.first] = Value{};
}
}
const CpuMeasurements &PmuProfiler::sample()
{
for (const auto &counter : enabled_counters_)
{
const auto &pmu_counter = pmu_counters_.find(counter);
if (pmu_counter == pmu_counters_.end())
{
continue;
}
try
{
auto value = pmu_counter->second.get_value<long long>();
// Resetting the PMU counter every frame seems to alter the data,
// so we make a differential reading.
measurements_[pmu_counter->first] = value - prev_measurements_[pmu_counter->first].get<long long>();
prev_measurements_[pmu_counter->first] = value;
}
catch (const std::runtime_error &e)
{
HWCPIPE_LOG("Failed to get value from PMU: %s.", e.what());
}
}
return measurements_;
}
void PmuProfiler::stop()
{
// We don't need to do anything on stop()
}
} // namespace hwcpipe