pps: Gfx-pps v0.3.0

Add the gfx-pps backbone in `src/pps`.

v2: Simplify supported drivers creation.
v3: No default getter is provided for counters.
v4: Open DRM device in read/write mode.
v5: Wait for datasource to be started.
v6: Set FIFO scheduler while sampling counters.

Signed-off-by: Antonio Caggiano <antonio.caggiano@collabora.com>
Acked-by: Emma Anholt <emma@anholt.net>
Reviewed-by: Rob Clark <robdclark@chromium.org>
Reviewed-by: John Bates <jbates@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9652>
This commit is contained in:
Antonio Caggiano 2021-03-18 18:30:45 +01:00 committed by Marge Bot
parent a0738525ed
commit 1cc72b2aef
18 changed files with 1117 additions and 1 deletions

View File

@ -2047,6 +2047,8 @@ endif
gcc_lto_quirk = (cc.get_id() == 'gcc') ? ['-fno-lto'] : []
with_perfetto = get_option('perfetto')
with_datasources = get_option('datasources')
with_any_datasource = with_datasources.length() != 0
if with_perfetto
dep_perfetto = dependency('perfetto', fallback: ['perfetto', 'dep_perfetto'])
endif
@ -2176,7 +2178,12 @@ lines += 'HUD lmsensors: ' + (dep_lmsensors.found() ? 'yes' : 'no')
lines += ''
lines += 'Shared-glapi: ' + (with_shared_glapi ? 'yes' : 'no')
lines += ''
lines += 'Perfetto: ' + (with_perfetto ? 'yes' : 'no')
if with_any_datasource
lines += 'Perfetto ds: ' + ' '.join(with_datasources)
endif
indent = ' '

View File

@ -470,4 +470,11 @@ option(
type : 'boolean',
value : false,
description : 'Enable performance analysis with Perfetto. Default: false'
)
)
option(
'datasources',
type : 'array',
value : ['auto'],
choices : ['auto', 'panfrost', 'intel'],
description: 'List of Perfetto datasources to build. If this is set to `auto`, datasources that can not be build are skipped. Default: [`auto`]'
)

View File

@ -25,6 +25,8 @@ inc_gallium = include_directories('gallium/include')
inc_gallium_aux = include_directories('gallium/auxiliary')
inc_amd_common = include_directories('amd/common')
inc_amd_common_llvm = include_directories('amd/llvm')
inc_tool = include_directories('tool')
pps_datasources = []
libglsl_util = static_library(
'glsl_util',
@ -139,3 +141,5 @@ if with_glx != 'disabled' and not with_glvnd
variables : ['glx_tls=@0@'.format(use_elf_tls ? 'yes' : 'no')],
)
endif
subdir('tool')

8
src/tool/meson.build Normal file
View File

@ -0,0 +1,8 @@
# Copyright © 2021 Collabora, Ltd.
# Author: Antonio Caggiano <antonio.caggiano@collabora.com>
#
# SPDX-License-Identifier: MIT
if with_perfetto
subdir('pps')
endif

View File

@ -0,0 +1,21 @@
BasedOnStyle: WebKit
AlignTrailingComments: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortFunctionsOnASingleLine: None
AlwaysBreakBeforeMultilineStrings: 'true'
BinPackArguments: 'false'
BinPackParameters: 'false'
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
ColumnLimit: '100'
Cpp11BracedListStyle: 'true'
KeepEmptyLinesAtTheStartOfBlocks: 'false'
NamespaceIndentation: None
PointerAlignment: Right
SortIncludes: 'true'
SpaceAfterTemplateKeyword: 'false'
Standard: Cpp11
TabWidth: '3'
IndentWidth: '3'
ConstructorInitializerIndentWidth: '3'
ContinuationIndentWidth: '3'

44
src/tool/pps/meson.build Normal file
View File

@ -0,0 +1,44 @@
# Copyright © 2020-2021 Collabora, Ltd.
# Author: Antonio Caggiano <antonio.caggiano@collabora.com>
#
# SPDX-License-Identifier: MIT
pps_sources = [
'pps.cc',
'pps_device.cc',
'pps_driver.cc',
'pps_counter.cc',
]
include_pps = include_directories('../')
dep_drm = dependency('libdrm')
pps_deps = [dep_drm, dep_perfetto]
pps_deps += pps_datasources
lib_pps = static_library(
'pps',
sources: pps_sources,
include_directories: [include_pps, inc_src],
dependencies: pps_deps,
cpp_args: '-std=c++17'
)
dep_pps = declare_dependency(
link_with: lib_pps,
include_directories: [include_pps, inc_src]
)
producer_sources = [
'pps_datasource.cc',
'pps_producer.cc'
]
executable(
'pps-producer',
sources: producer_sources,
include_directories: [include_pps, inc_src],
dependencies: [dep_pps, dep_perfetto],
cpp_args: '-std=c++17',
install: true
)

26
src/tool/pps/pps.cc Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "pps.h"
#include <cerrno>
#include <cstring>
namespace pps
{
bool check(int res, const char *msg)
{
if (res < 0) {
char *err_msg = std::strerror(errno);
PERFETTO_ELOG("%s: %s", msg, err_msg);
return false;
}
return true;
}
} // namespace pps

38
src/tool/pps/pps.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <perfetto.h>
#define PPS_LOG PERFETTO_LOG
#define PPS_LOG_IMPORTANT PERFETTO_ILOG
#define PPS_LOG_ERROR PERFETTO_ELOG
#define PPS_LOG_FATAL PERFETTO_FATAL
namespace pps
{
enum class State {
Stop, // initial state, or stopped by the tracing service
Start, // running, sampling data
};
/// @brief Checks whether a return value is valid
/// @param res Result from a syscall
/// @param msg Message to prepend to strerror
/// @return True if ok, false otherwise
bool check(int res, const char *msg);
/// @param num Numerator
/// @param den Denominator
/// @return A ratio between two floating point numbers, or 0 if the denominator is 0
constexpr double ratio(double num, double den)
{
return den > 0.0 ? num / den : 0.0;
}
} // namespace pps

View File

@ -0,0 +1,16 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <algorithm>
#define FIND_IF(c, lambda) (std::find_if(std::begin(c), std::end(c), lambda))
#define FIND(c, e) (std::find(std::begin(c), std::end(c), e))
#define CONTAINS(c, e) (FIND(c, e) != std::end(c))
#define CONTAINS_IT(c, it) (it != std::end(c))
#define APPEND(a, b) (a.insert(std::end(a), std::begin(b), std::end(b)))

View File

@ -0,0 +1,33 @@
/*
* Copyright © 2019-2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "pps_counter.h"
#include <cassert>
#include <cstring>
#include "pps_algorithm.h"
namespace pps
{
Counter::Counter(int32_t id, const std::string &name, int32_t group)
: id {id}
, name {name}
, group {group}
{
assert(id >= 0 && "Invalid counter ID");
assert(group >= 0 && "Invalid group ID");
}
bool Counter::operator==(const Counter &other) const
{
return id == other.id;
}
} // namespace pps

110
src/tool/pps/pps_counter.h Normal file
View File

@ -0,0 +1,110 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <functional>
#include <string>
#include <variant>
#include <vector>
namespace pps
{
struct CounterGroup {
std::string name;
uint32_t id;
/// List of counters ID belonging to this group
std::vector<int32_t> counters;
std::vector<CounterGroup> subgroups;
};
class Driver;
class Counter
{
public:
/// @brief A counter value can be of different types depending on what it represents:
/// cycles, cycles-per-instruction, percentages, bytes, and so on.
enum class Units {
Percent,
Byte,
Hertz,
None,
};
using Value = std::variant<int64_t, double>;
/// @param c Counter which we want to retrieve a value
/// @param d Driver used to sample performance counters
/// @return The value of the counter
using Getter = Value(const Counter &c, const Driver &d);
Counter() = default;
virtual ~Counter() = default;
/// @param id ID of the counter
/// @param name Name of the counter
/// @param group Group ID this counter belongs to
Counter(int32_t id, const std::string &name, int32_t group);
bool operator==(const Counter &c) const;
/// @param get New getter function for this counter
void set_getter(const std::function<Getter> &get);
/// @brief d Driver used to sample performance counters
/// @return Last sampled value for this counter
Value get_value(const Driver &d) const;
/// Id of the counter
int32_t id = -1;
/// Name of the counter
std::string name = "";
/// ID of the group this counter belongs to
int32_t group = -1;
/// Offset of this counter within GPU counter list
/// For derived counters it is negative and remains unused
int32_t offset = -1;
/// Whether it is a derived counter or not
bool derived = false;
/// Returns the value of this counter
std::function<Getter> getter;
/// The unit of the counter
Units units;
};
/// @param get New getter function for this counter
inline void Counter::set_getter(const std::function<Getter> &get)
{
getter = get;
}
/// @brief d Driver used to sample performance counters
/// @return Last sampled value for this counter
inline Counter::Value Counter::get_value(const Driver &d) const
{
return getter(*this, d);
}
/// @return The underlying u32 value
template<typename T> constexpr uint32_t to_u32(T &&elem)
{
return static_cast<uint32_t>(elem);
}
} // namespace pps

View File

@ -0,0 +1,323 @@
/*
* Copyright © 2019-2021 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
* Author: Corentin Noël <corentin.noel@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "pps_datasource.h"
#include "pps_driver.h"
#include <condition_variable>
#include <thread>
#include <variant>
// Minimum supported sampling period in nanoseconds
#define MIN_SAMPLING_PERIOD_NS 500000
namespace pps
{
static std::string driver_name;
/// Synchronize access to started_cv and started
static std::mutex started_m;
static std::condition_variable started_cv;
static bool started = false;
float ms(const std::chrono::nanoseconds &t)
{
return t.count() / 1000000.0f;
}
void GpuDataSource::OnSetup(const SetupArgs &args)
{
// Create drivers for all supported devices
auto drm_devices = DrmDevice::create_all();
for (auto &drm_device : drm_devices) {
if (drm_device.name != driver_name)
continue;
if (auto driver = Driver::get_driver(std::move(drm_device))) {
if (!driver->init_perfcnt()) {
// Skip failing driver
PPS_LOG_ERROR("Failed to initialize %s driver", driver->drm_device.name.c_str());
continue;
}
this->driver = driver;
}
}
if (driver == nullptr) {
PPS_LOG_FATAL("No DRM devices supported");
}
// Parse perfetto config
const std::string &config_raw = args.config->gpu_counter_config_raw();
perfetto::protos::pbzero::GpuCounterConfig::Decoder config(config_raw);
if (config.has_counter_ids()) {
// Get enabled counters
PPS_LOG_IMPORTANT("Selecting counters");
for (auto it = config.counter_ids(); it; ++it) {
uint32_t counter_id = it->as_uint32();
driver->enable_counter(counter_id);
}
} else {
// Enable all counters
driver->enable_all_counters();
}
// Get sampling period
auto min_sampling_period = std::chrono::nanoseconds(MIN_SAMPLING_PERIOD_NS);
auto dev_supported = std::chrono::nanoseconds(driver->get_min_sampling_period_ns());
if (dev_supported > min_sampling_period) {
min_sampling_period = dev_supported;
}
time_to_sleep = std::max(time_to_sleep, min_sampling_period);
if (config.has_counter_period_ns()) {
auto requested_sampling_period = std::chrono::nanoseconds(config.counter_period_ns());
if (requested_sampling_period < min_sampling_period) {
PPS_LOG_ERROR("Sampling period should be greater than %" PRIu64 " ns (%.2f ms)",
uint64_t(min_sampling_period.count()),
ms(min_sampling_period));
} else {
time_to_sleep = requested_sampling_period;
}
}
PPS_LOG("Sampling period set to %" PRIu64 " ns", uint64_t(time_to_sleep.count()));
}
void GpuDataSource::OnStart(const StartArgs &args)
{
driver->enable_perfcnt(time_to_sleep.count());
state = State::Start;
{
std::lock_guard<std::mutex> lock(started_m);
started = true;
}
started_cv.notify_all();
}
void close_callback(GpuDataSource::TraceContext ctx)
{
auto packet = ctx.NewTracePacket();
packet->Finalize();
ctx.Flush();
PPS_LOG("Context flushed");
}
void GpuDataSource::OnStop(const StopArgs &args)
{
state = State::Stop;
auto stop_closure = args.HandleStopAsynchronously();
Trace(close_callback);
stop_closure();
driver->disable_perfcnt();
driver = nullptr;
std::lock_guard<std::mutex> lock(started_m);
started = false;
}
void GpuDataSource::wait_started()
{
std::unique_lock<std::mutex> lock(started_m);
if (!started) {
PPS_LOG("Waiting for start");
started_cv.wait(lock, [] { return started; });
}
}
void GpuDataSource::register_data_source(const std::string &_driver_name)
{
driver_name = _driver_name;
static perfetto::DataSourceDescriptor dsd;
dsd.set_name("gpu.counters." + driver_name);
Register(dsd);
}
void add_group(perfetto::protos::pbzero::GpuCounterDescriptor *desc,
const CounterGroup &group,
const std::string &prefix,
int32_t gpu_num)
{
if (!group.counters.empty()) {
// Define a block for each group containing counters
auto block_desc = desc->add_blocks();
block_desc->set_name(prefix + "." + group.name);
block_desc->set_block_id(group.id);
// Associate counters to blocks
for (auto id : group.counters) {
block_desc->add_counter_ids(id);
}
}
for (auto const &sub : group.subgroups) {
// Perfetto doesnt currently support nested groups.
// Flatten group hierarchy, using dot separator
add_group(desc, sub, prefix + "." + group.name, gpu_num);
}
}
void add_descriptors(perfetto::protos::pbzero::GpuCounterEvent *event,
std::vector<CounterGroup> const &groups,
std::vector<Counter> const &counters,
Driver &driver)
{
// Start a counter descriptor
auto desc = event->set_counter_descriptor();
// Add the groups
for (auto const &group : groups) {
add_group(desc, group, driver.drm_device.name, driver.drm_device.gpu_num);
}
// Add the counters
for (auto const &counter : counters) {
auto spec = desc->add_specs();
spec->set_counter_id(counter.id);
spec->set_name(counter.name);
auto units = perfetto::protos::pbzero::GpuCounterDescriptor::NONE;
switch (counter.units) {
case Counter::Units::Percent:
units = perfetto::protos::pbzero::GpuCounterDescriptor::PERCENT;
break;
case Counter::Units::Byte:
units = perfetto::protos::pbzero::GpuCounterDescriptor::BYTE;
break;
case Counter::Units::Hertz:
units = perfetto::protos::pbzero::GpuCounterDescriptor::HERTZ;
break;
case Counter::Units::None:
units = perfetto::protos::pbzero::GpuCounterDescriptor::NONE;
break;
default:
assert(false && "Missing counter units type!");
break;
}
spec->add_numerator_units(units);
}
}
void add_samples(perfetto::protos::pbzero::GpuCounterEvent &event, const Driver &driver)
{
if (driver.enabled_counters.size() == 0) {
PPS_LOG_FATAL("There are no counters enabled");
}
for (const auto &counter : driver.enabled_counters) {
auto counter_event = event.add_counters();
counter_event->set_counter_id(counter.id);
auto value = counter.get_value(driver);
if (auto d_value = std::get_if<double>(&value)) {
counter_event->set_double_value(*d_value);
} else if (auto i_value = std::get_if<int64_t>(&value)) {
counter_event->set_int_value(*i_value);
} else {
PPS_LOG_ERROR("Failed to get value for counter %s", counter.name.c_str());
}
}
}
void GpuDataSource::trace(TraceContext &ctx)
{
using namespace perfetto::protos::pbzero;
if (auto state = ctx.GetIncrementalState(); state->was_cleared) {
// Mark any incremental state before this point invalid
{
auto packet = ctx.NewTracePacket();
packet->set_timestamp(perfetto::base::GetBootTimeNs().count());
packet->set_sequence_flags(TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
}
auto packet = ctx.NewTracePacket();
descriptor_timestamp = perfetto::base::GetBootTimeNs().count();
packet->set_timestamp(descriptor_timestamp);
auto event = packet->set_gpu_counter_event();
event->set_gpu_id(driver->drm_device.gpu_num);
auto &groups = driver->groups;
auto &counters = driver->enabled_counters;
PPS_LOG("Sending counter descriptors");
add_descriptors(event, groups, counters, *driver);
state->was_cleared = false;
}
// Save current scheduler for restoring later
int prev_sched_policy = sched_getscheduler(0);
sched_param prev_priority_param;
sched_getparam(0, &prev_priority_param);
// Use FIFO policy to avoid preemption while collecting counters
int sched_policy = SCHED_FIFO;
// Do not use max priority to avoid starving migration and watchdog threads
int priority_value = sched_get_priority_max(sched_policy) - 1;
sched_param priority_param { priority_value };
sched_setscheduler(0, sched_policy, &priority_param);
if (driver->dump_perfcnt()) {
while (auto timestamp = driver->next()) {
if (timestamp <= descriptor_timestamp) {
// Do not send counter values before counter descriptors
PPS_LOG_ERROR("Skipping counter values coming before descriptors");
continue;
}
auto packet = ctx.NewTracePacket();
packet->set_timestamp(timestamp);
auto event = packet->set_gpu_counter_event();
event->set_gpu_id(driver->drm_device.gpu_num);
add_samples(*event, *driver);
}
}
// Reset normal scheduler
sched_setscheduler(0, prev_sched_policy, &prev_priority_param);
}
void GpuDataSource::trace_callback(TraceContext ctx)
{
using namespace std::chrono;
nanoseconds sleep_time = nanoseconds(0);
if (auto data_source = ctx.GetDataSourceLocked()) {
if (data_source->time_to_sleep > data_source->time_to_trace) {
sleep_time = data_source->time_to_sleep - data_source->time_to_trace;
}
}
// Wait sampling period before tracing
std::this_thread::sleep_for(sleep_time);
auto time_zero = perfetto::base::GetBootTimeNs();
if (auto data_source = ctx.GetDataSourceLocked()) {
// Check data source is still running
if (data_source->state == pps::State::Start) {
data_source->trace(ctx);
data_source->time_to_trace = perfetto::base::GetBootTimeNs() - time_zero;
}
} else {
PPS_LOG("Tracing finished");
}
}
} // namespace pps

View File

@ -0,0 +1,64 @@
/*
* Copyright © 2019-2021 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
* Author: Corentin Noël <corentin.noel@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "pps.h"
#include "pps_driver.h"
namespace pps
{
struct GpuIncrementalState {
bool was_cleared = true;
};
struct GpuDataSourceTraits : public perfetto::DefaultDataSourceTraits {
using IncrementalStateType = GpuIncrementalState;
};
class Driver;
/// @brief This datasource samples performance counters at a specified rate.
/// Once the data is available, it sends a protobuf packet to the perfetto service.
/// At the very beginning, it sends a description of the counters.
/// After that, it sends counter values using the IDs set in the description.
class GpuDataSource : public perfetto::DataSource<GpuDataSource, GpuDataSourceTraits>
{
public:
void OnSetup(const SetupArgs &args) override;
void OnStart(const StartArgs &args) override;
void OnStop(const StopArgs &args) override;
/// Blocks until the data source starts
static void wait_started();
/// @brief Perfetto trace callback
static void trace_callback(TraceContext ctx);
static void register_data_source(const std::string &driver_name);
void trace(TraceContext &ctx);
private:
State state = State::Stop;
/// Time between trace callbacks
std::chrono::nanoseconds time_to_sleep = std::chrono::nanoseconds(1000000);
/// Used to check whether the datasource is quick enough
std::chrono::nanoseconds time_to_trace;
/// A data source supports one driver at a time, but if you need more
/// than one gpu datasource you can just run another producer
Driver *driver = nullptr;
/// Timestamp of packet sent with counter descriptors
uint64_t descriptor_timestamp = 0;
};
} // namespace pps

141
src/tool/pps/pps_device.cc Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "pps_device.h"
#include <cassert>
#include <fcntl.h>
#include <memory>
#include <unistd.h>
#include <xf86drm.h>
namespace pps
{
#define MAX_DRM_DEVICES 64
uint32_t DrmDevice::device_count()
{
drmDevicePtr devices[MAX_DRM_DEVICES] = {};
int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
drmFreeDevices(devices, num_devices);
return static_cast<uint32_t>(num_devices);
}
/// @return The name of a DRM device, empty string in case of error
std::string query_drm_name(const int fd)
{
assert(fd && "Failed to query DrmDevice: invalid fd");
std::string name = "";
if (drmVersionPtr version = drmGetVersion(fd)) {
name = std::string(version->name, version->name_len);
drmFreeVersion(version);
}
return name;
}
/// @return A DRM device, nullopt in case of error
std::optional<DrmDevice> create_drm_device(int fd, int32_t gpu_num)
{
if (fd < 0 || gpu_num < 0) {
return std::nullopt;
}
// Try getting the name
std::string name = query_drm_name(fd);
if (name.empty()) {
return std::nullopt;
}
auto ret = DrmDevice();
ret.fd = fd;
ret.gpu_num = gpu_num;
ret.name = name;
return ret;
}
std::vector<DrmDevice> DrmDevice::create_all()
{
std::vector<DrmDevice> ret = {};
drmDevicePtr devices[MAX_DRM_DEVICES] = {};
int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
if (num_devices <= 0) {
return ret;
}
for (int32_t gpu_num = 0; gpu_num < num_devices; gpu_num++) {
drmDevicePtr device = devices[gpu_num];
if ((device->available_nodes & (1 << DRM_NODE_RENDER))) {
int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR);
// If it can create a device, push it into the vector
if (auto drm_device = create_drm_device(fd, gpu_num)) {
ret.emplace_back(std::move(drm_device.value()));
}
}
}
drmFreeDevices(devices, num_devices);
return ret;
}
std::optional<DrmDevice> DrmDevice::create(int32_t gpu_num)
{
std::optional<DrmDevice> ret = std::nullopt;
if (gpu_num < 0) {
return ret;
}
drmDevicePtr devices[MAX_DRM_DEVICES] = {};
int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
if (num_devices > 0 && gpu_num < num_devices) {
drmDevicePtr device = devices[gpu_num];
int fd = open(device->nodes[DRM_NODE_RENDER], O_RDONLY);
ret = create_drm_device(fd, gpu_num);
}
drmFreeDevices(devices, num_devices);
return ret;
}
DrmDevice::DrmDevice(DrmDevice &&other)
: fd {other.fd}
, gpu_num {other.gpu_num}
, name {std::move(other.name)}
{
other.fd = -1;
other.gpu_num = -1;
}
DrmDevice &DrmDevice::operator=(DrmDevice &&other)
{
std::swap(fd, other.fd);
std::swap(gpu_num, other.gpu_num);
std::swap(name, other.name);
return *this;
}
DrmDevice::~DrmDevice()
{
if (fd >= 0) {
close(fd);
}
}
DrmDevice::operator bool() const
{
return !name.empty();
}
} // namespace pps

53
src/tool/pps/pps_device.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <optional>
#include <string>
#include <vector>
namespace pps
{
/// @brief Helper class for a DRM device
class DrmDevice
{
public:
/// @return The number of DRM devices available in the system
static uint32_t device_count();
/// @return All DRM devices available in the system
static std::vector<DrmDevice> create_all();
/// @return A DRM device selected by its number in the system, nullopt otherwise
static std::optional<DrmDevice> create(int32_t gpu_num);
/// @brief Prefer calling create instead of default constructor
DrmDevice() = default;
// Allow move
DrmDevice(DrmDevice &&);
DrmDevice &operator=(DrmDevice &&);
// Forbid copy
DrmDevice(const DrmDevice &) = delete;
DrmDevice &operator=(const DrmDevice &) = delete;
~DrmDevice();
/// @return Whether a device has a valid name
operator bool() const;
/// File descriptor of the device opened in read/write mode
int fd = -1;
int32_t gpu_num = -1;
std::string name = "";
};
} // namespace pps

View File

@ -0,0 +1,93 @@
/*
* Copyright © 2019-2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Rohan Garg <rohan.garg@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
* Author: Corentin Noël <corentin.noel@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "pps_driver.h"
#include <iterator>
#include <sstream>
#include "pps.h"
#include "pps_algorithm.h"
namespace pps
{
std::unordered_map<std::string, std::unique_ptr<Driver>> create_supported_drivers()
{
std::unordered_map<std::string, std::unique_ptr<Driver>> map;
return map;
}
const std::unordered_map<std::string, std::unique_ptr<Driver>> &Driver::get_supported_drivers()
{
static auto map = create_supported_drivers();
return map;
}
const std::vector<std::string> Driver::supported_device_names()
{
std::vector<std::string> supported_device_names;
for (auto &entry : get_supported_drivers()) {
supported_device_names.emplace_back(entry.first);
}
return supported_device_names;
}
Driver *Driver::get_driver(DrmDevice &&drm_device)
{
auto &supported_drivers = get_supported_drivers();
auto it = supported_drivers.find(drm_device.name);
if (it == std::end(supported_drivers)) {
PERFETTO_FATAL("Failed to find a driver for DRM device %s", drm_device.name.c_str());
}
Driver *driver = it->second.get();
driver->drm_device = std::move(drm_device);
return driver;
}
std::string Driver::default_driver_name()
{
auto supported_devices = Driver::supported_device_names();
auto devices = DrmDevice::create_all();
for (auto &device : devices) {
if (CONTAINS(supported_devices, device.name)) {
PPS_LOG_IMPORTANT("Driver selected: %s", device.name.c_str());
return device.name;
}
}
PPS_LOG_FATAL("Failed to find any driver");
}
std::string Driver::find_driver_name(const char *requested)
{
auto supported_devices = Driver::supported_device_names();
auto devices = DrmDevice::create_all();
for (auto &device : devices) {
if (device.name == requested) {
PPS_LOG_IMPORTANT("Driver selected: %s", device.name.c_str());
return device.name;
}
}
std::ostringstream drivers_os;
std::copy(supported_devices.begin(),
supported_devices.end() - 1,
std::ostream_iterator<std::string>(drivers_os, ", "));
drivers_os << supported_devices.back();
PPS_LOG_ERROR(
"Device '%s' not found (supported drivers: %s)", requested, drivers_os.str().c_str());
return default_driver_name();
}
} // namespace pps

95
src/tool/pps/pps_driver.h Normal file
View File

@ -0,0 +1,95 @@
/*
* Copyright © 2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
* Author: Corentin Noël <corentin.noel@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "pps_counter.h"
#include "pps_device.h"
namespace pps
{
/// @brief Abstract Driver class
class Driver
{
public:
/// @return A map of supported DRM device names and their relative pps driver
static const std::unordered_map<std::string, std::unique_ptr<Driver>> &get_supported_drivers();
/// @return A list of supported DRM device names
static const std::vector<std::string> supported_device_names();
/// @return A driver supporting a specific DRM device
static Driver *get_driver(DrmDevice &&drm_device);
/// @return The name of a default selected PPS driver
static std::string default_driver_name();
/// @return The name of a driver based on the request, otherwise the default driver name
static std::string find_driver_name(const char *requested_name);
Driver() = default;
virtual ~Driver() = default;
// Forbid copy
Driver(const Driver &) = delete;
Driver &operator=(const Driver &) = delete;
/// @return The minimum sampling period for the current device
virtual uint64_t get_min_sampling_period_ns() = 0;
/// @brief Enable a counter by its ID
virtual void enable_counter(uint32_t counter_id) = 0;
virtual void enable_all_counters() = 0;
/// @brief Initialize performance counters data such as groups and counters
/// @return Whether it was successful or not
virtual bool init_perfcnt() = 0;
/// @brief Enables performance counters, meaning that from now on they can be sampled
virtual void enable_perfcnt(uint64_t sampling_period_ns) = 0;
/// @brief Disables performance counters on the device
virtual void disable_perfcnt() = 0;
/// @brief Asking the GPU to dump performance counters could have different meanings
/// depending on the concrete driver. Some could just ask the GPU to dump counters to a
/// user space buffer, while some others will need to read data from a stream which was
/// written asynchronously.
/// @return Whether it was able to dump, false otherwise
virtual bool dump_perfcnt() = 0;
/// @brief After dumping performance counters, with this function you can iterate
/// through the samples collected.
/// @return The CPU timestamp associated to current sample, or 0 if there are no more samples
virtual uint64_t next() = 0;
DrmDevice drm_device;
/// List of counter groups
std::vector<CounterGroup> groups;
/// List of counters exposed by the GPU
std::vector<Counter> counters;
/// List of counters that are actually enabled
std::vector<Counter> enabled_counters;
protected:
// Prevent object slicing by allowing move only from subclasses
Driver(Driver &&) = default;
Driver &operator=(Driver &&) = default;
};
} // namespace pps

View File

@ -0,0 +1,33 @@
/*
* Copyright © 2019-2020 Collabora, Ltd.
* Author: Antonio Caggiano <antonio.caggiano@collabora.com>
* Author: Robert Beckett <bob.beckett@collabora.com>
* Author: Corentin Noël <corentin.noel@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <cstdlib>
#include "pps_datasource.h"
int main(int argc, const char **argv)
{
using namespace pps;
// Connects to the system tracing service
perfetto::TracingInitArgs args;
args.backends = perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
std::string driver_name =
(argc > 1) ? Driver::find_driver_name(argv[1]) : Driver::default_driver_name();
GpuDataSource::register_data_source(driver_name);
while (true) {
GpuDataSource::wait_started();
GpuDataSource::Trace(GpuDataSource::trace_callback);
}
return EXIT_SUCCESS;
}