Path: blob/21.2-virgl/src/tool/pps/pps_datasource.cc
7111 views
/*1* Copyright © 2019-2021 Collabora, Ltd.2* Author: Antonio Caggiano <[email protected]>3* Author: Rohan Garg <[email protected]>4* Author: Robert Beckett <[email protected]>5* Author: Corentin Noël <[email protected]>6*7* SPDX-License-Identifier: MIT8*/910#include "pps_datasource.h"11#include "pps_driver.h"1213#include <condition_variable>14#include <thread>15#include <variant>1617// Minimum supported sampling period in nanoseconds18#define MIN_SAMPLING_PERIOD_NS 500001920namespace pps21{22static std::string driver_name;2324/// Synchronize access to started_cv and started25static std::mutex started_m;26static std::condition_variable started_cv;27static bool started = false;2829float ms(const std::chrono::nanoseconds &t)30{31return t.count() / 1000000.0f;32}3334void GpuDataSource::OnSetup(const SetupArgs &args)35{36// Create drivers for all supported devices37auto drm_devices = DrmDevice::create_all();38for (auto &drm_device : drm_devices) {39if (drm_device.name != driver_name)40continue;4142if (auto driver = Driver::get_driver(std::move(drm_device))) {43if (!driver->init_perfcnt()) {44// Skip failing driver45PPS_LOG_ERROR("Failed to initialize %s driver", driver->drm_device.name.c_str());46continue;47}4849this->driver = driver;50}51}52if (driver == nullptr) {53PPS_LOG_FATAL("No DRM devices supported");54}5556// Parse perfetto config57const std::string &config_raw = args.config->gpu_counter_config_raw();58perfetto::protos::pbzero::GpuCounterConfig::Decoder config(config_raw);5960if (config.has_counter_ids()) {61// Get enabled counters62PPS_LOG_IMPORTANT("Selecting counters");63for (auto it = config.counter_ids(); it; ++it) {64uint32_t counter_id = it->as_uint32();65driver->enable_counter(counter_id);66}67} else {68// Enable all counters69driver->enable_all_counters();70}7172// Get sampling period73auto min_sampling_period = std::chrono::nanoseconds(MIN_SAMPLING_PERIOD_NS);7475auto dev_supported = std::chrono::nanoseconds(driver->get_min_sampling_period_ns());76if (dev_supported > min_sampling_period) {77min_sampling_period = dev_supported;78}7980time_to_sleep = std::max(time_to_sleep, min_sampling_period);8182if (config.has_counter_period_ns()) {83auto requested_sampling_period = std::chrono::nanoseconds(config.counter_period_ns());84if (requested_sampling_period < min_sampling_period) {85PPS_LOG_ERROR("Sampling period should be greater than %" PRIu64 " ns (%.2f ms)",86uint64_t(min_sampling_period.count()),87ms(min_sampling_period));88} else {89time_to_sleep = requested_sampling_period;90}91}92PPS_LOG("Sampling period set to %" PRIu64 " ns", uint64_t(time_to_sleep.count()));93}9495void GpuDataSource::OnStart(const StartArgs &args)96{97driver->enable_perfcnt(time_to_sleep.count());9899state = State::Start;100101{102std::lock_guard<std::mutex> lock(started_m);103started = true;104}105started_cv.notify_all();106}107108void close_callback(GpuDataSource::TraceContext ctx)109{110auto packet = ctx.NewTracePacket();111packet->Finalize();112ctx.Flush();113PPS_LOG("Context flushed");114}115116void GpuDataSource::OnStop(const StopArgs &args)117{118state = State::Stop;119auto stop_closure = args.HandleStopAsynchronously();120Trace(close_callback);121stop_closure();122123driver->disable_perfcnt();124driver = nullptr;125126std::lock_guard<std::mutex> lock(started_m);127started = false;128}129130void GpuDataSource::wait_started()131{132std::unique_lock<std::mutex> lock(started_m);133if (!started) {134PPS_LOG("Waiting for start");135started_cv.wait(lock, [] { return started; });136}137}138139void GpuDataSource::register_data_source(const std::string &_driver_name)140{141driver_name = _driver_name;142static perfetto::DataSourceDescriptor dsd;143dsd.set_name("gpu.counters." + driver_name);144Register(dsd);145}146147void add_group(perfetto::protos::pbzero::GpuCounterDescriptor *desc,148const CounterGroup &group,149const std::string &prefix,150int32_t gpu_num)151{152if (!group.counters.empty()) {153// Define a block for each group containing counters154auto block_desc = desc->add_blocks();155block_desc->set_name(prefix + "." + group.name);156block_desc->set_block_id(group.id);157158// Associate counters to blocks159for (auto id : group.counters) {160block_desc->add_counter_ids(id);161}162}163164for (auto const &sub : group.subgroups) {165// Perfetto doesnt currently support nested groups.166// Flatten group hierarchy, using dot separator167add_group(desc, sub, prefix + "." + group.name, gpu_num);168}169}170171void add_descriptors(perfetto::protos::pbzero::GpuCounterEvent *event,172std::vector<CounterGroup> const &groups,173std::vector<Counter> const &counters,174Driver &driver)175{176// Start a counter descriptor177auto desc = event->set_counter_descriptor();178179// Add the groups180for (auto const &group : groups) {181add_group(desc, group, driver.drm_device.name, driver.drm_device.gpu_num);182}183184// Add the counters185for (auto const &counter : counters) {186auto spec = desc->add_specs();187spec->set_counter_id(counter.id);188spec->set_name(counter.name);189190auto units = perfetto::protos::pbzero::GpuCounterDescriptor::NONE;191switch (counter.units) {192case Counter::Units::Percent:193units = perfetto::protos::pbzero::GpuCounterDescriptor::PERCENT;194break;195case Counter::Units::Byte:196units = perfetto::protos::pbzero::GpuCounterDescriptor::BYTE;197break;198case Counter::Units::Hertz:199units = perfetto::protos::pbzero::GpuCounterDescriptor::HERTZ;200break;201case Counter::Units::None:202units = perfetto::protos::pbzero::GpuCounterDescriptor::NONE;203break;204default:205assert(false && "Missing counter units type!");206break;207}208spec->add_numerator_units(units);209}210}211212void add_samples(perfetto::protos::pbzero::GpuCounterEvent &event, const Driver &driver)213{214if (driver.enabled_counters.size() == 0) {215PPS_LOG_FATAL("There are no counters enabled");216}217218for (const auto &counter : driver.enabled_counters) {219auto counter_event = event.add_counters();220221counter_event->set_counter_id(counter.id);222223auto value = counter.get_value(driver);224if (auto d_value = std::get_if<double>(&value)) {225counter_event->set_double_value(*d_value);226} else if (auto i_value = std::get_if<int64_t>(&value)) {227counter_event->set_int_value(*i_value);228} else {229PPS_LOG_ERROR("Failed to get value for counter %s", counter.name.c_str());230}231}232}233234void GpuDataSource::trace(TraceContext &ctx)235{236using namespace perfetto::protos::pbzero;237238if (auto state = ctx.GetIncrementalState(); state->was_cleared) {239// Mark any incremental state before this point invalid240{241auto packet = ctx.NewTracePacket();242packet->set_timestamp(perfetto::base::GetBootTimeNs().count());243packet->set_sequence_flags(TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);244}245246auto packet = ctx.NewTracePacket();247descriptor_timestamp = perfetto::base::GetBootTimeNs().count();248packet->set_timestamp(descriptor_timestamp);249250auto event = packet->set_gpu_counter_event();251event->set_gpu_id(driver->drm_device.gpu_num);252253auto &groups = driver->groups;254auto &counters = driver->enabled_counters;255PPS_LOG("Sending counter descriptors");256add_descriptors(event, groups, counters, *driver);257258state->was_cleared = false;259}260261// Save current scheduler for restoring later262int prev_sched_policy = sched_getscheduler(0);263sched_param prev_priority_param;264sched_getparam(0, &prev_priority_param);265266// Use FIFO policy to avoid preemption while collecting counters267int sched_policy = SCHED_FIFO;268// Do not use max priority to avoid starving migration and watchdog threads269int priority_value = sched_get_priority_max(sched_policy) - 1;270sched_param priority_param { priority_value };271sched_setscheduler(0, sched_policy, &priority_param);272273if (driver->dump_perfcnt()) {274while (auto timestamp = driver->next()) {275if (timestamp <= descriptor_timestamp) {276// Do not send counter values before counter descriptors277PPS_LOG_ERROR("Skipping counter values coming before descriptors");278continue;279}280281auto packet = ctx.NewTracePacket();282packet->set_timestamp(timestamp);283284auto event = packet->set_gpu_counter_event();285event->set_gpu_id(driver->drm_device.gpu_num);286287add_samples(*event, *driver);288}289}290291// Reset normal scheduler292sched_setscheduler(0, prev_sched_policy, &prev_priority_param);293}294295void GpuDataSource::trace_callback(TraceContext ctx)296{297using namespace std::chrono;298299nanoseconds sleep_time = nanoseconds(0);300301if (auto data_source = ctx.GetDataSourceLocked()) {302if (data_source->time_to_sleep > data_source->time_to_trace) {303sleep_time = data_source->time_to_sleep - data_source->time_to_trace;304}305}306307// Wait sampling period before tracing308std::this_thread::sleep_for(sleep_time);309310auto time_zero = perfetto::base::GetBootTimeNs();311if (auto data_source = ctx.GetDataSourceLocked()) {312// Check data source is still running313if (data_source->state == pps::State::Start) {314data_source->trace(ctx);315data_source->time_to_trace = perfetto::base::GetBootTimeNs() - time_zero;316}317} else {318PPS_LOG("Tracing finished");319}320}321322} // namespace pps323324325